From 8234a3c23fabd9e0204d4d127cdc6d39f0874dc2 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Wed, 29 Apr 2026 17:31:41 +0200 Subject: [PATCH] docs(design): E2 design docs for 0.6.0 navigation refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds technical design docs for the navigation epic of the doc-centric pivot: - 207 — Document-centric routing (/docs, /docs/:id?mode=, /index/:store, /runs) - 208 — Doc workspace breadcrumb (Studio > > ) - 209 — Sidebar nav rework (Home / Docs / Stores / Runs / Settings) - 210 — Feature flag mode gating (deep-link redirect + flag exposure) Status: Accepted on all four. Each doc spells out the contract, alternatives considered, risks per audit dimension, and testing strategy. Backwards-compatibility is preserved throughout: legacy routes and pages keep working until E3/E4/E5 explicitly replace them. Refs #207 #208 #209 #210 --- docs/design/207-document-centric-routing.md | 216 ++++++++++++++++++ docs/design/208-doc-workspace-breadcrumb.md | 180 +++++++++++++++ docs/design/209-nav-rework.md | 175 +++++++++++++++ docs/design/210-feature-flag-mode-gating.md | 235 ++++++++++++++++++++ 4 files changed, 806 insertions(+) create mode 100644 docs/design/207-document-centric-routing.md create mode 100644 docs/design/208-doc-workspace-breadcrumb.md create mode 100644 docs/design/209-nav-rework.md create mode 100644 docs/design/210-feature-flag-mode-gating.md diff --git a/docs/design/207-document-centric-routing.md b/docs/design/207-document-centric-routing.md new file mode 100644 index 0000000..24cb5db --- /dev/null +++ b/docs/design/207-document-centric-routing.md @@ -0,0 +1,216 @@ +# Design: Document-centric routing + +- **Issue:** #207 +- **Title on issue:** [FEATURE] Document-centric routing (/docs, /docs/:id?mode=, /index/:store, /runs) +- **Author:** Pier-Jean Malandrino +- **Date:** 2026-04-29 +- **Status:** Accepted +- **Target milestone:** 0.6.0 — Doc-centric ingest +- **Impacted layers:** frontend: app/router · pages · shared +- **Audit dimensions likely touched:** Clean Code · Tests · Documentation +- **ADR spawned?:** no + +--- + +## 1. Problem + +The current Vue Router (`frontend/src/app/router/index.ts`) is **analysis-centric**: routes are `/studio`, `/documents`, `/search`, `/reasoning`, `/history`. The selected document is held in a Pinia store, not in the URL — so two engineers cannot share a link to "the chunks editor for doc X". This breaks the killer flow ("paste this URL → fix the chunks → re-ingest") that 0.6.0 promises. + +The 0.6.0 sitemap puts the document at the centre of every URL: `/docs`, `/docs/:id?mode=ask|inspect|chunks`, plus `/index/:store` for stores and `/runs` for the run history. Mode is a query param so a doc URL stays stable across mode switches. + +This issue ships the routing skeleton. The actual page contents come in E3 (`/docs` library — #211), E4 (workspace shell — #216), E5 (chunks editor — #218 onward). + +## 2. Goals + +- [ ] Add `/docs`, `/docs/new`, `/docs/:id`, `/index`, `/index/:store`, `/index/:store/query`, `/runs`, `/runs/:id` routes. +- [ ] On `/docs/:id`, `?mode=ask|inspect|chunks` is parsed; default = `ask`. +- [ ] Each new route renders a placeholder page (clear "Coming in 0.6.0" message) until E3/E4/E5 implement them. +- [ ] Legacy routes (`/studio`, `/documents`, `/history`, `/search`, `/reasoning`) keep working — no breaking redirect in this issue. +- [ ] Smoke test: each new route renders without error. + +## 3. Non-goals + +- Building the actual pages — that is E3 / E4 / E5. +- Migrating users from old routes — kept functional in parallel; deprecation comes when the new pages are ready. +- Server-side route enforcement — backend exposes its API on `/api/*`; the routing here is client-side only. +- A site map / generated nav — the sidebar nav rework is **#209**. +- Feature-flag-aware redirection (e.g. mode disabled → redirect to default) — that is **#210**. + +## 4. Context & constraints + +### Existing code surface + +- `frontend/src/app/router/index.ts` — current Vue Router setup (history mode, lazy-loaded pages). +- `frontend/src/pages/` — existing pages (`HomePage.vue`, `StudioPage.vue`, `DocumentsPage.vue`, `HistoryPage.vue`, `SearchPage.vue`, `ReasoningPage.vue`, `SettingsPage.vue`). +- `frontend/src/app/App.vue` — shell with topbar + sidebar + ``. +- `frontend/src/features/feature-flags/` — flag store and `useFeatureFlag` composable. + +### Hard constraints + +- TypeScript strict — every new route needs a typed `name`. +- No regression on existing routes — old URLs keep returning their current pages until #211 / #216 explicitly replace them. +- Lazy loading is preserved — every new page goes through `() => import(...)`. + +### Deployment modes + +Same routing for both `latest-local` and `latest-remote`. No HF Space-specific concern. + +## 5. Proposed design + +### 5.1 Router additions + +Append to `frontend/src/app/router/index.ts`: + +```ts +{ path: '/docs', name: 'docs-library', + component: () => import('@/pages/DocsLibraryPage.vue') }, +{ path: '/docs/new', name: 'docs-new', + component: () => import('@/pages/DocsNewPage.vue') }, +{ path: '/docs/:id', name: 'doc-workspace', + component: () => import('@/pages/DocWorkspacePage.vue'), + props: route => ({ id: route.params.id, mode: parseMode(route.query.mode) }) }, +{ path: '/index', name: 'stores-list', + component: () => import('@/pages/StoresListPage.vue') }, +{ path: '/index/:store', name: 'store-detail', + component: () => import('@/pages/StoreDetailPage.vue'), + props: true }, +{ path: '/index/:store/query', name: 'store-query', + component: () => import('@/pages/StoreQueryPage.vue'), + props: true }, +{ path: '/runs', name: 'runs', + component: () => import('@/pages/RunsPage.vue') }, +{ path: '/runs/:id', name: 'run-detail', + component: () => import('@/pages/RunDetailPage.vue'), + props: true }, +``` + +### 5.2 Mode parser + +A pure helper in `frontend/src/shared/routing/modes.ts`: + +```ts +export type DocMode = 'ask' | 'inspect' | 'chunks' +const DEFAULT_MODE: DocMode = 'ask' + +export function parseMode(raw: unknown): DocMode { + return raw === 'inspect' || raw === 'chunks' ? raw : DEFAULT_MODE +} +``` + +This is intentionally tiny and testable. #210 will extend it with feature-flag-aware redirection. + +### 5.3 Placeholder pages + +Each new page is ~30 lines of Vue: a centered card with the page title, a "Coming in 0.6.0" tagline, and a link back to home. They use the existing `useI18n()` strings under a new `comingSoon.*` namespace. + +### 5.4 Router types + +`frontend/src/shared/routing/names.ts` exports a typed union of route names so callers do `router.push({ name: ROUTES.DOC_WORKSPACE, params: { id } })` instead of stringly-typed names. + +```ts +export const ROUTES = { + HOME: 'home', + DOCS_LIBRARY: 'docs-library', + DOCS_NEW: 'docs-new', + DOC_WORKSPACE: 'doc-workspace', + STORES_LIST: 'stores-list', + STORE_DETAIL: 'store-detail', + STORE_QUERY: 'store-query', + RUNS: 'runs', + RUN_DETAIL: 'run-detail', + // ...legacy names kept as-is +} as const +export type RouteName = (typeof ROUTES)[keyof typeof ROUTES] +``` + +### 5.5 i18n + +New keys under `comingSoon.*` in `frontend/src/shared/i18n.ts` (fr + en): + +- `comingSoon.title` +- `comingSoon.subtitle.docsLibrary` +- `comingSoon.subtitle.docsNew` +- `comingSoon.subtitle.docWorkspace` +- `comingSoon.subtitle.stores` +- `comingSoon.subtitle.storeDetail` +- `comingSoon.subtitle.storeQuery` +- `comingSoon.subtitle.runs` +- `comingSoon.subtitle.runDetail` +- `comingSoon.backHome` + +## 6. Alternatives considered + +### Alternative A — Replace existing routes immediately + +- **Summary:** Make `/studio` and `/documents` redirect to the new routes in this issue. +- **Why not:** The new pages do not exist yet. Redirecting now means the user lands on a "Coming soon" page where they used to have a working app. + +### Alternative B — Hash-mode routing + +- **Summary:** Switch to `createWebHashHistory` for the new doc-centric routes. +- **Why not:** History mode is the existing convention and SPA deep-linking still works behind Nginx (already configured). No reason to mix modes. + +## 7. API & data contract + +No backend changes. The routes are entirely client-side. No env vars. + +### Breaking changes + +None. Additive. + +## 8. Risks & mitigations + +| Risk | Audit dimension | Likelihood | Impact | How we notice | Mitigation / rollback | +|------|-----------------|------------|--------|---------------|------------------------| +| Placeholder pages confuse users who land on them via shared links | Documentation | Medium | Low | Support tickets | Clear "Coming soon" copy + back-home link | +| Route name collisions with legacy ones | Clean Code | Low | Low | TS error / runtime warning | Use a `ROUTES` constant; legacy names kept | +| Broken nav from sidebar to legacy routes after rename | Decoupling | Low | Medium | Smoke test catches | The sidebar update is explicitly **#209**, not this issue | + +## 9. Testing strategy + +### Frontend — Vitest + +- `app/router/router.test.ts` — every new route resolves to its component (smoke). +- `shared/routing/modes.test.ts` — `parseMode` returns `ask` for `undefined` / `null` / unknown values; respects `inspect` / `chunks`. + +### E2E — Karate UI + +Out of scope for this issue (placeholder pages only). E2E coverage lands with #211 (library) and #216 (workspace). + +### Manual QA + +1. Visit each new URL in the browser → "Coming soon" shell renders without 404. +2. Visit `/docs/abc?mode=chunks` → page receives `mode === 'chunks'`. +3. Visit `/docs/abc?mode=garbage` → page receives `mode === 'ask'` (default). +4. Old routes (`/studio`, `/documents`) still load their existing pages. + +## 10. Rollout & observability + +### Release branch + +`release/0.6.0`. + +### Feature flag + +None. The placeholder pages are visible to anyone; they explain themselves. + +### Observability + +No new logs. Existing router-error handling unchanged. + +### Rollback plan + +Revert the router and pages — old setup is untouched. + +## 11. Open questions + +- Should `/docs/:id` 404 if the doc id is unknown, or render the workspace shell with an error state? **Decision for 0.6.0:** the workspace handles the "doc not found" case in #216; this issue ships the placeholder which always renders. + +## 12. References + +- **Issue:** https://github.com/scub-france/Docling-Studio/issues/207 +- **Related issues:** #208 (breadcrumb), #209 (nav), #210 (FF mode gating), #211 (library page), #216 (workspace), #218+ (modes) +- **ADRs:** none planned +- **Project docs:** + - Architecture: `docs/architecture.md` + - Frontend conventions: `frontend/CLAUDE.md` diff --git a/docs/design/208-doc-workspace-breadcrumb.md b/docs/design/208-doc-workspace-breadcrumb.md new file mode 100644 index 0000000..77940a1 --- /dev/null +++ b/docs/design/208-doc-workspace-breadcrumb.md @@ -0,0 +1,180 @@ +# Design: Doc workspace breadcrumb + +- **Issue:** #208 +- **Title on issue:** [ENHANCEMENT] Refactor breadcrumb to Studio › +- **Author:** Pier-Jean Malandrino +- **Date:** 2026-04-29 +- **Status:** Accepted +- **Target milestone:** 0.6.0 — Doc-centric ingest +- **Impacted layers:** frontend: shared/ui · app +- **Audit dimensions likely touched:** Clean Code · Tests · Documentation +- **ADR spawned?:** no + +--- + +## 1. Problem + +There is no breadcrumb component today. The user lands on `/docs/:id?mode=chunks` and has no orientation: which doc, which mode, how to step back. With the new doc-centric URLs introduced in #207, the user navigates between modes on the same doc — a breadcrumb anchors them in the IA. + +The shape `Studio › ` is the agreed pattern: each segment is a link except the last, the doc title is truncated with ellipsis (full title on hover), and the mode segment updates as the user switches tabs without a page reload. + +## 2. Goals + +- [ ] New `` component renders three segments on doc workspace pages: `Studio › `. +- [ ] Each non-last segment is clickable: `Studio` → `/`, `` → `/docs/:id` (default mode). +- [ ] Doc title truncates beyond ~40 chars with ellipsis; full title shown via `title` attribute on hover. +- [ ] Mode segment reflects current `?mode=` and updates without remount. +- [ ] On non-doc routes (`/`, `/runs`, `/index`), the breadcrumb is empty / hidden — no fake breadcrumbs invented. +- [ ] Component is data-driven: takes a `Crumb[]` prop so future routes can supply their own. + +## 3. Non-goals + +- Auto-deriving the breadcrumb from the route — for 0.6.0 the doc workspace page passes its crumbs explicitly. Auto-derivation is a follow-up if more pages need it. +- Mobile-specific breadcrumb collapsing — a single-line ellipsis is enough for the layout. +- Breadcrumbs on store / run pages — those land in 0.7.0 with their own page work. + +## 4. Context & constraints + +### Existing code surface + +- `frontend/src/app/App.vue` — the shell. The breadcrumb slot will live in the topbar (between the logo and the right-side actions). +- `frontend/src/pages/DocWorkspacePage.vue` (created by #207, placeholder). Will be the first consumer. +- `frontend/src/shared/ui/` — home for shared UI primitives. The new component lives here. +- `frontend/src/shared/routing/names.ts` (created by #207) — typed route names. +- `frontend/src/shared/i18n.ts` — strings. + +### Hard constraints + +- TypeScript strict — `Crumb` is a discriminated union (link vs leaf). +- Accessibility — `