Repository navigation
Unified /sql routing for workspace and dashboard surfaces with live view/edit modes #407
Description
Goal
Serve the Workbench and its single workspace dashboard from the same /sql route, selected entirely by query parameters.
Canonical URLs:
/sql?ws=clickhouse_operations
/sql?ws=clickhouse_operations&surface=dashboard
/sql?ws=clickhouse_operations&surface=dashboard&mode=view
This issue depends on #406 for keyed multi-workspace persistence.
Product decisions
wsis the immutable human-readable workspace key.- Each workspace owns zero or one dashboard.
surfaceselects the application surface:- absent or
workspace-> Workbench/editor; dashboard-> Dashboard.
- absent or
- Dashboard edit mode is the default.
mode=viewselects live read-only presentation mode.mode=editis accepted but canonicalized away.- Dashboard view mode reads the same live workspace dashboard as edit mode.
- Durable detached dashboard snapshots are removed from the product model.
- Workbench <-> Dashboard navigation happens in the same browser tab by default.
- View <-> Edit changes presentation mode only and should not create noisy browser-history entries.
- There is no migration requirement for old development URLs or detached-view data.
Route contract
Introduce one pure parser/normalizer for /sql state. Equivalent shape:
type SqlRoute =
| {
surface: 'workspace';
workspaceKey: string | null;
}
| {
surface: 'dashboard';
workspaceKey: string | null;
mode: 'edit' | 'view';
};Parsing rules:
- Read
ws,surface, andmodefrom the current URL. - Missing
surfacemeansworkspace. surface=dashboardselects Dashboard.- Unknown
surfacevalues normalize toworkspace. mode=viewis meaningful only on Dashboard.- Missing,
edit, or unknown Dashboard mode means edit. - Remove irrelevant/invalid parameters when canonicalizing.
- Preserve unrelated application parameters that remain valid, especially OAuth callback parameters until the existing callback flow consumes them.
Canonicalization examples:
/sql?ws=x&surface=workspace -> /sql?ws=x
/sql?ws=x&surface=dashboard&mode=edit -> /sql?ws=x&surface=dashboard
/sql?ws=x&mode=view -> /sql?ws=x
/sql?ws=x&surface=unknown -> /sql?ws=x
Startup workspace resolution
After authentication/config setup:
Explicit workspace
/sql?ws=clickhouse_operations
- Resolve exactly that key through the multi-workspace repository.
- On success, set it as last-used and render the requested surface.
- On failure, render a dedicated Workspace not found state.
- Never silently substitute the last-used or another workspace.
No explicit workspace
/sql
- Resolve the last-used workspace key.
- If valid, canonicalize the URL to include it.
- If no workspace exists, create a default workspace and canonicalize to
/sql?ws=defaultor the actual collision-free generated key. - If the stored last-used key is stale, choose a deterministic existing workspace (alphabetically by display name/key is acceptable), update last-used, and canonicalize.
Surface rendering
Do not bootstrap two independent applications based on pathname.
The application should:
- resolve one workspace;
- parse the requested surface;
- construct/render only the required surface controller;
- switch surfaces without a full document reload where practical.
Workspace surface
- Render normal editor/workbench UI.
- Dashboard mode parameters are absent.
Dashboard edit surface
- Load
workspace.dashboardfrom the same workspace aggregate. - Register authoring capabilities: reorder, resize/layout changes, tile mutations, and persistence.
- If
dashboard === null, show an editable empty state with Create dashboard. - Merely visiting the route must not create a dashboard.
Dashboard view surface
- Render the same current dashboard document and execute the same dashboard queries.
- Do not expose or register mutation capabilities.
- No drag/reorder/resize/delete handlers.
- No workspace commit path reachable from view-mode controls or shortcuts.
- If
dashboard === null, show This workspace has no dashboard and execute nothing. - View mode is presentation-only, not authorization. The same local user may switch back to edit.
Navigation behavior
Workspace -> Dashboard
Use same-tab navigation and history.pushState():
/sql?ws=x
-> /sql?ws=x&surface=dashboard
This keeps browser Back useful and avoids default concurrent editing between Workbench and Dashboard tabs.
Dashboard -> Workspace
Use history.pushState() or browser Back when appropriate:
/sql?ws=x&surface=dashboard
-> /sql?ws=x
View <-> Edit
Use history.replaceState():
/sql?ws=x&surface=dashboard
<-> /sql?ws=x&surface=dashboard&mode=view
The Back button should not alternate repeatedly between view and edit presentation states.
Switching workspace
Preserve the current surface and mode when the user selects another workspace:
/sql?ws=a&surface=dashboard&mode=view
-> /sql?ws=b&surface=dashboard&mode=view
Dirty-draft confirmation is owned by the workspace-management UI issue.
Remove old Dashboard route assumptions
Retire production dependence on pathname suffix routing:
/sql/dashboardstandalone route;isDashboardRoute(pathname)as the application surface discriminator;configBase()logic that strips/dashboard;- dashboard-specific OAuth redirect URI behavior;
- code paths that bootstrap Dashboard separately solely because of pathname.
The OAuth callback must still preserve ws, surface, and mode while stripping only OAuth callback parameters.
Remove detached snapshot/view infrastructure
The product no longer needs durable detached read-only snapshots.
Remove or retire:
- detached dashboard view store;
- one-time dashboard state handoff token/store;
- session-bundle dashboard open source;
stdashboard transport parameter;- detached workspace materialization and retention policy;
- Open for viewing… snapshot action;
- mode discrimination by which store contains a workspace ID.
Do not remove the existing credential handoff mechanism merely because state handoff is removed if credential handoff is still used elsewhere. Re-evaluate its necessity after same-tab navigation becomes the default.
Dashboard header controls
On Dashboard surface show:
[Workspace | Dashboard] [View | Edit]
Exact visual form may follow existing header primitives.
- Surface switch updates
surface. - Mode switch is visible only on Dashboard.
- View mode is clearly indicated.
- Edit controls are not rendered in view mode.
Missing/deleted workspace behavior
If another tab deletes the workspace currently addressed by the URL:
- the next load/refresh/mutation failure resolves to Workspace not found;
- do not silently open another workspace;
- execute no dashboard queries after the workspace is known missing.
Real-time cross-tab invalidation is a separate follow-up; this issue only requires correct behavior when the missing state is observed.
Tests
Add pure route tests and integration tests covering at least:
- Parse and canonicalize all three canonical URL forms.
/sqlresolves last-used workspace and rewrites URL./sqlcreates a default workspace when none exist.- Explicit missing
wsshows not-found without fallback. - Workspace surface renders for absent
surface. - Dashboard edit is default for
surface=dashboard. mode=viewrenders read-only viewer with no mutation handlers.mode=editcanonicalizes away.- Missing dashboard differs correctly in edit and view modes.
- Workspace/Dashboard same-tab navigation uses expected history behavior.
- View/Edit uses
replaceState. - OAuth callback cleanup retains
ws,surface, andmode. - Old detached stores/tokens are no longer required by the production route.
- Dashboard queries are not executed for missing workspace or missing dashboard in view mode.
Acceptance criteria
- Workbench and Dashboard are both served from
/sql. - The canonical Dashboard edit URL is
/sql?ws=<key>&surface=dashboard. - The canonical live read-only URL is
/sql?ws=<key>&surface=dashboard&mode=view. - Dashboard view and edit use the same canonical workspace/dashboard data.
- Detached dashboard snapshots and one-time state handoff are removed.
- Default navigation between Workbench and Dashboard stays in the same tab.
- Explicit unknown workspace keys never fall back silently.
Non-goals
- Security/authorization enforcement for view mode.
- Multiple dashboards per workspace.
- Frozen historical dashboard snapshots.
- Cross-device bookmark portability when data is only local.
- Cross-tab stale-write prevention; tracked separately.
- Migration of old development URLs or IndexedDB records.
Activity
feat: unify workspace and dashboard routing (#407) (#411)