summaryrefslogtreecommitdiffhomepage
path: root/src/App.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-25 10:55:51 +0900
committerAdam Malczewski <[email protected]>2026-06-25 10:55:51 +0900
commit38db3827870960f466be89afbc49f91238d46144 (patch)
tree24cb1b896dfadc31e72552dbe67f00530881242e /src/App.svelte
parent17ce47987e673b6618454d033885b17b2a01912e (diff)
downloaddispatch-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.svelte71
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}