From d8a0d4d83ea1eda9b3341196a281d3e57c2cfef0 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Sun, 10 May 2026 19:15:10 +0800 Subject: [PATCH] docs: align dashboard ux contract with refined ui --- docs/ux-contract.md | 6 +++--- paperforge/plugin/main.js | 29 ++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/ux-contract.md b/docs/ux-contract.md index f3b04c2d..f4dfc155 100644 --- a/docs/ux-contract.md +++ b/docs/ux-contract.md @@ -47,10 +47,10 @@ | Step ID | Trigger | Action | Measurable Outcome | Error Contract | |---------|---------|--------|-------------------|----------------| | W4-S1 | User opens Obsidian vault | Plugin loads and registers dashboard view | Plugin view registered; sidebar icon visible; clicking opens PaperForge dashboard panel | Plugin not loaded; console shows "Failed to register PaperForge view" | -| W4-S2 | User opens a `.base` file | Collection mode activates | Dashboard switches to `collection` mode; shows: workflow funnel (Total β†’ PDF Ready β†’ OCR Done β†’ Deep Read), OCR pipeline progress bar scoped to current base, issue summary (compact, only when issues exist), action buttons (Sync Library, Run OCR) | Falls to global mode; missing stats | -| W4-S3 | User opens a formal note (`.md` with `zotero_key`) | Paper mode activates | Dashboard switches to `paper` mode; shows: status strip (PDF/OCR/DeepRead pills), paper overview card (Pass 1 summary from `## πŸ” η²Ύθ―»`), workflow toggle checkboxes (`do_ocr`/`analyze` β€” sync to Base), next-step card, files row (Open PDF / Open Fulltext), collapsible technical details | Falls to global mode; components empty or erroring | +| W4-S2 | User opens a `.base` file | Collection mode activates | Dashboard switches to `collection` mode; preserves module order: header, workflow funnel (Total β†’ PDF Ready β†’ OCR Done β†’ Deep Read), OCR pipeline scoped to current base, collection-scoped issue/health messaging only inside the collection view, then action buttons (Run OCR primary, Sync Library secondary) | Falls to global mode; missing stats; collection issues appear as a separate extra module outside the collection view | +| W4-S3 | User opens a formal note (`.md` with `zotero_key`) | Paper mode activates | Dashboard switches to `paper` mode; preserves module order: paper header, status/file row, paper overview card, next-step or complete row, discussion card, collapsible technical details; panel keeps bottom-safe-area padding so final content is fully visible; expanding technical details does not trigger scrollbar reflow/width jump; first discussion expand does not collapse on the first click | Falls to global mode; components empty or erroring; bottom content is clipped; expanding discussion/technical details causes collapse or layout jump | | W4-S4 | User opens `fulltext.md` or other workspace file | Paper mode activates via workspace detection | Dashboard stays in `paper` mode for any file inside a paper workspace directory (`{key} - {title}/`). zotero_key resolved from dirname pattern | Falls to global mode | -| W4-S5 | User opens no file or non-workspace `.md` | Global mode activates | Dashboard shows: library snapshot (papers / PDFs / OCR done / deep-read done), system status grid (runtime / index / Zotero export / OCR token), issues panel (only when anomalies exist with Run Doctor / Repair Issues), action buttons (Open Literature Hub / Sync Library) | Shows loading/empty state; console errors | +| W4-S5 | User opens no file or non-workspace `.md` | Global mode activates | Dashboard shows, in preserved order: library snapshot (papers / PDFs / OCR done / deep-read done), system status grid (runtime / index / Zotero export / OCR token), optional issues panel (only when anomalies exist with Run Doctor / Repair Issues), then action buttons (Open Literature Hub primary / Sync Library secondary); light and dark themes both keep muted accents, readable contrast, and visible keyboard focus on collection/global action controls | Shows loading/empty state; console errors; module order changes unexpectedly; light/dark theme contrast or focus visibility regresses | | W4-S6 | User toggles `do_ocr` or `analyze` checkbox | Workflow toggle writes to frontmatter | Checkbox state writes to formal note YAML frontmatter via `processFrontMatter`; Base view reflects change immediately (reads from file); dashboard auto-refreshes via `modify` event | Frontmatter not written; notice shows "Note file not found" | --- diff --git a/paperforge/plugin/main.js b/paperforge/plugin/main.js index ef4f11d6..01891377 100644 --- a/paperforge/plugin/main.js +++ b/paperforge/plugin/main.js @@ -1316,7 +1316,7 @@ class PaperForgeStatusView extends ItemView { const actionsRow = view.createEl('div', { cls: 'paperforge-global-actions' }); actionsRow.createEl('div', { cls: 'paperforge-section-label', text: 'Start Working' }); const btnsRow = actionsRow.createEl('div', { cls: 'paperforge-global-actions-row' }); - const hubBtn = btnsRow.createEl('button', { cls: 'paperforge-contextual-btn' }); + const hubBtn = btnsRow.createEl('button', { cls: 'paperforge-contextual-btn primary' }); hubBtn.createEl('span', { cls: 'paperforge-contextual-btn-icon', text: '\uD83D\uDCC1' }); hubBtn.createEl('span', { text: 'Open Literature Hub' }); hubBtn.addEventListener('click', () => { @@ -1817,6 +1817,14 @@ class PaperForgeStatusView extends ItemView { // ── Contextual Actions ── const actionsRow = view.createEl('div', { cls: 'paperforge-collection-actions' }); + const ocrActionBtn = actionsRow.createEl('button', { cls: 'paperforge-contextual-btn primary' }); + ocrActionBtn.createEl('span', { cls: 'paperforge-contextual-btn-icon', text: '\u229E' }); + ocrActionBtn.createEl('span', { text: 'Run OCR' }); + ocrActionBtn.addEventListener('click', () => { + const action = ACTIONS.find(a => a.id === 'paperforge-ocr'); + if (action) this._runAction(action, ocrActionBtn); + }); + const syncBtn = actionsRow.createEl('button', { cls: 'paperforge-contextual-btn' }); syncBtn.createEl('span', { cls: 'paperforge-contextual-btn-icon', text: '\u21BB' }); syncBtn.createEl('span', { text: 'Sync Library' }); @@ -1824,14 +1832,6 @@ class PaperForgeStatusView extends ItemView { const action = ACTIONS.find(a => a.id === 'paperforge-sync'); if (action) this._runAction(action, syncBtn); }); - - const ocrActionBtn = actionsRow.createEl('button', { cls: 'paperforge-contextual-btn' }); - ocrActionBtn.createEl('span', { cls: 'paperforge-contextual-btn-icon', text: '\u229E' }); - ocrActionBtn.createEl('span', { text: 'Run OCR' }); - ocrActionBtn.addEventListener('click', () => { - const action = ACTIONS.find(a => a.id === 'paperforge-ocr'); - if (action) this._runAction(action, ocrActionBtn); - }); } /* ── Refresh current mode (called on index change, D-09, REFR-01) ── */ @@ -2067,6 +2067,17 @@ class PaperForgeStatusView extends ItemView { const leafHandler = this.app.workspace.on('active-leaf-change', () => { clearTimeout(this._leafChangeTimer); this._leafChangeTimer = setTimeout(() => { + const resolved = this._resolveModeForFile(this.app.workspace.getActiveFile()); + const nextMode = resolved.mode; + const nextFilePath = resolved.filePath; + + // Clicking inside the dashboard can activate its leaf without changing + // the underlying paper/base context. Avoid rebuilding the whole mode + // tree in that case, or transient UI state like discussion expansion resets. + if (this._currentMode === nextMode && this._currentFilePath === nextFilePath) { + return; + } + this._detectAndSwitch(); }, 300); });