21 KiB
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)
- Upload PDF → list → select → view (with mock fallback when gateway is down)
- Virtualized scroll viewer with per-page render + zoom
- Highlight via drag-select → persists via
applyEdits(creates new doc id) - Freehand ink draw → persists
- Sticky-note comment (click → popup → save) → persists
- Redact area (drag → confirm → permanent removal, full save)
- Page rotate / delete / reorder → persists
- Live full-text search (debounced) with prev/next + on-page highlight overlay
- Export / download edited PDF
- 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 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.cssinto 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.htmltitle, 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.tsxover the existingapplyEditsmodel — each edit returns anewDocumentId, 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 —
/fontsinventory, the differentiator (Done). - Comments/Notes — list + create only (annotation reader in-progress; edit/delete deferred until the
delete/update_annotationop 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) 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 viatext_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:392is 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)
- 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.
- Layout: Left tool-rail + contextual tool strip under the top bar (per §4.1).
- Scope: Full redesign, Phases 0–6 (everything — sophisticated shell and flagship features and polish).
- 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.