summaryrefslogtreecommitdiffhomepage
path: root/src/app
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-05 17:33:06 +0900
committerAdam Malczewski <[email protected]>2026-09-05 17:33:06 +0900
commit254f90a4081a3f15dfe54277da922579dbaee254 (patch)
treef5f4e409196109baf938cd9d557e244242df18ce /src/app
parent535215bfd1fee6087613108f42d28cd2807184d3 (diff)
downloaddispatch-web-254f90a4081a3f15dfe54277da922579dbaee254.tar.gz
dispatch-web-254f90a4081a3f15dfe54277da922579dbaee254.zip
feat(layout): plan 04 — ABI layout.applyOps + reset affordance
Diffstat (limited to 'src/app')
-rw-r--r--src/app/plugin-host.svelte.ts11
-rw-r--r--src/app/store.svelte.ts19
2 files changed, 28 insertions, 2 deletions
diff --git a/src/app/plugin-host.svelte.ts b/src/app/plugin-host.svelte.ts
index 729a8aa..8054005 100644
--- a/src/app/plugin-host.svelte.ts
+++ b/src/app/plugin-host.svelte.ts
@@ -28,6 +28,7 @@ import {
usablePluginIds,
} from "../core/plugin-abi";
import {
+ type LayoutAbiPort,
registerPluginView,
unregisterViewsByPlugin,
type ViewComponent,
@@ -60,6 +61,12 @@ export interface PluginHostOptions {
/** The ACTIVE workspace provider (read per sync — workspace.set trigger). */
readonly activeWorkspaceId: () => string;
readonly surfaces: FrontendSurfacesPorts;
+ /**
+ * The layout port (plan 04 §4.5) — the ABI's `api.layout` (applyOps to the
+ * ACTIVE workspace's provider + getTree, null in no-provider mode). Built
+ * by the composition root over the layout store + ops HTTP edge.
+ */
+ readonly layout: LayoutAbiPort;
}
export interface PluginHost {
@@ -108,6 +115,10 @@ export function createPluginHost(opts: PluginHostOptions): PluginHost {
info: (msg) => console.info(`[frontend-plugin:${pluginId}] ${msg}`),
warn: (msg) => console.warn(`[frontend-plugin:${pluginId}] ${msg}`),
},
+ // plan 04 §4.5: the layout port — the composition-root-built adapter
+ // over the layout store + the ops HTTP edge (the plugin never sees
+ // either; only this typed port).
+ layout: opts.layout,
};
}
diff --git a/src/app/store.svelte.ts b/src/app/store.svelte.ts
index 8a80160..c1d265f 100644
--- a/src/app/store.svelte.ts
+++ b/src/app/store.svelte.ts
@@ -84,7 +84,12 @@ import type {
} from "../features/heartbeat";
import { normalizeHeartbeatConfig, normalizeHeartbeatRuns } from "../features/heartbeat";
import type { LayoutStore } from "../features/layout";
-import { createLayoutHttp, createLayoutStore, listPanelViews } from "../features/layout";
+import {
+ createLayoutAbiPort,
+ createLayoutHttp,
+ createLayoutStore,
+ listPanelViews,
+} from "../features/layout";
import type {
NamedAgent,
NamedAgentDeleteResult,
@@ -723,8 +728,9 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore {
// ephemeral, logged once, no error UI). Probes run on boot, on every
// workspace switch, and on `extensions-changed` (§2.6); the one-way
// localStorage migration (§2.7) arms on the boot probe.
+ const layoutHttp = createLayoutHttp(httpBase, fetchImpl);
const layoutStore = createLayoutStore({
- http: createLayoutHttp(httpBase, fetchImpl),
+ http: layoutHttp,
workspaceId: activeWorkspaceId,
storage: localStorageOpt,
knownViews: listPanelViews().map((v) => v.id),
@@ -1417,6 +1423,15 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore {
invokeSurfaceOp(surfaceId, actionId, payload, conversationId),
getSpec: (surfaceId) => surfaceSpecOp(surfaceId),
},
+ // plan 04 §4.5: the plugins' `api.layout` port — applyOps posts to the
+ // ACTIVE workspace's provider (the ops route) and feeds the returned
+ // tree back through the store (instant local echo; the WS broadcast is
+ // every client's authoritative copy); getTree exposes the active tree
+ // only while a provider is PRESENT (null = no-provider mode).
+ layout: createLayoutAbiPort({
+ store: layoutStore,
+ postOps: (workspaceId, ops) => layoutHttp.postOps(workspaceId, ops),
+ }),
});
const socketOpts: SurfaceSocketOptions = {