From dfe6f121ffabc9fb1ad3356bd63e6562c0ff32ec Mon Sep 17 00:00:00 2001 From: Jim Lancaster <40281233+zoltan57@users.noreply.github.com> Date: Sat, 1 Aug 2026 18:28:00 -0500 Subject: [PATCH] V2 (new) sStep 4 complete. (untested, unreviewed, cross my fingers) --- docs/implementation_plan_v2.md | 7 ++ docs/ui/test-bento-grid.py | 107 ++++++++++++++++++ docs/ui_style_guide.md | 50 ++++++++ src/transcription/ui/components/job_detail.py | 4 +- .../ui/components/page_content.py | 6 +- src/transcription/ui/components/transcript.py | 4 +- src/transcription/ui/pages/jobs_page.py | 2 +- tests/ui/test_jobs_page.py | 6 +- 8 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 docs/ui/test-bento-grid.py create mode 100644 docs/ui_style_guide.md diff --git a/docs/implementation_plan_v2.md b/docs/implementation_plan_v2.md index 8b30960..8467f94 100644 --- a/docs/implementation_plan_v2.md +++ b/docs/implementation_plan_v2.md @@ -42,6 +42,13 @@ Use a fresh database. There will be no migrations, data conversion, legacy compa - Test both service-owned sessions and caller-provided sessions so flush/commit behavior remains correct. - Run the focused database and service tests, then the full suite with `uv run pytest`. +### 4. Update the UI for the V2 schema + +- Review the UI components and views that display document, job, person, and source data so they reference the V2 schema instead of V1 relationships. +- Update upload, detail, and listing screens to show the new person and source associations, revised-source fields, and the revised status values. +- Keep the UI behavior aligned with the updated service layer and ensure the existing UI tests continue to pass with the V2 data model. +- Consider the guidance in `docs/ui_style_guide.md` when making UI changes so the updated views remain consistent with the project’s visual and interaction conventions. + ## Done When - A fresh database is created directly from the V2 SQLModel metadata. diff --git a/docs/ui/test-bento-grid.py b/docs/ui/test-bento-grid.py new file mode 100644 index 0000000..f512182 --- /dev/null +++ b/docs/ui/test-bento-grid.py @@ -0,0 +1,107 @@ +from nicegui import ui + +# 1. Mature Dark Mode Setup +ui.dark_mode(True) + +# Define a refined dark palette using expanded dictionary styling +theme_colors = { + 'primary': '#6366f1', + 'secondary': '#8b5cf6', + 'accent': '#ec4899', + 'dark': '#0f172a', + 'dark_page': '#020617', + 'positive': '#10b981', + 'negative': '#ef4444', +} + +ui.colors(**theme_colors) + +# Optional: Add custom CSS for subtle noise overlays or kinetic typography +ui.add_css(''' + .glass-card { + background: rgba(255, 255, 255, 0.03); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 1.5rem; + } +''') + +# 2. Bento Grid Layout +with ui.element('div').classes('grid grid-cols-1 md:grid-cols-4 gap-6 w-full max-w-6xl mx-auto p-8'): + + # Header spanning all columns + with ui.element('div').classes('col-span-1 md:col-span-4 mb-4'): + ui.label('Analytics Dashboard').classes('text-4xl font-extrabold tracking-tight text-white') + ui.label('AI-driven insights for Q3').classes('text-lg text-slate-400 mt-1') + + # Large Feature Card (Glassmorphism + Functional Motion) + with ui.element('div').classes('glass-card col-span-1 md:col-span-2 p-6 transition-transform duration-300 hover:scale-[1.02]'): + ui.icon('monitoring', size='2rem').classes('text-primary mb-4') + ui.label('Revenue Prediction').classes('text-xl font-semibold text-slate-100') + ui.label('$45,231.00').classes('text-5xl font-bold text-white mt-2') + # Placeholder for an interactive EChart + ui.echart({ + 'xAxis': { + 'type': 'category', + 'data': [ + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + ], + }, + 'yAxis': { + 'type': 'value', + }, + 'series': [ + { + 'data': [ + 120, + 200, + 150, + 80, + 70, + ], + 'type': 'bar', + 'itemStyle': { + 'color': '#6366f1', + }, + }, + ], + }).classes('w-full h-48 mt-4') + + # Smaller Metric Cards + metric_cards = [ + { + 'title': 'Active Users', + 'value': '1,204', + 'icon': 'group', + 'color': 'text-secondary', + }, + { + 'title': 'Server Load', + 'value': '34%', + 'icon': 'memory', + 'color': 'text-accent', + }, + ] + + for card in metric_cards: + with ui.element('div').classes('glass-card col-span-1 p-6 flex flex-col justify-between transition-transform duration-300 hover:-translate-y-1'): + ui.icon(card['icon'], size='2rem').classes(card['color']) + ui.element('div').classes('flex-grow') + ui.label(card['value']).classes('text-4xl font-bold text-white mt-4') + ui.label(card['title']).classes('text-sm font-medium text-slate-400 uppercase tracking-wider') + + # AI Assistant Module (Adaptive Interface) + with ui.element('div').classes('glass-card col-span-1 md:col-span-4 p-6 flex items-center gap-4'): + ui.icon('smart_toy', size='2rem').classes('text-positive animate-pulse') + with ui.element('div'): + ui.label('Ambient AI Suggestion').classes('text-sm font-bold text-positive uppercase tracking-wider') + ui.label('Based on current server load, scaling up instances in the EU-West region is recommended.').classes('text-slate-300') + ui.space() + ui.button('Apply Now', color='positive').classes('rounded-full px-6 py-2 shadow-lg shadow-positive/20') + +ui.run(title='2026 UI Dashboard') \ No newline at end of file diff --git a/docs/ui_style_guide.md b/docs/ui_style_guide.md new file mode 100644 index 0000000..b7cadf3 --- /dev/null +++ b/docs/ui_style_guide.md @@ -0,0 +1,50 @@ +# UI Design Specification + +## 1. Overview and Aesthetic Philosophy +This document outlines the design language for a high-density document transcription workflow application built in Python. The UI represents a deliberate blend of **Modern Modular Dashboard** design and an **Archival/Academic** aesthetic. + +The interface balances modern structural trends with stylistic nods to historical preservation, ensuring maximum legibility, reduced eye strain during extended transcription sessions, and a clear presentation of complex relational data. + +## 2. Key Structural Patterns + +### 2.1. Modern Bento Grid Layout +The structure strictly adheres to the Bento Grid trend. +* The interface is divided into well-defined rectangular zones (e.g., image viewer, transcription editors, right-hand metadata column). +* This layout is highly functional for complex workflows, allowing users to cross-reference the original scanned document, raw AI output, human revision, and database relationships simultaneously without scrolling. + +### 2.2. High-Density Flat Design (Flat 2.0) +The styling deliberately avoids heavy drop shadows or aggressive 3D glassmorphism. +* **Subtle Separation:** Relies on 1px borders and slight variations in background shade to separate functional zones. +* **Data Density:** Prioritizes fitting a large amount of structured data (processing metadata, related entities, tokens) into a compact space while remaining readable. + +### 2.3. Dual-Typography System +The interface employs a classic typographic pairing standard in editorial or academic software: +* **Serif Headers:** Used for primary document titles. This lends a traditional, authoritative, and print-like feel appropriate for historical documents. +* **Sans-Serif UI/Body Text:** Functional UI elements, buttons, and transcription input fields use a clean, modern sans-serif font for maximum utility. + +## 3. The "Archival" Color Palette + +The color palette is strictly utilitarian: prioritizing glare elimination, minimizing eye strain for fading texts, and establishing hierarchy without distracting from the documents. + +### 3.1 Palette Philosophy +* **The "Canvas" (Backgrounds):** A warm off-white/cream mimicking standard archival folder stock to provide a low-contrast environment. Card surfaces use a slightly elevated manila/parchment hue for visual separation. +* **The "Ink" (Typography):** Deep charcoal or slate (aged iron-gall ink) instead of pure black to prevent visual vibration. Secondary text uses a muted grey-brown. +* **The "Accents" (Interactive Elements):** A sophisticated forest green for primary actions/tabs, and warm earthy sepia/tan for badges and highlights. +* **The "Dark Room" (Image Viewer):** A very dark, matte slate isolates the scanned original. This high contrast eliminates screen glare, ensuring faded handwriting remains the absolute focal point. + +### 3.2 Exact Color Specifications + +| UI Role | Color Name | Hex Code | Tailwind Equivalent (Approx) | +| :--- | :--- | :--- | :--- | +| App Background | Archival Cream | `#FAF9F6` | `stone-50` | +| Card Surface | Folder Stock | `#F4F0E6` | `stone-100` | +| Image Viewer | Matte Slate | `#2B2D2C` | `zinc-800` | +| Primary Text | Iron Ink | `#333333` | `neutral-800` | +| Secondary Text | Faded Type | `#6B6A65` | `stone-500` | +| Primary Accent | Library Green | `#2D5A4C` | `emerald-800` | +| Secondary Accent | Aged Sepia | `#E2C7A8` | `orange-200` | + +## 4. Implementation Guidelines +* When generating layout code, utilize UI framework grid utilities to construct the Bento Grid. +* Map the custom hex codes to the application's global theme variables to ensure consistency across all components. +* Prioritize static borders and background colors over hover effects or transition animations to maintain the academic utility of the interface. \ No newline at end of file diff --git a/src/transcription/ui/components/job_detail.py b/src/transcription/ui/components/job_detail.py index 3301454..c0eace8 100644 --- a/src/transcription/ui/components/job_detail.py +++ b/src/transcription/ui/components/job_detail.py @@ -49,11 +49,11 @@ def _render_source_section(source: Source) -> None: def _render_revision_section(revision: Source | None) -> None: with ui.card().classes("w-full q-pa-md vibe-card"): - ui.label("Revision").classes("text-subtitle1 text-weight-medium") + ui.label("Source revision").classes("text-subtitle1 text-weight-medium") ui.separator().classes("q-my-sm") if revision is None: - ui.label("No revision exists for this source.").classes("text-body2 vibe-text-muted") + ui.label("No source revision exists for this source.").classes("text-body2 vibe-text-muted") return render_revision_row(revision=revision, initially_expanded=True) diff --git a/src/transcription/ui/components/page_content.py b/src/transcription/ui/components/page_content.py index f62dd6d..424b863 100644 --- a/src/transcription/ui/components/page_content.py +++ b/src/transcription/ui/components/page_content.py @@ -39,14 +39,14 @@ def render_page_content( ui.label(raw_text).classes("transcription-placeholder__text") with ui.element("section").classes("transcription-placeholder__section"): - ui.label("Human revision").classes("transcription-placeholder__title") + ui.label("Human-reviewed text").classes("transcription-placeholder__title") ui.textarea(value=revised_text).props("outlined autogrow").classes("w-full") with ui.element("aside").props('aria-label="Document context"').classes("page-content__sidebar"): with ui.element("section").classes("page-content__sidebar-section"): ui.label("People").classes("page-content__sidebar-title") - ui.label("Author · Placeholder Person") - ui.label("Recipient · Placeholder Person") + ui.label("Author · Placeholder person") + ui.label("Recipient · Placeholder person") with ui.element("section").classes("page-content__sidebar-section"): ui.label("AI processing").classes("page-content__sidebar-title") diff --git a/src/transcription/ui/components/transcript.py b/src/transcription/ui/components/transcript.py index 6cd3cf9..701ec53 100644 --- a/src/transcription/ui/components/transcript.py +++ b/src/transcription/ui/components/transcript.py @@ -57,7 +57,7 @@ def render_revision_row( if revision is None: return None - header = "Revision | User-authored" + header = "Source revision | User-authored" caption = _format_created_at(revision.date_revised or revision.date_uploaded) expansion = ui.expansion(value=initially_expanded, group="group").classes(f"{classes} rounded-borders vibe-card") @@ -70,7 +70,7 @@ def render_revision_row( if on_delete is not None: with ui.dialog() as delete_dialog, ui.card().classes("q-pa-md"): - ui.label("Delete this transcript revision?").classes("text-body1") + ui.label("Delete this source revision?").classes("text-body1") with ui.row().classes("w-full justify-end q-gutter-sm"): ui.button("Cancel", on_click=lambda: delete_dialog.submit(False)).props("flat") ui.button("Delete", on_click=lambda: delete_dialog.submit(True)).props( diff --git a/src/transcription/ui/pages/jobs_page.py b/src/transcription/ui/pages/jobs_page.py index 795e130..61d4466 100644 --- a/src/transcription/ui/pages/jobs_page.py +++ b/src/transcription/ui/pages/jobs_page.py @@ -128,7 +128,7 @@ def register_page() -> None: # noqa: PLR0915 ).props('unelevated color="primary"') if current_revision_text is None: - ui.label("No revision exists for this source.").classes("text-body2 vibe-text-muted") + ui.label("No source revision exists for this source.").classes("text-body2 vibe-text-muted") return render_revision_row( diff --git a/tests/ui/test_jobs_page.py b/tests/ui/test_jobs_page.py index f28259d..0ca1bd5 100644 --- a/tests/ui/test_jobs_page.py +++ b/tests/ui/test_jobs_page.py @@ -48,8 +48,8 @@ class TestPageRendering: assert response.status_code == 200 assert "Original Transcription" in response.text assert "detail.pdf" in response.text - assert "Revision" in response.text - assert "Revision" in response.text + assert "Source revision" in response.text + assert "Source revision" in response.text assert "hello" in response.text assert "original text" in response.text assert "Document preview" in response.text @@ -87,7 +87,7 @@ class TestPageRendering: assert response.status_code == 200 assert "Revision Editor" in response.text assert "Create revision" in response.text - assert "No revision exists for this source." in response.text + assert "No source revision exists for this source." in response.text def test_job_detail_page_shows_update_action_for_existing_revision(self, app_client, seed_job): """GET /ui/jobs/{job_id} renders revision editor with update action when revision exists."""