-
Notifications
You must be signed in to change notification settings - Fork 2
fix(desktop): pty resume backfill + persist per-project route #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,52 @@ function primaryTabPath(pathname: string): string { | |
| return roots.find((root) => pathname === root || pathname.startsWith(`${root}/`)) ?? pathname; | ||
| } | ||
|
|
||
| const PROJECT_ROUTE_STORAGE_PREFIX = "ade:project-route:"; | ||
|
|
||
| function projectRouteStorageKey(projectRoot: string): string { | ||
| return `${PROJECT_ROUTE_STORAGE_PREFIX}${projectRoot}`; | ||
| } | ||
|
|
||
| function serializeLocationRoute(location: ReturnType<typeof useLocation>): string | null { | ||
| const pathname = location.pathname || "/work"; | ||
| const route = `${pathname}${location.search ?? ""}${location.hash ?? ""}`; | ||
| const allowedRoots = [ | ||
| "/project", | ||
| "/lanes", | ||
| "/files", | ||
| "/work", | ||
| "/graph", | ||
| "/prs", | ||
| "/review", | ||
| "/history", | ||
| "/automations", | ||
| "/missions", | ||
| "/cto", | ||
| "/settings", | ||
| ]; | ||
| if (!allowedRoots.some((root) => pathname === root || pathname.startsWith(`${root}/`))) { | ||
| return null; | ||
| } | ||
| return route; | ||
|
Comment on lines
+75
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/renderer/components/app/AppShell.tsx
Line: 75-92
Comment:
**`allowedRoots` diverges from `primaryTabPath` roots**
`serializeLocationRoute` adds `/review` and `/cto` to its allow-list, but `primaryTabPath` (defined just above) does not include them. Routes under those paths will therefore be persisted and later restored, but the sidebar tab won't be activated (since `primaryTabPath` won't match them). If `/review` and `/cto` are real top-level pages that should be restored, they should also be added to `primaryTabPath`; otherwise they should be dropped from `allowedRoots`.
How can I resolve this? If you propose a fix, please make it concise. |
||
| } | ||
|
|
||
| function readStoredProjectRoute(projectRoot: string): string | null { | ||
| try { | ||
| const value = window.localStorage.getItem(projectRouteStorageKey(projectRoot)); | ||
| return value?.startsWith("/") ? value : null; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| function writeStoredProjectRoute(projectRoot: string, route: string): void { | ||
| try { | ||
| window.localStorage.setItem(projectRouteStorageKey(projectRoot), route); | ||
| } catch { | ||
| // localStorage can be unavailable in private/test environments. | ||
| } | ||
| } | ||
|
|
||
| type AiBannerState = { | ||
| laneId: string | null; | ||
| jobId: string | null; | ||
|
|
@@ -206,6 +252,7 @@ export function AppShell({ children }: { children: React.ReactNode }) { | |
| const [projectMissing, setProjectMissing] = useState(false); | ||
| const [feedbackGenerating, setFeedbackGenerating] = useState(false); | ||
| const previousProjectRootRef = useRef<string | null | undefined>(undefined); | ||
| const lastRouteSaveProjectRootRef = useRef<string | null | undefined>(undefined); | ||
| const isOnboardingRoute = location.pathname === "/onboarding"; | ||
| const isLanesRoute = location.pathname.startsWith("/lanes"); | ||
| const shouldTrackTerminalAttention = | ||
|
|
@@ -582,10 +629,26 @@ export function AppShell({ children }: { children: React.ReactNode }) { | |
|
|
||
| if (previousProjectRoot === undefined) return; | ||
| if (!nextProjectRoot || showWelcome) return; | ||
| if (location.pathname !== "/project") return; | ||
| if (previousProjectRoot === nextProjectRoot) return; | ||
| navigate("/work", { replace: true }); | ||
| }, [location.pathname, navigate, project?.rootPath, showWelcome]); | ||
| if (previousProjectRoot) { | ||
| const previousRoute = serializeLocationRoute(location); | ||
| if (previousRoute) writeStoredProjectRoute(previousProjectRoot, previousRoute); | ||
| } | ||
| navigate(readStoredProjectRoute(nextProjectRoot) ?? "/work", { replace: true }); | ||
| }, [location, navigate, project?.rootPath, showWelcome]); | ||
|
|
||
| useEffect(() => { | ||
| const projectRoot = project?.rootPath ?? null; | ||
| if (!projectRoot || showWelcome) return; | ||
|
|
||
| if (lastRouteSaveProjectRootRef.current !== projectRoot) { | ||
| lastRouteSaveProjectRootRef.current = projectRoot; | ||
| return; | ||
| } | ||
|
|
||
| const route = serializeLocationRoute(location); | ||
| if (route) writeStoredProjectRoute(projectRoot, route); | ||
| }, [location, project?.rootPath, showWelcome]); | ||
|
|
||
| useEffect(() => { | ||
| let cancelled = false; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
tryBackfillResumeTargetsucceeds,startupCommandis overwritten with the backfilled command (e.g.claude --resume <id>). However, the originalrequestedStartupCommandmay contain flags such as--permission-mode defaultthat are not preserved in the backfilled command. The PTY therefore launches without those flags.This may be intentional if permission mode is meant to come from session metadata at the tool level, but the test's expected invocation (
"claude --resume claude-session-123\r") silently drops--permission-mode defaultthat was in the startup command. Worth verifying the tool CLI picks up the correct permission mode without the explicit flag on resume.Prompt To Fix With AI