From fc50b2962c24dc37fa131759cd56460fbc1f43fa Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Mon, 19 Jan 2026 11:28:18 -0600
Subject: fix(app): make terminal sessions scoped to workspace

---
 packages/app/src/context/terminal.tsx | 57 +++++++++++++++++++++--------------
 1 file changed, 34 insertions(+), 23 deletions(-)

(limited to 'packages/app/src/context/terminal.tsx')

diff --git a/packages/app/src/context/terminal.tsx b/packages/app/src/context/terminal.tsx
index a7753069c..709d7b899 100644
--- a/packages/app/src/context/terminal.tsx
+++ b/packages/app/src/context/terminal.tsx
@@ -25,11 +25,11 @@ type TerminalCacheEntry = {
   dispose: VoidFunction
 }
 
-function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id: string | undefined) {
-  const legacy = `${dir}/terminal${id ? "/" + id : ""}.v1`
+function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, session?: string) {
+  const legacy = session ? [`${dir}/terminal/${session}.v1`, `${dir}/terminal.v1`] : [`${dir}/terminal.v1`]
 
   const [store, setStore, _, ready] = persisted(
-    Persist.scoped(dir, id, "terminal", [legacy]),
+    Persist.workspace(dir, "terminal", legacy),
     createStore<{
       active?: string
       all: LocalPTY[]
@@ -43,17 +43,28 @@ function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id:
     all: createMemo(() => Object.values(store.all)),
     active: createMemo(() => store.active),
     new() {
+      const parse = (title: string) => {
+        const match = title.match(/^Terminal (\d+)$/)
+        if (!match) return
+        const value = Number(match[1])
+        if (!Number.isFinite(value) || value <= 0) return
+        return value
+      }
+
       const existingTitleNumbers = new Set(
-        store.all.map((pty) => {
-          const match = pty.titleNumber
-          return match
+        store.all.flatMap((pty) => {
+          const direct = Number.isFinite(pty.titleNumber) && pty.titleNumber > 0 ? pty.titleNumber : undefined
+          if (direct !== undefined) return [direct]
+          const parsed = parse(pty.title)
+          if (parsed === undefined) return []
+          return [parsed]
         }),
       )
 
-      let nextNumber = 1
-      while (existingTitleNumbers.has(nextNumber)) {
-        nextNumber++
-      }
+      const nextNumber =
+        Array.from({ length: existingTitleNumbers.size + 1 }, (_, index) => index + 1).find(
+          (number) => !existingTitleNumbers.has(number),
+        ) ?? 1
 
       sdk.client.pty
         .create({ title: `Terminal ${nextNumber}` })
@@ -166,8 +177,8 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
       }
     }
 
-    const load = (dir: string, id: string | undefined) => {
-      const key = `${dir}:${id ?? WORKSPACE_KEY}`
+    const load = (dir: string, session?: string) => {
+      const key = `${dir}:${WORKSPACE_KEY}`
       const existing = cache.get(key)
       if (existing) {
         cache.delete(key)
@@ -176,7 +187,7 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
       }
 
       const entry = createRoot((dispose) => ({
-        value: createTerminalSession(sdk, dir, id),
+        value: createTerminalSession(sdk, dir, session),
         dispose,
       }))
 
@@ -185,18 +196,18 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
       return entry.value
     }
 
-    const session = createMemo(() => load(params.dir!, params.id))
+    const workspace = createMemo(() => load(params.dir!, params.id))
 
     return {
-      ready: () => session().ready(),
-      all: () => session().all(),
-      active: () => session().active(),
-      new: () => session().new(),
-      update: (pty: Partial<LocalPTY> & { id: string }) => session().update(pty),
-      clone: (id: string) => session().clone(id),
-      open: (id: string) => session().open(id),
-      close: (id: string) => session().close(id),
-      move: (id: string, to: number) => session().move(id, to),
+      ready: () => workspace().ready(),
+      all: () => workspace().all(),
+      active: () => workspace().active(),
+      new: () => workspace().new(),
+      update: (pty: Partial<LocalPTY> & { id: string }) => workspace().update(pty),
+      clone: (id: string) => workspace().clone(id),
+      open: (id: string) => workspace().open(id),
+      close: (id: string) => workspace().close(id),
+      move: (id: string, to: number) => workspace().move(id, to),
     }
   },
 })
-- 
cgit v1.2.3

