diff options
| author | Adam Malczewski <[email protected]> | 2026-06-25 10:55:51 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-25 10:55:51 +0900 |
| commit | 38db3827870960f466be89afbc49f91238d46144 (patch) | |
| tree | 24cb1b896dfadc31e72552dbe67f00530881242e /src/App.svelte | |
| parent | 17ce47987e673b6618454d033885b17b2a01912e (diff) | |
| download | dispatch-web-38db3827870960f466be89afbc49f91238d46144.tar.gz dispatch-web-38db3827870960f466be89afbc49f91238d46144.zip | |
feat: workspaces shell + cwd-lsp rename + mcp/settings/system-prompt features + app wiring
- workspaces: URL-driven conversation grouping (home listing at /, routing,
store, http adapter, WorkspaceCard) wired into the App.svelte shell
- rename features/workspace -> features/cwd-lsp (the cwd/lsp status feature)
- new features: mcp (status view), settings (chat-limit field), system-prompt
(prompt builder), all rendered via the generic surface host
- chat: store + ChatView updates
- tabs: tabs-store updates
- app wiring: ErrorModal (full-screen error surface), app/App.svelte + store.svelte
This commit makes HEAD typecheck clean for the first time: the prior HEAD
(c95cc77) imported features/settings from app/App.svelte but never committed
the feature, so only the full working tree was green.
Diffstat (limited to 'src/App.svelte')
| -rw-r--r-- | src/App.svelte | 71 |
1 files changed, 69 insertions, 2 deletions
diff --git a/src/App.svelte b/src/App.svelte index ffd5543..9d06b9f 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,7 +1,74 @@ <script lang="ts"> import { App, createAppStore } from "./app"; + import { createHistoryAdapter } from "./adapters/history"; + import { + createWorkspaceHttp, + createWorkspaceStore, + pageTitle, + parsePath, + WorkspacesHome, + type Route, + } from "./features/workspaces"; + import { resolveHttpUrl } from "./app/resolve-http-url"; - const store = createAppStore(); + // Parse the route BEFORE creating the store so the boot draft + active + // workspace are correct from the first render (no flash of the "default" + // workspace when deep-linking to /<id>). + const history = createHistoryAdapter(); + const initialRoute = parsePath(history.path); + const store = createAppStore( + initialRoute.kind === "workspace" ? { workspaceId: initialRoute.id } : {}, + ); + + // The workspace HTTP edge shares the same httpBase resolution as the store. + const httpBase = resolveHttpUrl( + { + VITE_HTTP_URL: import.meta.env.VITE_HTTP_URL, + VITE_HTTP_PORT: import.meta.env.VITE_HTTP_PORT, + }, + typeof location !== "undefined" ? location : undefined, + ); + const workspaceStore = createWorkspaceStore( + createWorkspaceHttp(httpBase, globalThis.fetch.bind(globalThis)), + ); + + let route = $state<Route>(initialRoute); + + // React to back/forward + programmatic navigation. + $effect(() => { + return history.subscribe((path) => { + route = parsePath(path); + }); + }); + + // On entering a workspace: scope the store to it (idempotent — skip if already + // scoped) + ensure the workspace exists (create-on-miss, so it appears in the + // home list immediately). The backend also auto-creates on `chat.send`. + $effect(() => { + if (route.kind !== "workspace") return; + const id = route.id; + if (store.activeWorkspaceId !== id) { + store.setActiveWorkspace(id); + } + void workspaceStore.ensure(id); + }); + + // Keep the browser tab title in sync with the route: "Dispatch" on the home + // page, "Dispatch: {title}" on a workspace page (falling back to the URL slug + // until the list loads it). Reads `route` + the workspace list, so it re-runs + // on navigation and again once `ensure` refreshes the list. + $effect(() => { + if (typeof document === "undefined") return; + document.title = pageTitle(route, workspaceStore.list); + }); + + function navigate(path: string): void { + history.navigate(path); + } </script> -<App {store} /> +{#if route.kind === "home"} + <WorkspacesHome store={workspaceStore} onNavigate={navigate} /> +{:else} + <App {store} /> +{/if} |
