From e3d8e8a124d97e44b6de5b4dec20b96b4db9bb24 Mon Sep 17 00:00:00 2001 From: benjsmith Date: Thu, 8 Oct 2026 22:41:05 +0000 Subject: [PATCH 1/3] Graph: use Curiosity Engine's viewer only (0.14.0) Drop Switchbay's built-in Graph viewer from the PWA. The Graph tab, the wiki page list and the Zen graph now mount Curiosity Engine's own viewer through /embed/ce (CEEmbed.create, shared sidebar + canvas session). - GET /api/settings reports ce_graph {installed, has_wiki}; without CE the Graph tab is hidden and graph shortcuts fall back to the Editor, with no wiki a placeholder is shown. Files uploader, RAG, Agents and Library do not depend on CE. - proxied_skill_embeds now only picks the Agents surface (okstratr observer vs built-in dashboard); Graph ignores it. Settings copy renamed "Agents: okstratr observer". No settings migration needed. - Host shims on the CE embed: page HTML sanitized before CE's modal renders it; KaTeX, table -> Sheet/Plot and slideshow in CE's modal; CE split panel routed to /api/workspaces/split; Procedure/Execution label-type rows; in-place Atlas/Classic switch (no app reload); sidebar + keeps opening UploadVaultDialog (/api/upload-vault). - Keep the {{WORKSPACE}} sidebar-header substitution; factor it into ceSidebarHeader.ts with node tests and an e2e check that the header shows the workspace name, never the raw placeholder. - Remove GraphTab, CurationReplay, WikiPane, the fork's edit.js and sidebar.js, and their e2e specs. The remaining fork under widgets/graph is kept only for the VS Code graph webview. - Docs: architecture says Graph comes from CE; notices updated. - Bump to 0.14.0. Signed-off-by: benjsmith --- CHANGELOG.md | 34 ++ docs/THIRD-PARTY-NOTICES.md | 3 +- docs/architecture.md | 48 +- docs/license-risk-report.md | 2 +- frontend/package.json | 2 +- frontend/src/App.tsx | 36 +- frontend/src/center/builtinTabs.tsx | 14 +- frontend/src/host.ts | 2 +- frontend/src/index.css | 194 ++------- frontend/src/layout/SettingsModal.tsx | 15 +- frontend/src/layout/WorkspaceSwitcher.tsx | 2 +- frontend/src/palette.css | 268 ++++++++++++ frontend/src/sidebar/Sidebar.tsx | 76 ++-- frontend/src/sidebar/WikiPane.tsx | 70 --- frontend/src/walkthrough/Walkthrough.tsx | 4 +- frontend/src/webview-graph.ts | 1 - frontend/src/widgets/embed/CeAtlasEmbed.tsx | 257 ++++++++++- frontend/src/widgets/embed/CeGraphSurface.tsx | 37 ++ frontend/src/widgets/embed/CeSidebarSlot.tsx | 5 +- .../src/widgets/embed/ceEmbedBootstrap.ts | 13 +- frontend/src/widgets/embed/ceEmbedSession.ts | 17 +- frontend/src/widgets/embed/ceEmbedShell.html | 12 +- .../src/widgets/embed/ceHostBridge.test.ts | 57 +++ frontend/src/widgets/embed/ceHostBridge.ts | 336 ++++++++++++++ .../src/widgets/embed/ceSidebarHeader.test.ts | 41 ++ frontend/src/widgets/embed/ceSidebarHeader.ts | 34 ++ .../src/widgets/embed/ceSidebarShell.html | 2 +- .../widgets/embed/useProxiedSkillEmbeds.ts | 116 +++-- frontend/src/widgets/graph/CurationReplay.tsx | 73 ---- frontend/src/widgets/graph/GraphTab.tsx | 411 ------------------ frontend/src/widgets/graph/ce-graph.css | 268 +----------- frontend/src/widgets/graph/graphSearch.ts | 4 +- frontend/src/widgets/graph/init.ts | 32 +- frontend/src/widgets/graph/load.ts | 18 +- frontend/src/widgets/graph/static.d.ts | 15 +- frontend/src/widgets/graph/static/edit.js | 264 ----------- frontend/src/widgets/graph/static/sidebar.js | 261 ----------- frontend/src/widgets/graph/template.ts | 34 +- frontend/src/zen/ZenBrowserTab.tsx | 20 +- frontend/src/zen/ZenShell.tsx | 10 +- frontend/tests/e2e/atlas-search.spec.ts | 179 -------- frontend/tests/e2e/atlas-select.spec.ts | 87 ---- frontend/tests/e2e/graph-ce-embed.spec.ts | 136 ++++++ frontend/tests/e2e/graph-search.spec.ts | 100 ----- frontend/tests/e2e/minimap-zoom.spec.ts | 99 ----- frontend/tests/e2e/mock_backend.py | 78 +++- pyproject.toml | 2 +- src/switchbay/__init__.py | 2 +- src/switchbay/cebridge.py | 21 + src/switchbay/daemon.py | 3 + tests/unit/test_ce_graph_availability.py | 54 +++ .../unit/test_procedure_execution_palette.py | 34 +- tests/unit/test_upload_vault.py | 19 +- uv.lock | 2 +- 54 files changed, 1673 insertions(+), 2251 deletions(-) create mode 100644 frontend/src/palette.css delete mode 100644 frontend/src/sidebar/WikiPane.tsx create mode 100644 frontend/src/widgets/embed/CeGraphSurface.tsx create mode 100644 frontend/src/widgets/embed/ceHostBridge.test.ts create mode 100644 frontend/src/widgets/embed/ceHostBridge.ts create mode 100644 frontend/src/widgets/embed/ceSidebarHeader.test.ts create mode 100644 frontend/src/widgets/embed/ceSidebarHeader.ts delete mode 100644 frontend/src/widgets/graph/CurationReplay.tsx delete mode 100644 frontend/src/widgets/graph/GraphTab.tsx delete mode 100644 frontend/src/widgets/graph/static/edit.js delete mode 100644 frontend/src/widgets/graph/static/sidebar.js delete mode 100644 frontend/tests/e2e/atlas-search.spec.ts delete mode 100644 frontend/tests/e2e/atlas-select.spec.ts create mode 100644 frontend/tests/e2e/graph-ce-embed.spec.ts delete mode 100644 frontend/tests/e2e/graph-search.spec.ts delete mode 100644 frontend/tests/e2e/minimap-zoom.spec.ts create mode 100644 tests/unit/test_ce_graph_availability.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 304164c..4c364aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,40 @@ Human-curated release notes. Earlier 0.9.x notes also live on the [GitHub releases](https://github.com/benjsmith/switchbay/releases) page. +## 2026-10-08 — v0.14.0 + +**Migration:** none. Your settings carry over. **Breaking:** the Graph tab is +now always Curiosity Engine's own viewer; Switch Bay's separate copy of it is +gone. + +Works with curiosity-engine 1.9.2, curiosity-merge 0.8.4, and okstratr 0.2.0. + +### Changed + +- **Graph is Curiosity Engine's viewer** — the Graph tab, the wiki page list + in the left column, and the graph in Zen all show Curiosity Engine's own + viewer. Search, type filters, Atlas and Classic views, keyboard shortcuts, + the page window, note editing, curation replay and splitting a workspace + all come from Curiosity Engine, so they match what you see in its own + viewer and pick up its improvements when you update it. +- **Switch Bay extras inside the CE viewer** — pages still render maths, + tables still open as a Sheet or Plot, pages can still start a slideshow, + Procedures and Executions have their own label colours, and the + button + still opens Switch Bay's upload dialog. +- **Without Curiosity Engine** — the Graph tab is hidden and links that would + open it go to the Editor instead. Files, uploading to the vault, RAG, + Agents and Library work as before. A workspace with no wiki yet shows a + short note in the Graph tab instead of an empty canvas. +- **Settings → "Agents: okstratr observer"** — the old "Proxied skill + embeds" switch now only chooses between the okstratr observer and the + built-in Agents dashboard. It no longer changes the Graph tab. Your + existing choice is kept. + +### Fixed + +- **Safer page window** — page content shown in the Graph page window is + cleaned before it is displayed, so scripts embedded in a wiki page can't run. + ## 2026-10-08 — v0.13.2 **Migration:** none. **Breaking:** none. diff --git a/docs/THIRD-PARTY-NOTICES.md b/docs/THIRD-PARTY-NOTICES.md index 025f258..8e083d3 100644 --- a/docs/THIRD-PARTY-NOTICES.md +++ b/docs/THIRD-PARTY-NOTICES.md @@ -27,7 +27,8 @@ obligation arises as we do not modify their files. - **Graph / wiki-view** (`frontend/src/widgets/graph/static/*.js`) is a fork of **curiosity-engine**'s wiki-view, used under the **MIT License** - (Copyright (c) 2026, curiosity-engine authors). Full MIT text below. + (Copyright (c) 2026, curiosity-engine authors), and now ships only in the + VS Code extension's graph webview. Full MIT text below. - **Knowledge Atlas** (`frontend/src/widgets/graph/static/vendor/knowledge-atlas.js`) is the first-party IIFE from curiosity-engine's `packages/knowledge-atlas`, same MIT license. diff --git a/docs/architecture.md b/docs/architecture.md index c7688f7..3b515bf 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,4 +1,4 @@ -# Architecture (v0.13) +# Architecture (v0.14) How Switchbay **hosts** first-party skills today. Product vocabulary and data flows stay in [`concepts-and-data-flow.md`](concepts-and-data-flow.md). @@ -28,8 +28,11 @@ Graph / Agents skill surfaces. non-loopback rejected). - Injects `X-CE-Host: switchbay` / `X-Okstratr-Host: switchbay`. - Vite dev proxies `/embed` to the daemon. -- Settings → **Proxied skill embeds** switches Graph → CE and Agents → - okstratr observer; turning it off restores built-ins. The proxy itself +- The **Graph** tab is always Curiosity Engine's own viewer, mounted + through `/embed/ce`. Switchbay has no built-in graph viewer. +- Settings → **Agents: okstratr observer** (`proxied_skill_embeds`) only + chooses the Agents surface: okstratr observer when on, the built-in + Agents dashboard when off. It does not affect Graph. The proxy itself stays on either way. ## Same-document skill mount @@ -42,14 +45,30 @@ Graph / Agents skill surfaces. - Implementation: `frontend/src/widgets/embed/` (`ProxiedSkillPanel`, `embedMount.ts`). -## Dual-mount Graph - -- Shell left column: CE Pages|Files (`CeSidebarSlot`) persists across - Graph / Agents / Editor when proxied embeds are on for a CE workspace. -- Graph pane: atlas canvas only (`CeAtlasEmbed`); keeps the atlas warm - when you leave the tab; `atlas-cache.js` + workspace key for cold Graph cache. -- Prefers `window.CEEmbed.create` / `mount` when CE provides it; one - session survives Graph unmount. +## Graph comes from Curiosity Engine + +- Graph, the wiki page list, page modal (incl. note editing), search, + type filters, Atlas/Classic, keybindings and curation replay are all + CE's viewer, loaded through `/embed/ce` with `window.CEEmbed.create`. +- `GET /api/settings` reports `ce_graph: {installed, has_wiki}`. + CE not installed → the Graph tab is hidden and Graph shortcuts fall back + to the Editor. Installed but no `wiki/` yet → a short placeholder. +- Shell left column: CE Pages (`CeSidebarSlot`) above Switchbay's own + Files | Sources browser. The Files uploader, RAG, Agents and Library do + not depend on CE. +- Graph pane: canvas only (`CeAtlasEmbed`, wrapped by `CeGraphSurface`); + one `CeEmbedSession` per workspace shares `data.json` between sidebar and + canvas, parks the canvas across tab switches and remounts it on wiki + `files_changed`. +- Host shims (`ceHostBridge.ts`, `ceEmbedShell.html`): page HTML is + sanitized before CE's modal renders it; KaTeX, table → Sheet / Plot and + the slideshow button are added to CE's modal; CE's split panel is routed + to `/api/workspaces/split`; Procedure / Execution label-type rows; the + sidebar `+` opens Switchbay's upload dialog (`/api/upload-vault`); the + sidebar header's `{{WORKSPACE}}` placeholder is filled from `data.json`. +- The forked viewer under `frontend/src/widgets/graph/` is kept **only** + for the VS Code extension's graph webview (`src/webview-graph.ts`); the + PWA never imports it. ## Core-skills auto-start @@ -69,7 +88,8 @@ Graph / Agents skill surfaces. `local_ingest` (prefer CE drop-ingest endpoint; rail LLM only when run metadata opts in). -## Built-in Graph and Agents +## Built-in Agents -Built-in GraphTab, AgentDashboardTab, and filebrowser stay available. -Proxied skill embeds are opt-in in Settings. +The built-in Agents dashboard and file browser stay available; turning +off **Agents: okstratr observer** in Settings selects the built-in +dashboard. There is no built-in Graph. diff --git a/docs/license-risk-report.md b/docs/license-risk-report.md index a781fa3..331f507 100644 --- a/docs/license-risk-report.md +++ b/docs/license-risk-report.md @@ -17,7 +17,7 @@ what was remediated. |------|------|--------| | Proprietary code in the dependency tree | `@univerjs-pro/*` (Univer commercial tier) | ✅ **RESOLVED** — dropped `@univerjs/presets`; built on granular Apache-2.0 packages; Pro tree fully removed (0 in lockfile/bundle); Sheet tab verified working | | No aggregated attribution file | ~460 MIT/BSD/Apache/ISC deps require notice preservation | ✅ **RESOLVED** — `docs/THIRD-PARTY-NOTICES.md` generated | -| Forked MIT code without its notice | graph static JS forked from curiosity-engine (MIT) | ✅ **RESOLVED** — MIT attribution header added to the 5 forked files + NOTICES entry | +| Forked MIT code without its notice | graph static JS forked from curiosity-engine (MIT) | ✅ **RESOLVED** — MIT attribution header added to the forked files + NOTICES entry (since 0.14 the fork ships only in the VS Code graph webview; `edit.js` and `sidebar.js` were removed) | | Undocumented dual-license elections | jszip, dompurify, certifi, tqdm | ✅ **RESOLVED** — recorded in NOTICES | **No GPL / AGPL / LGPL / SSPL anywhere** in either dependency tree — the diff --git a/frontend/package.json b/frontend/package.json index 4ea60f6..9d7eaaa 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "switchbay-frontend", "private": true, - "version": "0.13.2", + "version": "0.14.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 189c77d..94a30db 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -29,7 +29,9 @@ import { installKeyRegistry, registerChord, registerCombo } from "./keys"; import { RailSocket, type Mode, type Selection, type ServerMessage, type TabSpec, type Workspaces } from "./ws"; import { applyWebPolicy, bindWebPolicy, loadWebPolicy } from "./lib/webPolicy"; import { SelectionProvider } from "./selection/SelectionContext"; -import "./widgets/graph/load"; // window.Sidebar/Subgraph/Modal/Graph + ce-graph.css +import "./palette.css"; // theme vars + wiki-type palette + shared markdown view +import { useCeGraph } from "./widgets/embed/useProxiedSkillEmbeds"; +import { resetCeEmbedSession } from "./widgets/embed/ceEmbedSession"; import type { GraphData } from "./widgets/graph/types"; import Walkthrough, { maybeAutoStartWalkthrough } from "./walkthrough/Walkthrough"; import UploadVaultDialog from "./widgets/upload/UploadVaultDialog"; @@ -415,11 +417,18 @@ export default function App() { // Tab scoping (control surface v1): thread-scoped tabs render only // while their thread is focused. Filtered client-side so a thread // switch shows/hides them instantly, no round-trip. + // Graph is Curiosity Engine's viewer; without a CE install the tab + // (and every route into it) is hidden rather than shown broken. + const ceGraph = useCeGraph(); + const graphHidden = ceGraph !== null && !ceGraph.installed; + const graphHiddenRef = useRef(graphHidden); + graphHiddenRef.current = graphHidden; const visibleTabs = useMemo( () => mode.tabs.filter( - (t) => !t.thread || t.thread === focusedThread, + (t) => (!t.thread || t.thread === focusedThread) + && !(graphHidden && t.kind === "graph"), ), - [mode, focusedThread], + [mode, focusedThread, graphHidden], ); // If the active tab just went out of scope (thread switch), fall // back to the first visible one. @@ -1820,6 +1829,10 @@ export default function App() { }; }, [setSelection]); + /** A new workspace gets a fresh CE embed session (Power remounts the + * sidebar per workspace too; this covers Zen, which has none). */ + useEffect(() => () => { resetCeEmbedSession(); }, [workspace]); + /** CE's sidebar + graph.js still write `#page=` to window.location.hash * when the user clicks. Translate that into a selection-layer dispatch. */ useEffect(() => { @@ -1846,13 +1859,13 @@ export default function App() { return () => window.removeEventListener("hashchange", onHash); }, [setSelection]); - /** Highlight the active row in the Browser sidebar regardless of which - * tab is active. Modal/graph effects live in GraphTab (they can only - * fire when that tab is mounted). */ + /** Highlight the active row in CE's page sidebar regardless of which + * tab is active. Modal/graph effects live in CeAtlasEmbed (they can + * only fire when the Graph canvas is mounted). */ useEffect(() => { if (!graphData) return; if (!selection || selection.kind !== "page") return; - try { window.Sidebar.setActive(selection.id); } catch { /* ignore */ } + try { window.Sidebar?.setActive(selection.id); } catch { /* ignore */ } }, [selection, graphData]); /** One-shot tips to drop into the rail the first time the user opens @@ -1933,8 +1946,8 @@ export default function App() { values: detail.values, }); switchToKindRef.current?.("univer"); - // Don't call window.Modal.close() — its onClose callback - // (registered in GraphTab.tsx) nulls selection back to null, + // Don't call window.Modal.close() — closing the doc modal + // (CeAtlasEmbed watches it) nulls selection back to null, // which clobbers the table-data we just set. The modal stays // open behind the now-active Sheet tab and the user can dismiss // it on next visit. @@ -2410,7 +2423,10 @@ export default function App() { }, [workspace, pushToast]); const switchToKind = useCallback( - (kind: string) => { + (kindIn: string) => { + // No CE → no Graph tab: page routes that would open the graph's + // doc modal open the page in the Editor instead. + const kind = kindIn === "graph" && graphHiddenRef.current ? "markdown" : kindIn; // Zen: the graph is always visible (left pane); every other // kind lands on the right-pane surface host. Reaching a kind // clears a pending artifact of that kind — the user got there. diff --git a/frontend/src/center/builtinTabs.tsx b/frontend/src/center/builtinTabs.tsx index 56192e3..498d168 100644 --- a/frontend/src/center/builtinTabs.tsx +++ b/frontend/src/center/builtinTabs.tsx @@ -2,7 +2,7 @@ import { lazy } from "react"; import SketchErrorBoundary from "../widgets/sketch/ErrorBoundary"; import { registerTabKind, type TabComponent } from "./tabRegistry"; import ProxiedSkillPanel from "../widgets/embed/ProxiedSkillPanel"; -import CeAtlasEmbed from "../widgets/embed/CeAtlasEmbed"; +import CeGraphSurface from "../widgets/embed/CeGraphSurface"; import { useProxiedSkillEmbeds } from "../widgets/embed/useProxiedSkillEmbeds"; /** @@ -23,7 +23,6 @@ import { useProxiedSkillEmbeds } from "../widgets/embed/useProxiedSkillEmbeds"; * entries can keep a uniform `(ctx) => JSX` shape. */ -const GraphTab = lazy(() => import("../widgets/graph/GraphTab")); const EditorTab = lazy(() => import("../widgets/editor/EditorTab")); const DuckDBTab = lazy(() => import("../widgets/duckdb/DuckDBTab")); const SheetTab = lazy(() => import("../widgets/sheet/SheetTab")); @@ -43,15 +42,8 @@ const ReportDocTab = lazy(() => import("../widgets/library/ReportDocTab")); const ThrustersTab = lazy(() => import("../widgets/thrusters/ThrustersTab")); const OwidTab = lazy(() => import("../widgets/owid/OwidTab")); -const GraphAdapter: TabComponent = ({ graphData, graphError }) => { - const proxied = useProxiedSkillEmbeds(); - if (proxied === null) { - return

Loading…

; - } - // Graph = CE atlas embed skin (dedicated mount; not full HTML remount). - if (proxied) return ; - return ; -}; +/** Graph = Curiosity Engine's own viewer, mounted through /embed/ce. */ +const GraphAdapter: TabComponent = () => ; const EditorAdapter: TabComponent = ({ tab }) => ; const DuckDBAdapter: TabComponent = () => ; const SheetAdapter: TabComponent = () => ; diff --git a/frontend/src/host.ts b/frontend/src/host.ts index bef28c5..29975b8 100644 --- a/frontend/src/host.ts +++ b/frontend/src/host.ts @@ -6,7 +6,7 @@ * in webviews should call this interface; the PWA implements it with * REST/WS, the plugin with `acquireVsCodeApi().postMessage`. * - * This file is the contract. Wiring GraphTab / AgentDashboard / preview + * This file is the contract. Wiring the Graph embed / AgentDashboard / preview * onto it is a later spike step — do not fetch the daemon from a * plugin webview in the meantime. */ diff --git a/frontend/src/index.css b/frontend/src/index.css index af9d241..cdf386e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -37,7 +37,7 @@ --shadow: 0 12px 36px rgba(20, 30, 50, 0.10); } -/* Type palette is defined in widgets/graph/ce-graph.css — that file +/* Type palette is defined in palette.css — that file * is the single source of truth for `--type-*` variables (sidebar * dots, label-types panel, graph node fills via data.palette * override on the daemon side). Removed the duplicate set from this @@ -838,28 +838,14 @@ a.sy-source-cite { color: var(--accent); } font-size: 11px; } -/* CE's #sidebar wrapper now lives inside .sy-side-pages. Drop CE's - * own width/border/background — our column owns the chrome. */ +/* The CE page sidebar (top) and Files|Sources (bottom) split the + * column; without CE the Files pane takes the whole column. */ .sy-side-pages { flex: 1 1 50%; min-height: 0; display: flex; flex-direction: column; } -.sy-side-pages-mount { - flex: 1 1 auto; - min-height: 0; - display: flex; - flex-direction: column; -} -.sy-side-pages-mount > #sidebar { - width: 100%; - flex: 1 1 auto; - min-height: 0; - background: transparent; - border-right: none; - transition: none; -} .sy-side-files { flex: 1 1 50%; min-height: 0; @@ -867,6 +853,10 @@ a.sy-source-cite { color: var(--accent); } display: flex; flex-direction: column; } +.sy-side-files--full { + flex: 1 1 auto; + border-top: none; +} .sy-side-bottom { flex-shrink: 0; border-top: 1px solid var(--line); @@ -2539,7 +2529,7 @@ a.sy-source-cite { color: var(--accent); } min-height: 0; } /* Bare = no padding/scroll/overflow handling (the tab widget owns it). - * Used by GraphTab and other widgets that fill their container. */ + * Used by the Graph embed and other widgets that fill their container. */ .sy-tab-content--bare { padding: 0; overflow: hidden; @@ -2547,30 +2537,6 @@ a.sy-source-cite { color: var(--accent); } flex-direction: column; min-height: 0; } -.sy-graph-host { - position: relative; - width: 100%; - height: 100%; -} -.sy-graph-progress { - position: absolute; - top: 10px; - left: 50%; - transform: translateX(-50%); - z-index: 6; - padding: 6px 14px; - border-radius: 999px; - font-size: 12px; - color: var(--text); - background: color-mix(in srgb, var(--bg-soft) 92%, transparent); - border: 1px solid var(--line); - box-shadow: 0 2px 8px color-mix(in srgb, #000 18%, transparent); - pointer-events: none; - max-width: min(520px, 90%); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} /* ── Sheet tab (Univer) ──────────────────────────────────────────── */ .sy-sheet { display: flex; @@ -3041,51 +3007,6 @@ a.sy-source-cite { color: var(--accent); } } /* ── Sketch tab (Excalidraw + drawio) ────────────────────────────── */ -/* Opening-graph replay (curation-history animation). Renders as - * an overlay above the real graph so we can crossfade out once - * the timeline finishes — the real graph sits underneath, already - * laid out. */ -.sy-curation-replay { - position: absolute; - inset: 0; - background: var(--bg); - overflow: hidden; - z-index: 5; - opacity: 1; - /* Fade duration must match the JS setTimeout in GraphTab that - * unmounts the component (1500ms). Mismatch = either pop or - * empty canvas while the timeout finishes. */ - transition: opacity 1.4s ease-out; - /* Pointer events ENABLED here (was none) so d3.zoom on the - * SVG can receive wheel + drag for manual zoom/pan during the - * animation. The fading state below disables them so a - * mid-fade click reaches the real graph beneath. */ -} -.sy-curation-replay--fading { - pointer-events: none; -} -.sy-curation-replay--fading { - opacity: 0; -} -.sy-curation-replay-svg { - width: 100%; - height: 100%; - display: block; -} -.sy-curation-replay-label { - position: absolute; - left: 50%; - bottom: 24px; - transform: translateX(-50%); - font-size: 11px; - color: var(--text-faint); - letter-spacing: 0.4px; - pointer-events: none; - user-select: none; - opacity: 0.85; -} -.sy-curation-replay--fading .sy-curation-replay-label { opacity: 0; } - /* Node/edge counter, shared between the replay overlay and the * real graph. Sits to the left of the Classic/Atlas minimap * (190px + 12px inset + 12px gap) so the two never stack. */ @@ -3129,58 +3050,6 @@ a.sy-source-cite { color: var(--accent); } color: #d29922; border-color: #d29922; } -.sy-split-bar { - position: absolute; - bottom: 18px; - left: 50%; - transform: translateX(-50%); - z-index: 7; - display: flex; - align-items: center; - gap: 10px; - padding: 8px 14px; - background: var(--bg-soft); - border: 1px solid var(--line); - border-radius: 12px; - box-shadow: 0 4px 18px rgba(0, 0, 0, 0.25); - font-size: 12px; - max-width: 92%; -} -.sy-split-bar-count { font-weight: 600; white-space: nowrap; } -.sy-split-bar-hint { - color: var(--text-faint); - font-size: 10.5px; - white-space: nowrap; - cursor: help; -} -.sy-split-bar-name { - width: 180px; - padding: 3px 8px; - font-size: 12px; - background: var(--bg-elev); - border: 1px solid var(--line); - border-radius: 6px; - color: var(--text); -} -.sy-split-bar-go { - padding: 3px 12px; - border-radius: 6px; - border: 1px solid #d29922; - background: transparent; - color: #d29922; - font-weight: 600; - cursor: pointer; -} -.sy-split-bar-go:disabled { opacity: 0.5; cursor: default; } -.sy-split-bar-cancel { - padding: 3px 10px; - border-radius: 6px; - border: 1px solid var(--line); - background: transparent; - color: var(--text-faint); - cursor: pointer; -} -.sy-split-bar-err { color: var(--err, #f38ba8); font-size: 11px; } .sy-graph-replay-btn { position: absolute; @@ -5556,10 +5425,6 @@ a.sy-source-cite { color: var(--accent); } color: var(--text); } .sy-duckdb-results tr:hover td { background: var(--bg-elev); } -.sy-graph-mount { - width: 100%; - height: 100%; -} .sy-tab-swap { position: absolute; top: 8px; @@ -5829,8 +5694,7 @@ a.sy-source-cite { color: var(--accent); } .sy-editor-view-chev:hover:not(:disabled) { background: var(--bg-hover, rgba(128, 128, 128, 0.14)); color: var(--text); } .sy-editor-view-chev:disabled { opacity: 0.3; cursor: default; } /* Typography (.sy-mdview, .sy-mdview-title, .sy-mdview-properties) - * lives in widgets/graph/ce-graph.css so it stays in lockstep with - * #modal-body. */ + * lives in palette.css next to the wiki-type palette. */ /* Placeholder tab body (replaced step-by-step as each widget lands). */ .sy-placeholder { @@ -8329,8 +8193,8 @@ a.sy-source-cite { color: var(--accent); } flex-direction: column; overflow: hidden; } -.sy-zen-browse-pages #sidebar, -.sy-zen-browse-pages .sy-side-pages-mount { +.sy-zen-browse-pages .sy-ce-sidebar-slot, +.sy-zen-browse-pages #sidebar { flex: 1 1 auto; min-height: 0; display: flex; @@ -8961,9 +8825,9 @@ a.sy-source-cite { color: var(--accent); } Power mode (graph as a center tab, no floating chrome) is untouched. */ .sy-zen-left .sy-tab-swap { top: 44px; } /* In the graph, the "↗ Editor" tab-swap shares the top-right corner - with the forked viewer's physics-settings gear (.settings-trigger, - right:14px). Shift it left of the gear so it never covers it. */ -.sy-graph-host .sy-tab-swap { right: 52px; } + with CE's physics-settings gear (.settings-trigger, right:14px). + Shift it left of the gear so it never covers it. */ +.sy-ce-atlas-embed .sy-tab-swap { right: 52px; } /* ── OWID pack browse tab ─────────────────────────────────────────── */ .sy-owid { display: flex; flex-direction: column; height: 100%; min-height: 0; @@ -9265,16 +9129,40 @@ body.sy-embed-hosting { font-size: 0.85em; } -/* Dual-mount Graph: Switchbay ↻/✂ sit top-left (prior GraphTab chrome). +/* CE's page modal sizes itself against the full viewport (it assumes + * it owns the window minus its own sidebar). Inside the Graph tab it + * would run under the rail, so fit it to the graph pane instead. */ +.sy-ce-atlas-embed-root #modal-backdrop { + left: 0 !important; +} +.sy-ce-atlas-embed-root #modal { + top: 4% !important; + bottom: 4% !important; + left: 8% !important; + width: 84% !important; +} +/* Slideshow button sits left of CE's padlock (right:44px) and close X. */ +.sy-ce-atlas-embed-root .modal-slides { + position: absolute; + top: 14px; + right: 74px; + z-index: 51; + color: var(--text-faint); +} +.sy-ce-atlas-embed-root .modal-slides:hover { color: var(--text-muted); } +.sy-ce-atlas-embed-root .modal-slides:disabled { opacity: 0.4; cursor: progress; } + +/* CE Graph: Switch Bay ↻/✂ sit top-left. Hide CE's bottom-bar copies so we don't double up. */ .sy-ce-atlas-embed-root #split-toggle, .sy-ce-atlas-embed-root #replay-toggle { display: none !important; } -/* CE dual-mount: shell-left sidebar slot (Pages|Files) */ +/* CE dual-mount: shell-left sidebar slot (Pages|Files). Shares the + * column 50/50 with Switch Bay's Files|Sources pane below it. */ .sy-side-pages--ce-embed { - flex: 1 1 auto; + flex: 1 1 50%; min-height: 0; display: flex; flex-direction: column; diff --git a/frontend/src/layout/SettingsModal.tsx b/frontend/src/layout/SettingsModal.tsx index 2b3b0e7..f72885a 100644 --- a/frontend/src/layout/SettingsModal.tsx +++ b/frontend/src/layout/SettingsModal.tsx @@ -4058,19 +4058,18 @@ function StoragePanel({ open }: { open: boolean }) {

- Proxied skill embeds (Phase 4a) + Agents: okstratr observer

- When on: shell left hosts CE Pages|Files (persistent); Graph hosts - CE atlas canvas only — one embed session, two mounts, shared{" "} - /embed/ce/data.json. Agents mounts okstratr under{" "} - /embed/okstratr. No iframes / full CE document remount. - Default off keeps WikiPane + GraphTab / AgentDashboard. + When on, the Agents tab shows okstratr's own observer (served + under /embed/okstratr). When off, it shows Switch + Bay's built-in Agents dashboard. The Graph tab always uses + Curiosity Engine's viewer and isn't affected by this switch.

- Proxied embeds:{" "} - {settings.proxied_skill_embeds ? "on (Graph→CE canvas, Agents→okstratr)" : "off (built-in)"} + okstratr observer:{" "} + {settings.proxied_skill_embeds ? "on" : "off (built-in dashboard)"}
- {proxied === null ? ( + {ce === null ? (

Loading browser…

- ) : proxied === true ? ( - // One CE page list (Pages|Files) in the shell — persists across - // Graph/Agents/Editor. Graph pane is canvas-only via CeAtlasEmbed. + ) : showWiki ? ( + // CE's page list (Pages|Files) persists across Graph/Agents/Editor; + // the Graph tab mounts the canvas from the same embed session.
- ) : ( - <> -
- setRefreshKey((k) => k + 1)} - /> -
- {error && ( -
- {error} -
- )} -
- {view === "files" ? ( - - } - /> - ) : ( - - } - /> - )} -
- + ) : null} + {error && showWiki && ( +
+ {error} +
)} +
+ {view === "files" ? ( + + } + /> + ) : ( + + } + /> + )} +