# PDF Editor — UI Redesign: Gap Analysis & Implementation Plan **Status:** Draft for review **Goal:** Replace the current "baby-like" UI with a sophisticated, comprehensive, professional PDF-editor interface that surfaces the full capability already built into the engine + gateway — **without breaking or losing any existing functionality or workflow.** **Naming:** Drop "EditQube"/"DocQube" branding. Use plain **"PDF Editor"** for now. --- ## 1. What we have today (baseline inventory) ### 1.1 Architecture (this is solid — we keep it) ``` React 19 + Vite 8 + Tailwind v4 App.tsx ............ central state owner (zoom, tool, annotations, search, docs) components/ ........ Toolbar, LeftSidebar (tool rail), Sidebar (right tabs), SearchBar, Thumbnail, WasmInspector viewer/ ............ PDFViewer + 6 stacked layers (Canvas, Annotation, Selection, Redaction, Overlay, SearchOverlay) lib/ ............... gatewayService (REST client), coordinateMapping, wasmLoader | v REST FastAPI gateway (app/routers: documents, edits, render, info, health) | v pybind11 C++ engine (PDFium + Skia + FreeType + HarfBuzz) ``` The **layered-canvas viewer** and the **typed edit-operation envelope** (`{version, operations[]}`) are genuinely good foundations. The redesign is a UI/UX re-skin + feature-surfacing effort, **not** an architecture rewrite. ### 1.2 Existing tools (left rail) — 8 tools Select · Search · Pan · Highlight · Draw (ink) · Comment (sticky) · Signature · Redact ### 1.3 Existing right-sidebar tabs — 4 Files · Pages (thumbnails + delete/reorder) · Notes (annotation list) · Settings (empty stub) ### 1.4 Existing toolbar controls Logo · Gateway-health % · device-preview icons (non-functional) · undo/redo (non-functional) · rotate · zoom ± · Comments · Share (stub) · Export · Save Edits ### 1.5 Workflows that currently work (MUST be preserved) 1. Upload PDF → list → select → view (with mock fallback when gateway is down) 2. Virtualized scroll viewer with per-page render + zoom 3. Highlight via drag-select → persists via `applyEdits` (creates new doc id) 4. Freehand ink draw → persists 5. Sticky-note comment (click → popup → save) → persists 6. Redact area (drag → confirm → permanent removal, full save) 7. Page rotate / delete / reorder → persists 8. Live full-text search (debounced) with prev/next + on-page highlight overlay 9. Export / download edited PDF 10. WASM inspector (debug panel) — client-side engine probe --- ## 2. The core problems (why it feels "baby-like") | # | Problem | Evidence | |---|---------|----------| | P1 | **Two fighting themes.** `index.css` defines a full *dark* "enterprise" component system (`rgba(2,6,23,…)`, white-on-dark) but `App.tsx`/components render a *light* theme with ad-hoc inline Tailwind. Half the CSS classes are unused or visually contradict what's on screen. | `index.css` `.toolbar`, `.sidebar`, `.doc-card` vs. `App.tsx` `bg-gray-50`, `text-gray-900` | | P2 | **No design system.** Spacing, radii, colors, font-weights are chosen per-component. Multiple blues (`#3b82f6` called "indigo"), random `rounded-full` pills, heavy drop-shadows. | Toolbar uses `shadow-lg`; CSS uses `0 4px 20px rgba(0,0,0,.25)` | | P3 | **Gimmicky chrome.** "Nice work! You completed 2/6 steps!" gamification toast, oversized circular page badge, decorative non-functional device-preview + undo/redo icons. | `App.tsx:470`, Toolbar | | P4 | **Thin feature surface.** The engine/gateway expose ~20 endpoints and 10 edit types; the UI wires maybe half. Big capabilities (metadata, fonts, structured text model, free-text, image/stamp, real text selection) are invisible. | §3 gap table | | P5 | **Stubs masquerading as features.** Text selection returns *mock* text (`SelectionLayer.tsx:52`); Signature tool only shows a banner; device-preview & undo/redo do nothing. | `SelectionLayer`, `OverlayLayer`, `Toolbar` | | P6 | **No empty/error/loading polish.** Single spinner + "No active document." No drag-drop upload zone, no toasts system, no command surface, no keyboard shortcuts. | `App.tsx` | | P7 | **Branding leftovers.** "EditQube" logo, "DocQube PDF Engine" baked into mock-page SVG. | `Toolbar.tsx:40`, `gatewayService.ts:361,415` | --- ## 3. Gap analysis — engine readiness vs. UI exposure > **Reworked against the master engineering roadmap.** Every UI feature is now gated on whether the *engine* actually supports it. We must build the UI to comprehensively surface what's **Done**, gracefully stub what's **In-progress / Not-started**, and never ship UI ahead of the engine. **Engine status** (from roadmap): 🟢 Done · 🟠 In-progress · 🔴 Not-started/None **UI status:** ✅ wired · 🟡 partial/stub · ❌ absent | Capability | Endpoint / edit type | Engine | UI | Action | |---|---|:--:|:--:|---| | Render / zoom / pan / nav / thumbnails | `GET …/render` | 🟢 | ✅ | Keep; re-skin | | Full-text search w/ rects | `GET …/search` | 🟢 | ✅ | Keep; add results-list panel | | **Text extraction + glyph bounds** | `GET …/pages/{i}/text`, `…/model` | 🟢 | 🟡 **mocked** | ⭐⭐ **Wire real selection + copy** ([SelectionLayer.tsx:53](frontend/src/viewer/SelectionLayer.tsx#L53) returns mock; clipboard never wired) | | Structured model (para→line→run→glyph) | `GET …/pages/{i}/model` | 🟢 | ❌ (logged only) | Use for line-box selection now; reflow is future | | Coordinate transforms (page↔device) | `GET …/transform/*` | 🟢 | ❌ (client math) | Use for pixel-accurate placement | | Glyph-width measure | `GET …/glyph-width` | 🟢 | ❌ | Use for free-text/overlay layout | | Document metadata (read) | `GET …/metadata` | 🟢 | ❌ | ⭐ **Properties panel (read-only)** | | Fonts inventory (embedded/subset/substituted…) | `GET …/fonts` | 🟢 | ❌ (only WASM debug) | ⭐ **Fonts inspector** (differentiator) | | **Highlight** | edit `highlight` | 🟢 | ✅ | Add color/opacity picker | | **Freehand ink** | edit `freehand` | 🟢 | ✅ | Add color/thickness/eraser | | **Comment / sticky (create)** | edit `comment` | 🟢 | ✅ | Keep; create-only (see edit/delete below) | | **Free-text box** | edit `free_text` | 🟢 | ❌ | ⭐ **Add "Text box" tool** | | **Text overlay / stamp** | edit `text_overlay` | 🟢 | ❌ | ⭐ **Add stamp / text-overlay tool** | | **Image overlay** | edit `image_overlay` | 🟢 | ❌ | ⭐⭐ **Visual Signature (draw/type/upload) + image stamp** | | **Redaction** | edit `redaction` | 🟠 (in progress) | ✅ | Keep; add multi-area when engine lands | | Page rotate / delete / reorder | edits `page_*` | 🟢 | ✅ | Keep; add multi-select | | Export / download (full save) | `GET …/export` | 🟠 (in progress) | ✅ | Keep; add print/flatten when ready | | Annotation **reader** (existing annots) | `GET …/annotations` | 🟠 (in progress) | ✅ list | Read-only list; refine when reader lands | | **Edit / delete existing annotation** | — *(no edit op exists)* | 🔴 | ❌ | 🔧 **Needs new backend op**; UI shows create-only until then | | Underline / strikeout / squiggly markup | decoration renderer | 🔴 (Phase 3) | 🟡 render-only | Defer real markup; keep FE strikeout as-is | | Glyph-accurate hit-testing (spatial index) | hit-test module | 🔴 (Phase 3) | ❌ | Approximate (line-box) selection only for v1 | | **Edit existing text / replace / reflow** | content-stream editor | 🔴 (Phase 3) | ❌ | ❗ **Not a v1 UI feature** — set expectations | | Form fields (view / fill) | AcroForm | 🔴 (P2 view + P3 fill) | ❌ | Defer; stub "Forms" as coming-soon | | Bookmarks / Outline | — *(no API anywhere)* | 🔴 | ❌ | 🔧 **Needs new engine+gateway**; stub Outline tab | | Encrypted / password PDFs | encryption | 🔴 (Phase 3) | ❌ | Defer password prompt until engine supports | | **Digital (cryptographic) signature** | cert/PKI | 🔴 (Phase 4, out of v1 scope) | ❌ | Do **visual** sig only; label as not certified | | WASM in-browser render + worker | wasm path | 🟠 (in progress) | 🟡 (inspector) | Keep behind dev flag | ### 3.1 What this rework changes vs. the first draft - ⭐⭐ **Real text selection/copy is the #1 win and is genuinely ready** — the engine extracts glyphs with bounds; the live viewer just never calls it. This is *wiring*, not new engine work. (Note: glyph-perfect hit-testing is Phase 3, so v1 selection is line-box-accurate, not per-glyph.) - ✅ **Signature stays a flagship — but as a *visual* signature** (image_overlay, which is Done), explicitly **not** a cryptographic/certified signature (Phase 4, out of v1 scope). UI copy must not imply legal e-signature. - 🔧 **Three features I'd wrongly treated as "easy UI wins" are blocked on backend:** (1) edit/delete of existing annotations (no edit op), (2) Outline/Bookmarks (no API at all), (3) password/encrypted PDFs (Phase 3). These become either small backend tickets or graceful stubs — **not** silent dead buttons. - ❗ **"Edit existing text" must be framed as future.** Users hear "PDF editor" and expect to retype existing paragraphs — that's the Phase 3 content-stream editor, **Not-started**. The UI should make the v1 capability set (annotate, overlay, redact, organize pages, fill-via-overlay) clear so it doesn't feel broken. ### 3.2 Small backend tickets that unlock high-value UI (flag to engine devs) | Ticket | Unlocks | Size | |---|---|---| | `delete_annotation` / `update_annotation` edit op | Editable/deletable comments & markup in the inspector | Small | | `GET …/outline` (PDFium `FPDFBookmark_*`) | Outline/Bookmarks navigation tab | Small | | Surface `password` flow + encryption (Phase 3) | Open protected PDFs | Medium (already roadmapped) | --- ## 4. Target experience (the redesign) ### 4.1 Layout model — professional editor shell ``` ┌────────────────────────────────────────────────────────────────────┐ │ TOP BAR: [≡ File ▾] PDF Editor · doc name · ⟲⟳ undo/redo │ │ · save-state ("All changes saved" / "Saving…") │ │ · zoom · page X/Y · [Share] [Export ▾] │ ├──┬─────────────────────────────────────────────────────┬───────────┤ │ │ CONTEXTUAL TOOL STRIP (changes with active tool): │ │ │T │ e.g. Highlight → color swatches, opacity │ RIGHT │ │O │ Draw → color, thickness, eraser │ PANEL │ │O ├───────────────────────────────────────────────────────┤ (tabbed) │ │L │ │ Pages │ │ │ PAGE CANVAS (scroll / virtualized) │ Outline │ │R │ + layered overlays (unchanged) │ Comments │ │A │ │ Search │ │I │ │ Props │ │L │ │ Fonts │ └──┴───────────────────────────────────────────────────────┴───────────┘ ``` Key moves vs. today: - **Contextual tool strip** under the top bar that swaps controls per active tool (this is what makes it feel like Acrobat/Figma rather than a toy). Replaces hardcoded colors/thicknesses with real pickers — closes several gaps at once. - **Tool rail** stays on the left (familiar), but redrawn on the new design system with consistent icons + tooltips + keyboard shortcut hints. - **Right panel** becomes a real tabbed inspector. Tabs are gated on engine readiness: - **Ready now (engine Done):** Pages, Comments/Notes (read + create), Search results, **Properties** (metadata, read-only), **Fonts**. - **Stubbed (engine Not-started):** Outline/Bookmarks and Forms render as a clean "coming soon" empty-state, *not* a broken tab — they light up when their backend ticket lands. - **Command-driven top bar**: real undo/redo (operation history), explicit save-state indicator, Export dropdown (Download / Print-when-ready / Flatten-when-ready). - Remove gamification toast, decorative device-preview icons, oversized page badge. ### 4.2 Design system (single source of truth) Define tokens once (CSS variables + a Tailwind theme) and delete the contradictory legacy classes. - **Theme:** **light, neutral, low-chroma** workspace (Acrobat/Foxit-like) — locked (§8). Tokens structured so dark mode is a later toggle. - **Color:** one primary accent + neutral grays + semantic (success/warn/error). Kill the "indigo that's actually blue" ambiguity. - **Type:** keep Outfit (UI) + JetBrains Mono (debug/metadata). Define a type scale (xs–2xl) and stop per-component font-weights. - **Spacing/radii/shadow:** 4px grid; 2 radii (control / card); 2 elevations. No more `0 4px 20px rgba(0,0,0,.25)` on a light UI. - **Primitives:** small set of reusable components — `Button`, `IconButton`, `Tooltip`, `Panel`, `Tab`, `Toolbar`, `Popover`, `ColorPicker`, `Slider`, `Toast`, `Modal`, `EmptyState`, `Spinner`. Everything else composes these. ### 4.3 Interaction upgrades - Keyboard shortcuts (V select, H highlight, etc.; ⌘/Ctrl+Z undo, ⌘F search). - Drag-and-drop file upload onto the canvas with a real drop zone. - Toast/notification system (replace the gamification + scattered `alert()`/`confirm()` calls with a consistent confirm modal + toasts). - Proper unsaved-changes tracking and "Save"/"Saved" affordance. --- ## 5. Implementation plan (phased, non-breaking) Principle: **re-skin behind stable behavior first, surface only engine-Done features, stub the rest gracefully.** Each phase compiles and ships; no phase removes a working workflow until its replacement is proven; **no phase ships UI for an engine feature that isn't Done.** **v1 capability framing (set in the UI so it doesn't feel broken):** PDF Editor v1 = *annotate (highlight/ink/comment), overlay (text box / stamp / visual signature), redact, organize pages (rotate/delete/reorder), search, select & copy text, inspect (properties/fonts), export.* It does **not** yet edit existing text, fill forms, or apply certified digital signatures — those are engine Phase 3/4. ### Phase 0 — Foundation & rename (no behavior change) — *small* - Establish design tokens: rewrite `index.css` into a coherent token layer + Tailwind theme; remove dead/contradictory classes. - Build the primitive component library (§4.2) as empty-but-styled shells. - Rename: "EditQube"/"DocQube" → "PDF Editor" (`Toolbar.tsx:40`, `gatewayService.ts:361,415`, `index.html` title, favicon/logo). Remove gamification toast (`App.tsx:470`) and decorative non-functional icons (device-preview). - **Exit criteria:** app looks cleaner, every existing workflow still works, zero functional regressions. ### Phase 1 — App shell & top bar — *medium* - New top bar: File menu, doc title, **real undo/redo** (operation-history stack in `App.tsx` over the existing `applyEdits` model — each edit returns a `newDocumentId`, so keep a stack of doc ids for instant undo/redo), save-state indicator, zoom, page nav, Export dropdown. - **Exit criteria:** every top-bar control is functional or removed; undo/redo works for highlight/ink/comment/redact/page ops. ### Phase 2 — Tool rail + contextual tool strip — *medium* - Redraw the tool rail on the new system with tooltips + shortcuts. - Add the contextual strip; move color/opacity (highlight) and color/thickness/eraser (draw) into real pickers — closes the hardcoded-color gaps (all engine-Done). - **Exit criteria:** highlight + draw fully parameterized; tool switching is keyboard-driven. ### Phase 3 — Right inspector panel — *medium* - Rebuild right sidebar as tabbed inspector. **Build the engine-Done tabs for real; stub the rest:** - **Pages** — improved thumbnails + multi-select (Done). - **Search** — results list + on-page sync (Done). - **Properties** — `/metadata`, read-only (Done). - **Fonts** — `/fonts` inventory, the differentiator (Done). - **Comments/Notes** — list + **create** only (annotation reader in-progress; **edit/delete deferred** until the `delete/update_annotation` op exists — show a disabled affordance, not a dead button). - **Outline** + **Forms** — clean "coming soon" empty-states (no engine API yet). - **Exit criteria:** metadata + fonts + search-list visible; comments listed; stubbed tabs render an intentional empty-state. ### Phase 4 — Real text layer (replace the mock) — *medium-large* ⭐⭐ - Replace `SelectionLayer`'s mock ([SelectionLayer.tsx:53](frontend/src/viewer/SelectionLayer.tsx#L53)) with real glyph data from `/pages/{i}/text` / `/model`: selectable text, **copy-to-clipboard** (currently never wired), highlight quad-points snapped to text. - v1 selection is **line-box accurate** (uses returned glyph bboxes); per-glyph hit-testing waits on the engine Phase 3 spatial index. - **Exit criteria:** user selects and copies real text; highlights align to text runs. ### Phase 5 — New overlay tools: Visual Signature, Text box, Stamp — *medium-large* ⭐ - **Visual Signature** via `image_overlay` (Done): draw-pad / type / upload → place on page. Replaces the banner-only stub. **Label clearly as a visual signature, not a certified/digital signature.** - **Text box** via `free_text` (Done); **Stamp / text overlay** via `text_overlay` / `image_overlay` (Done). - **Exit criteria:** signature + text box + stamp produce real edits and survive export. ### Phase 6 — Polish & power features — *ongoing* - Drag-drop upload, toast system, confirm modals (replace scattered `alert`/`confirm`), keyboard-shortcut help overlay, search-and-redact, multi-select page ops, dark-mode toggle. - **Engine-gated (wire when their tickets land):** edit/delete annotations, Outline navigation, Forms view/fill, encrypted-PDF password prompt, print/flatten export, page insert/duplicate/extract, in-browser WASM worker render. > Phases 0–3 deliver the sophisticated look + surface every Done capability. Phases 4–5 wire the two flagships (real text, visual signature). Phase 6 polishes and lights up engine features as they land. "Everything (0–6)" = everything the engine currently supports, with graceful forward-compat stubs for the rest. --- ## 6. Preserve-don't-break checklist (regression guardrails) Each phase must keep these intact (manual smoke + ideally a test): - [ ] Upload (real gateway **and** mock fallback when gateway 501/offline) - [ ] Document list / select / metadata load - [ ] Virtualized scroll + per-page render + zoom (0.5×–3×) + rotate - [ ] Highlight → `applyEdits` → new doc id swap - [ ] Freehand ink → persists - [ ] Comment popup → persists - [ ] Redact → confirm → full save → persists - [ ] Page rotate / delete / reorder → persists + current-page math - [ ] Search debounce + prev/next + on-page overlay + scroll-to-page - [ ] Export download - [ ] WASM inspector still loads (keep as dev/debug panel, hidden behind a flag) - [ ] Coordinate mapping correctness (zoom/rotation/DPR) — the redaction y-flip math in `App.tsx:392` is fragile; cover before refactor. **Contracts that must not change without backend coordination:** the `EditOperation` envelope shape, `DocumentInfo` shape, and gateway URLs in `gatewayService.ts`. Redesign is presentation-layer; keep `gatewayService` as the stable seam. --- ## 7. Effort & sequencing summary | Phase | Theme | Rel. size | User-visible payoff | |---|---|---|---| | 0 | Tokens + rename + de-gimmick | S | Instantly looks less toy-like | | 1 | App shell, undo/redo, save-state | M | Feels like a real editor | | 2 | Tool rail + contextual strip + pickers | M | Pro tool ergonomics | | 3 | Tabbed inspector (props, fonts, comments, search) | M | Surfaces hidden power | | 4 | Real text selection/copy (engine Done; wiring) | M–L | Kills the biggest "fake" feeling | | 5 | Visual signature / text box / stamp (engine Done) | M–L | Major new capabilities | | 6 | Polish + light up engine features as they land | ongoing | Differentiation | > Every phase touches presentation or wires an **engine-Done** capability. Nothing here waits on unbuilt engine work except the explicitly-stubbed tabs (Outline, Forms) and the Phase-6 engine-gated list. --- ## 8. Locked decisions (confirmed) 1. **Theme:** **Light neutral** workspace (Acrobat/Foxit-like). Tokens architected so a dark theme is a later toggle, but dark mode itself is deferred to Phase 6. 2. **Layout:** **Left tool-rail + contextual tool strip** under the top bar (per §4.1). 3. **Scope:** **Full redesign, Phases 0–6** (everything — sophisticated shell *and* flagship features *and* polish). 4. **Components:** **Hand-built primitives on Tailwind** — zero new dependencies, full control, consistent with the current zero-dep frontend. **Next step:** detailed component-by-component build spec for Phase 0, then begin implementation phase-by-phase with a smoke-check of the §6 regression list after each phase.