summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-01-24 00:35:02 -0600
committerAdam <[email protected]>2026-01-24 07:00:41 -0600
commit962ab3bc8cf0b051e9858db0c6c3e4b96a42bf1b (patch)
tree3d147aebe8bb7e7c615e5fe96b49f90eb841393e /packages/app/src
parentda8f3e92a7bbc3b288f89f6b535b72b94c1d1c19 (diff)
downloadopencode-962ab3bc8cf0b051e9858db0c6c3e4b96a42bf1b.tar.gz
opencode-962ab3bc8cf0b051e9858db0c6c3e4b96a42bf1b.zip
fix(app): reactive loops
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/context/layout.tsx31
-rw-r--r--packages/app/src/pages/layout.tsx35
2 files changed, 53 insertions, 13 deletions
diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx
index ddd66aa2f..c24e433d1 100644
--- a/packages/app/src/context/layout.tsx
+++ b/packages/app/src/context/layout.tsx
@@ -267,17 +267,36 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
return map
})
- createEffect(() => {
+ const rootFor = (directory: string) => {
const map = roots()
- if (map.size === 0) return
+ if (map.size === 0) return directory
+
+ const visited = new Set<string>()
+ const chain = [directory]
+
+ while (chain.length) {
+ const current = chain[chain.length - 1]
+ if (!current) return directory
+ const next = map.get(current)
+ if (!next) return current
+
+ if (visited.has(next)) return directory
+ visited.add(next)
+ chain.push(next)
+ }
+
+ return directory
+ }
+
+ createEffect(() => {
const projects = server.projects.list()
const seen = new Set(projects.map((project) => project.worktree))
batch(() => {
for (const project of projects) {
- const root = map.get(project.worktree)
- if (!root) continue
+ const root = rootFor(project.worktree)
+ if (root === project.worktree) continue
server.projects.close(project.worktree)
@@ -350,7 +369,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
projects: {
list,
open(directory: string) {
- const root = roots().get(directory) ?? directory
+ const root = rootFor(directory)
if (server.projects.list().find((x) => x.worktree === root)) return
globalSync.project.loadSessions(root)
server.projects.open(root)
@@ -384,7 +403,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setStore("sidebar", "width", width)
},
workspaces(directory: string) {
- return createMemo(() => store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false)
+ return () => store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false
},
setWorkspaces(directory: string, value: boolean) {
setStore("sidebar", "workspaces", directory, value)
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 714e23b23..202996eea 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -724,7 +724,8 @@ export default function Layout(props: ParentProps) {
if (!directory) return
const [store] = globalSync.child(directory)
- if (store.message[session.id] !== undefined) return
+ const cached = untrack(() => store.message[session.id] !== undefined)
+ if (cached) return
const q = queueFor(directory)
if (q.inflight.has(session.id)) return
@@ -855,14 +856,34 @@ export default function Layout(props: ParentProps) {
setStore(
produce((draft) => {
const removed = new Set<string>([session.id])
- const collect = (parentID: string) => {
- for (const item of draft.session) {
- if (item.parentID !== parentID) continue
- removed.add(item.id)
- collect(item.id)
+
+ const byParent = new Map<string, string[]>()
+ for (const item of draft.session) {
+ const parentID = item.parentID
+ if (!parentID) continue
+ const existing = byParent.get(parentID)
+ if (existing) {
+ existing.push(item.id)
+ continue
}
+ byParent.set(parentID, [item.id])
}
- collect(session.id)
+
+ const stack = [session.id]
+ while (stack.length) {
+ const parentID = stack.pop()
+ if (!parentID) continue
+
+ const children = byParent.get(parentID)
+ if (!children) continue
+
+ for (const child of children) {
+ if (removed.has(child)) continue
+ removed.add(child)
+ stack.push(child)
+ }
+ }
+
draft.session = draft.session.filter((s) => !removed.has(s.id))
}),
)