Skip to content

Commit de69c5a

Browse files
committed
fix(app): stabilize server switching
1 parent e5a4f6f commit de69c5a

8 files changed

Lines changed: 80 additions & 71 deletions

File tree

packages/app/src/app.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,11 @@ function ConnectionError(props: { onRetry?: () => void; onServerSelected?: (key:
286286
)
287287
}
288288

289-
function ServerKey(props: ParentProps) {
289+
function ServerKey(props: { children: (key: ServerConnection.Key) => JSX.Element }) {
290290
const server = useServer()
291291
return (
292292
<Show when={server.key} keyed>
293-
{props.children}
293+
{(key) => props.children(key)}
294294
</Show>
295295
)
296296
}
@@ -310,22 +310,24 @@ export function AppInterface(props: {
310310
>
311311
<ConnectionGate disableHealthCheck={props.disableHealthCheck}>
312312
<ServerKey>
313-
<QueryProvider>
314-
<GlobalSDKProvider>
315-
<GlobalSyncProvider>
316-
<Dynamic
317-
component={props.router ?? Router}
318-
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
319-
>
320-
<Route path="/" component={HomeRoute} />
321-
<Route path="/:dir" component={DirectoryLayout}>
322-
<Route path="/" component={SessionIndexRoute} />
323-
<Route path="/session/:id?" component={SessionRoute} />
324-
</Route>
325-
</Dynamic>
326-
</GlobalSyncProvider>
327-
</GlobalSDKProvider>
328-
</QueryProvider>
313+
{() => (
314+
<QueryProvider>
315+
<GlobalSDKProvider>
316+
<GlobalSyncProvider>
317+
<Dynamic
318+
component={props.router ?? Router}
319+
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
320+
>
321+
<Route path="/" component={HomeRoute} />
322+
<Route path="/:dir" component={DirectoryLayout}>
323+
<Route path="/" component={SessionIndexRoute} />
324+
<Route path="/session/:id?" component={SessionRoute} />
325+
</Route>
326+
</Dynamic>
327+
</GlobalSyncProvider>
328+
</GlobalSDKProvider>
329+
</QueryProvider>
330+
)}
329331
</ServerKey>
330332
</ConnectionGate>
331333
</ServerProvider>

packages/app/src/components/dialog-select-server.tsx

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { List } from "@opencode-ai/ui/list"
88
import { TextField } from "@opencode-ai/ui/text-field"
99
import { useMutation } from "@tanstack/solid-query"
1010
import { showToast } from "@opencode-ai/ui/toast"
11-
import { batch, createEffect, createMemo, createResource, For, onCleanup, Show, startTransition, untrack } from "solid-js"
11+
import { batch, createEffect, createMemo, createResource, For, onCleanup, Show, untrack } from "solid-js"
1212
import { createStore, reconcile } from "solid-js/store"
1313
import { DialogWslServer } from "@/components/dialog-wsl-server"
1414
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
@@ -386,28 +386,21 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
386386
const nextKey = ServerConnection.key(conn)
387387
const changed = server.key !== nextKey
388388

389-
const navigateHome = () => {
390-
if (changed && typeof window !== "undefined" && window.history?.replaceState) {
391-
window.history.replaceState(null, "", "/")
389+
const navigateHome = () => props.onNavigateHome?.()
390+
391+
const apply = () => {
392+
dialog.close()
393+
if (persist && conn.type === "http") {
394+
server.add(conn)
395+
navigateHome()
392396
return
393397
}
394-
props.onNavigateHome?.()
395-
}
396398

397-
const apply = () =>
398-
startTransition(() => {
399-
dialog.close()
400-
if (persist && conn.type === "http") {
401-
server.add(conn)
402-
navigateHome()
403-
return
404-
}
405-
406-
batch(() => {
407-
navigateHome()
408-
server.setActive(nextKey)
409-
})
399+
batch(() => {
400+
navigateHome()
401+
server.setActive(nextKey)
410402
})
403+
}
411404

412405
if (!changed) {
413406
await apply()

packages/app/src/components/status-popover-body.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Switch } from "@opencode-ai/ui/switch"
55
import { Tabs } from "@opencode-ai/ui/tabs"
66
import { useMutation, useQueryClient } from "@tanstack/solid-query"
77
import { showToast } from "@opencode-ai/ui/toast"
8-
import { useNavigate } from "@solidjs/router"
8+
import { useLocation, useNavigate } from "@solidjs/router"
99
import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
1010
import { createStore, reconcile } from "solid-js/store"
1111
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
@@ -156,13 +156,14 @@ const useMcpToggleMutation = () => {
156156
}))
157157
}
158158

159-
export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
159+
export function StatusPopoverBody(props: { shown: Accessor<boolean>; close?: () => void }) {
160160
const sync = useSync()
161161
const server = useServer()
162162
const platform = usePlatform()
163163
const dialog = useDialog()
164164
const language = useLanguage()
165165
const navigate = useNavigate()
166+
const location = useLocation()
166167

167168
const fail = (err: unknown) => {
168169
showToast({
@@ -251,8 +252,16 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
251252
aria-disabled={blocked()}
252253
onClick={() => {
253254
if (blocked()) return
255+
props.close?.()
254256
navigate("/")
255-
queueMicrotask(() => server.setActive(key))
257+
const activate = () => {
258+
if (location.pathname !== "/") {
259+
setTimeout(activate, 16)
260+
return
261+
}
262+
setTimeout(() => server.setActive(key), 0)
263+
}
264+
setTimeout(activate, 0)
256265
}}
257266
>
258267
<ServerHealthIndicator health={health[key]} />

packages/app/src/components/status-popover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export function StatusPopover() {
5858
<div class="w-[360px] h-14 rounded-xl bg-background-strong shadow-[var(--shadow-lg-border-base)]" />
5959
}
6060
>
61-
<Body shown={shown} />
61+
<Body shown={shown} close={() => setShown(false)} />
6262
</Suspense>
6363
</Show>
6464
</Popover>

packages/app/src/context/global-sync.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,7 @@ function createGlobalSync() {
371371
onCleanup(() => {
372372
queue.dispose()
373373
})
374-
onCleanup(() => {
375-
for (const directory of Object.keys(children.children)) {
376-
children.disposeDirectory(directoryKey(directory))
377-
}
378-
})
374+
onCleanup(children.disposeAll)
379375

380376
onMount(() => {
381377
if (typeof requestAnimationFrame === "function") {

packages/app/src/context/global-sync/child-store.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ export function createChildStoreManager(input: {
9292
})
9393
}
9494

95+
function disposeChild(key: DirectoryKey) {
96+
const dispose = disposers.get(key)
97+
if (!key || !children[key]) return false
98+
vcsCache.delete(key)
99+
metaCache.delete(key)
100+
iconCache.delete(key)
101+
lifecycle.delete(key)
102+
disposers.delete(key)
103+
delete children[key]
104+
input.onDispose(key)
105+
if (dispose) {
106+
dispose()
107+
}
108+
return true
109+
}
110+
95111
function disposeDirectory(directory: DirectoryKey) {
96112
const key = directory
97113
if (
@@ -106,18 +122,13 @@ export function createChildStoreManager(input: {
106122
return false
107123
}
108124

109-
vcsCache.delete(key)
110-
metaCache.delete(key)
111-
iconCache.delete(key)
112-
lifecycle.delete(key)
113-
const dispose = disposers.get(key)
114-
if (dispose) {
115-
dispose()
116-
disposers.delete(key)
125+
return disposeChild(key)
126+
}
127+
128+
function disposeAll() {
129+
for (const directory of Object.keys(children)) {
130+
disposeChild(directoryKey(directory))
117131
}
118-
delete children[key]
119-
input.onDispose(key)
120-
return true
121132
}
122133

123134
function runEviction(skip?: string) {
@@ -329,6 +340,7 @@ export function createChildStoreManager(input: {
329340
unpin,
330341
pinned,
331342
disposeDirectory,
343+
disposeAll,
332344
runEviction,
333345
vcsCache,
334346
metaCache,

packages/app/src/utils/solid-dnd.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useDragDropContext } from "@thisbeyond/solid-dnd"
22
import type { Transformer } from "@thisbeyond/solid-dnd"
3-
import { createRoot, onCleanup, type JSXElement } from "solid-js"
3+
import type { JSXElement } from "solid-js"
44

55
type DragEvent = { draggable?: { id?: unknown } }
66

@@ -27,20 +27,16 @@ const createAxisConstraint = (axis: "x" | "y", transformerId: string) => (): JSX
2727
if (!context) return null
2828
const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context
2929
const transformer = createTransformer(transformerId, axis)
30-
const dispose = createRoot((dispose) => {
31-
onDragStart((event) => {
32-
const id = getDraggableId(event)
33-
if (!id) return
34-
addTransformer("draggables", id, transformer)
35-
})
36-
onDragEnd((event) => {
37-
const id = getDraggableId(event)
38-
if (!id) return
39-
removeTransformer("draggables", id, transformer.id)
40-
})
41-
return dispose
30+
onDragStart((event) => {
31+
const id = getDraggableId(event)
32+
if (!id) return
33+
addTransformer("draggables", id, transformer)
34+
})
35+
onDragEnd((event) => {
36+
const id = getDraggableId(event)
37+
if (!id) return
38+
removeTransformer("draggables", id, transformer.id)
4239
})
43-
onCleanup(dispose)
4440
return null
4541
}
4642

packages/desktop-electron/src/main/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ setupApp()
8787
function setupApp() {
8888
ensureLoopbackNoProxy()
8989
app.commandLine.appendSwitch("proxy-bypass-list", "<-loopback>")
90+
if (!app.isPackaged) app.commandLine.appendSwitch("remote-debugging-port", "9222")
9091

9192
if (!app.requestSingleInstanceLock()) {
9293
app.quit()

0 commit comments

Comments
 (0)