summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-19 10:23:15 -0600
committerAdam <[email protected]>2026-02-19 11:12:56 -0600
commit6b8902e8b91a7561d57f80249feada949c4d0665 (patch)
treec4e0989b32df5bf7dc849946e6e0c62a758364fc /packages/app/src
parent08a2d002b8f972c98911fd3b25c847c0da8b1d9b (diff)
downloadopencode-6b8902e8b91a7561d57f80249feada949c4d0665.tar.gz
opencode-6b8902e8b91a7561d57f80249feada949c4d0665.zip
fix(app): navigate to last session on project nav
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/pages/layout.tsx15
-rw-r--r--packages/app/src/pages/layout/helpers.test.ts39
-rw-r--r--packages/app/src/pages/layout/helpers.ts18
3 files changed, 69 insertions, 3 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 29ba142e5..1e46b3085 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -61,6 +61,7 @@ import {
displayName,
errorMessage,
getDraggableId,
+ projectSessionTarget,
sortedRootSessions,
syncWorkspaceOrder,
workspaceKey,
@@ -82,6 +83,7 @@ export default function Layout(props: ParentProps) {
Persist.global("layout.page", ["layout.page.v1"]),
createStore({
lastSession: {} as { [directory: string]: string },
+ lastSessionAt: {} as { [directory: string]: number },
activeProject: undefined as string | undefined,
activeWorkspace: undefined as string | undefined,
workspaceOrder: {} as Record<string, string[]>,
@@ -1077,8 +1079,16 @@ export default function Layout(props: ParentProps) {
function navigateToProject(directory: string | undefined) {
if (!directory) return
server.projects.touch(directory)
- const lastSession = store.lastSession[directory]
- navigateWithSidebarReset(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
+ const project = layout.projects
+ .list()
+ .find((item) => item.worktree === directory || item.sandboxes?.includes(directory))
+ const target = projectSessionTarget({
+ directory,
+ project,
+ lastSession: store.lastSession,
+ lastSessionAt: store.lastSessionAt,
+ })
+ navigateWithSidebarReset(`/${base64Encode(target.directory)}${target.id ? `/session/${target.id}` : ""}`)
}
function navigateToSession(session: Session | undefined) {
@@ -1433,6 +1443,7 @@ export default function Layout(props: ParentProps) {
const directory = decode64(dir)
if (!directory) return
setStore("lastSession", directory, id)
+ setStore("lastSessionAt", directory, Date.now())
notification.session.markViewed(id)
const expanded = untrack(() => store.workspaceExpanded[directory])
if (expanded === false) {
diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts
index 83d8f4748..6f868ab69 100644
--- a/packages/app/src/pages/layout/helpers.test.ts
+++ b/packages/app/src/pages/layout/helpers.test.ts
@@ -1,6 +1,13 @@
import { describe, expect, test } from "bun:test"
import { collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink } from "./deep-links"
-import { displayName, errorMessage, getDraggableId, syncWorkspaceOrder, workspaceKey } from "./helpers"
+import {
+ displayName,
+ errorMessage,
+ getDraggableId,
+ projectSessionTarget,
+ syncWorkspaceOrder,
+ workspaceKey,
+} from "./helpers"
describe("layout deep links", () => {
test("parses open-project deep links", () => {
@@ -89,4 +96,34 @@ describe("layout workspace helpers", () => {
expect(errorMessage(new Error("broken"), "fallback")).toBe("broken")
expect(errorMessage("unknown", "fallback")).toBe("fallback")
})
+
+ test("picks newest session across project workspaces", () => {
+ const result = projectSessionTarget({
+ directory: "/root",
+ project: { worktree: "/root", sandboxes: ["/root/a", "/root/b"] },
+ lastSession: {
+ "/root": "root-session",
+ "/root/a": "sandbox-a",
+ "/root/b": "sandbox-b",
+ },
+ lastSessionAt: {
+ "/root": 1,
+ "/root/a": 3,
+ "/root/b": 2,
+ },
+ })
+
+ expect(result).toEqual({ directory: "/root/a", id: "sandbox-a", at: 3 })
+ })
+
+ test("falls back to project route when no session exists", () => {
+ const result = projectSessionTarget({
+ directory: "/root",
+ project: { worktree: "/root", sandboxes: ["/root/a"] },
+ lastSession: {},
+ lastSessionAt: {},
+ })
+
+ expect(result).toEqual({ directory: "/root" })
+ })
})
diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts
index 6a1e7c012..88066cfb8 100644
--- a/packages/app/src/pages/layout/helpers.ts
+++ b/packages/app/src/pages/layout/helpers.ts
@@ -62,6 +62,24 @@ export const errorMessage = (err: unknown, fallback: string) => {
return fallback
}
+export function projectSessionTarget(input: {
+ directory: string
+ project?: { worktree: string; sandboxes?: string[] }
+ lastSession: Record<string, string>
+ lastSessionAt: Record<string, number>
+}): { directory: string; id?: string; at?: number } {
+ const dirs = input.project ? [input.project.worktree, ...(input.project.sandboxes ?? [])] : [input.directory]
+ const best = dirs.reduce<{ directory: string; id: string; at: number } | undefined>((result, directory) => {
+ const id = input.lastSession[directory]
+ if (!id) return result
+ const at = input.lastSessionAt[directory] ?? 0
+ if (result && result.at >= at) return result
+ return { directory, id, at }
+ }, undefined)
+ if (best) return best
+ return { directory: input.directory }
+}
+
export const syncWorkspaceOrder = (local: string, dirs: string[], existing?: string[]) => {
if (!existing) return dirs
const keep = existing.filter((d) => d !== local && dirs.includes(d))