diff options
| author | Adam Malczewski <[email protected]> | 2026-09-07 19:16:08 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-09-07 19:16:08 +0900 |
| commit | 9c201c60a727106f1e3bf8c2af5f86aa7427098f (patch) | |
| tree | 37c0fa07913154386785126bd75093866f05c975 /src/app | |
| parent | 61733924352285df6c44f66edf69ea6f738fae3e (diff) | |
| download | dispatch-web-9c201c60a727106f1e3bf8c2af5f86aa7427098f.tar.gz dispatch-web-9c201c60a727106f1e3bf8c2af5f86aa7427098f.zip | |
feat(workspaces): sync the home tile layout per device orientation
The Win8-Start home grid's arrangement now syncs from the backend's
home-tile-layout plugin: ONE shared document per device orientation
(wide = landscape/desktop, tall = portrait/mobile) — every landscape
device renders the same layout, every portrait device the same other
one. localStorage persistence is retired (no migration — devices start
from the shared document; the first arrangement creates it).
- adapter/tile-layout-http.ts: the injected HTTP edge (GET/PUT/DELETE
/home/tile-layout/:mode), the features/layout adapter's pattern
- tile-layout-store: server-backed, keyed by geometry mode — boot +
orientation-change probes (stale in-flight responses never land),
locally-applied edits + debounced full-document PUT with rollback to
the last server document, WS push filtered to the active orientation,
quiet ephemeral degrade when the backend is unreachable (no error UI),
sync gated while a probe is in flight
- WorkspacesHome locks the store's orientation to the resolved grid
geometry (the landscape/portrait split IS the device-type split)
- app store: the tile store lives beside the layout store; the WS
dispatch feeds 'tile-layout' broadcasts into it; 'tiles' prop from
App.svelte (the page entry owns the httpBase)
- ws adapter: the 'tile-layout' server message (parse guard) + socket
handler; wire conformance case
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/store.svelte.ts | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/src/app/store.svelte.ts b/src/app/store.svelte.ts index c1d265f..a858927 100644 --- a/src/app/store.svelte.ts +++ b/src/app/store.svelte.ts @@ -34,6 +34,7 @@ import type { SystemPromptVariable, SystemPromptVariablesResponse, TestComputerResponse, + TileLayoutChangedMessage, WarmRequest, WarmResponse, WorkspaceSetMessage, @@ -108,6 +109,9 @@ import { type VisionSettings, type VisionSettingsPatch, } from "../features/vision"; +import { createTileLayoutHttp } from "../features/workspaces/adapter/tile-layout-http"; +import type { TileLayoutStore } from "../features/workspaces/tile-layout-store.svelte"; +import { createTileLayoutStore } from "../features/workspaces/tile-layout-store.svelte"; import { createPluginHost } from "./plugin-host.svelte"; import { resolveHttpUrl } from "./resolve-http-url"; import { resolveWsUrl } from "./resolve-ws-url"; @@ -581,6 +585,15 @@ export interface AppStore { */ readonly layout: LayoutStore; /** + * The home grid's synced tile layout (ONE shared document per device + * orientation — `wide` = landscape/desktop, `tall` = portrait/mobile), + * served by the backend's home-tile-layout plugin. The home page locks the + * active orientation to the resolved grid geometry; this store owns the + * probes, the WS-push filter, the debounced PUT + rollback, and the + * offline degrade. + */ + readonly tileLayout: TileLayoutStore; + /** * Loaded provider-tier plugins effectively enabled in the ACTIVE workspace * (`GET /workspaces/:id/plugins/providers-status`). `null` = unknown — the * first-boot guardrail renders only on a confident `0` (§7). @@ -736,6 +749,17 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { knownViews: listPanelViews().map((v) => v.id), }); + // ── Home tile-layout (the synced Win8-Start grid) ───────────────────────── + // ONE shared document PER device orientation (`wide` = landscape/desktop, + // `tall` = portrait/mobile), persisted by the backend's home-tile-layout + // plugin and broadcast over WS (`tile-layout`). The home page locks the + // active orientation to the resolved grid geometry and renders + // `tileLayout.entries`; this store owns the probes, the WS-push filter, + // the debounced PUT + rollback, and the offline degrade. + const tileLayoutStore = createTileLayoutStore({ + http: createTileLayoutHttp(httpBase, fetchImpl), + }); + /** * Refetch the model catalog for the ACTIVE workspace * (`GET /models?workspaceId=`, plan 06 §6.5 — workspace-aware; a backend @@ -1543,6 +1567,12 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { // workspaces (the server fans out to everyone). layoutStore.onServerTree(msg.workspaceId, msg.tree); }, + onTileLayout(msg: TileLayoutChangedMessage) { + // Multi-client coherence for the home grid's synced layouts: a write + // on ANY client re-renders here — the store filters the OTHER + // orientation (the server fans out to everyone). + tileLayoutStore.onServerTileLayout(msg.mode, msg.data); + }, }; if (opts?.socketFactory !== undefined) { socketOpts.socketFactory = opts.socketFactory; @@ -2670,6 +2700,9 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { get layout(): LayoutStore { return layoutStore; }, + get tileLayout(): TileLayoutStore { + return tileLayoutStore; + }, get enabledProviderCount(): number | null { return enabledProviderCount; }, |
