From 5fd0124626d4c7a17a79c508f2c2937355f9a0d1 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 20 Jun 2026 03:32:23 +0200 Subject: [PATCH] Polish workbench shell density --- CHANGELOG.md | 8 ++ .../test_sprint82_shell_density_polish.py | 38 +++++++++ docs/CODEX_EXECUTION_LOG.md | 27 +++++++ docs/TODO.md | 1 + frontend/README.md | 1 + frontend/src/App.tsx | 7 +- frontend/src/styles/app.css | 77 ++++++++++++++++++- 7 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 backend/tests/test_sprint82_shell_density_polish.py diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe21b62..25aefd9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ # Changelog +## Sprint 82 Shell density polish (2026-06-20) + +- Added a keyboard skip link to jump directly from the workbench shell to active workspace content. +- Added an explicit primary workspace navigation label and main focus target. +- Made narrow-view context chips, sidebar navigation and workspace shortcuts more compact and scroll-safe. +- Added static regression coverage for shell density, skip-link and mobile navigation contracts. +- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed. + ## Sprint 81 Result state consistency polish (2026-06-20) - Added shared result-state styling for compact loading, error, empty and ready states. diff --git a/backend/tests/test_sprint82_shell_density_polish.py b/backend/tests/test_sprint82_shell_density_polish.py new file mode 100644 index 00000000..edb17e33 --- /dev/null +++ b/backend/tests/test_sprint82_shell_density_polish.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_workbench_shell_has_skip_link_and_main_focus_target() -> None: + app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + + assert 'className="skip-link"' in app + assert 'href="#workspace-main"' in app + assert 'nav aria-label="Primary workspaces"' in app + assert 'id="workspace-main"' in app + assert "tabIndex={-1}" in app + + +def test_mobile_shell_density_contracts_are_defined() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert ".skip-link" in css + assert ".skip-link:focus-visible" in css + assert "scroll-margin-top: 8.5rem;" in css + assert "grid-template-columns: repeat(4, minmax(6.75rem, 1fr));" in css + assert "overflow-x: auto;" in css + assert "scroll-snap-type: x proximity;" in css + assert "white-space: nowrap;" in css + + +def test_small_screen_navigation_keeps_labels_readable() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert "@media (max-width: 620px)" in css + assert ".workbench-sidebar .nav-item" in css + assert "min-width: 9.5rem;" in css + assert ".nav-item small" in css + assert "line-height: 1.18;" in css diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 735bfe8d..94050bfa 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -2984,3 +2984,30 @@ Limitations: Next recommended pass: - Continue with visual density review for topbar/sidebar/responsive shell after another live browser pass. + +## Sprint 82 Shell density polish (2026-06-20) + +Changed: +- Added a keyboard skip link that targets the active workspace main region. +- Added an explicit `Primary workspaces` label to the sidebar navigation and a focusable `workspace-main` target. +- Tightened narrow-screen topbar, context chip, sidebar nav and workspace shortcut density while keeping intentional horizontal rails scroll-safe. +- Added `backend/tests/test_sprint82_shell_density_polish.py`. +- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`. + +Tested: +- Live browser pre-check against `http://192.168.10.150:1202` showed no console errors, no horizontal document overflow and a too-tall narrow viewport topbar/context stack. +- Red step: `python -m pytest backend/tests/test_sprint82_shell_density_polish.py -q` failed on missing skip-link, main focus target and compact mobile shell CSS contracts. +- `python -m pytest backend/tests/test_sprint82_shell_density_polish.py -q` (`3 passed`) +- `python -m pytest backend/tests/test_sprint82_shell_density_polish.py backend/tests/test_sprint79_accessibility_focus_polish.py backend/tests/test_sprint72_mobile_overflow_hardening.py backend/tests/test_sprint49_workbench_shell_refactor.py backend/tests/test_sprint47_workbench_interaction_smoke.py -q` (`13 passed`) +- `cd frontend && npm run typecheck` +- `cd frontend && npm run build` +- `bash scripts/run_readiness_check.sh` (`279 passed`; frontend typecheck/build passed; Alembic single head `202606120900`) + +Open: +- Deployment and live browser verification still need to run for this pass. + +Limitations: +- Frontend shell presentation/accessibility polish only; no workflow behavior, API contract, persistence, migration, provider fetching or AI/model changes. + +Next recommended pass: +- Continue with a live visual review of dense workspace panel hierarchy after the shell density changes are deployed. diff --git a/docs/TODO.md b/docs/TODO.md index 14033407..0642a143 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -71,6 +71,7 @@ This file now starts with the current implementation status. Older preparation/b - [x] Add accessibility focus polish for primary workbench keyboard navigation. - [x] Add raster/vector operation form readability polish for dense tool panels. - [x] Add compact loading/error/empty/result state polish across QA, exports and AI labs. +- [x] Add compact shell density polish for topbar context, mobile navigation and workspace skip flow. ## Sprint 8 status diff --git a/frontend/README.md b/frontend/README.md index 7c6db061..0b40b862 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -236,6 +236,7 @@ QA/QC, exports and AI lab result panels use shared loading, error, empty and rea - Offline demo workflow orchestration lives in `src/hooks/useDemoWorkflow.ts`, because it coordinates project, dataset, map, QA/QC, detection, segmentation and export state after the backend fixture seed. - Workbench bootstrap and reload effects live in `src/hooks/useWorkbenchBootstrap.ts`, keeping `App.tsx` focused on composing hooks into panels. - The workbench shell includes a compact command bar, consistent raised/sunken surfaces, structured empty states and scroll-safe AI result tables to keep the V1 workflow usable across desktop and mobile widths. +- The shell includes a keyboard skip link, an explicit primary workspace navigation label and compact horizontal context/navigation rails on narrow screens so active workspace content is reachable without a tall preamble. - The Export Center includes a handoff readiness summary, grouped artifact actions and provenance-rich export cards so report/GeoJSON handoff stays understandable in long-running demo projects. ## Workbench shell refactor diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1ce721fb..6f73fb76 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -452,6 +452,9 @@ function App(): JSX.Element { return (
+ + Skip to workspace +

GeoAI Workbench

@@ -481,7 +484,7 @@ function App(): JSX.Element {
-
+

Workspace

diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 146278c9..fbd6082a 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -48,6 +48,31 @@ body { overflow-x: clip; } +.skip-link { + position: fixed; + top: 0.45rem; + left: 0.75rem; + z-index: 100; + transform: translateY(-150%); + border: 1px solid var(--accent); + border-radius: 6px; + padding: 0.48rem 0.7rem; + background: #ffffff; + color: var(--accent-strong); + font-size: 0.84rem; + font-weight: 800; + text-decoration: none; + box-shadow: var(--shadow-soft); + transition: transform 120ms ease; +} + +.skip-link:focus-visible { + transform: translateY(0); + outline: 3px solid var(--focus-ring); + outline-offset: 2px; + box-shadow: 0 0 0 5px var(--focus-ring-soft); +} + button, input, select, @@ -671,6 +696,7 @@ section li strong + div { min-height: 0; max-width: 100%; overflow: auto; + scroll-margin-top: 8.5rem; padding: 0.9rem 1.05rem 1.15rem; } @@ -2325,11 +2351,36 @@ button.entity-card { } .workbench-sidebar nav, - .context-bar, .quick-action-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .context-bar { + display: grid; + grid-template-columns: repeat(4, minmax(6.75rem, 1fr)); + max-width: 100%; + overflow-x: auto; + overscroll-behavior-x: contain; + scroll-snap-type: x proximity; + padding-bottom: 0.1rem; + } + + .context-bar > div { + min-height: 2.62rem; + padding: 0.36rem 0.48rem; + scroll-snap-align: start; + } + + .context-bar span, + .context-bar strong { + white-space: nowrap; + } + + .context-bar strong { + overflow: hidden; + text-overflow: ellipsis; + } + .nav-item { min-height: 3.45rem; } @@ -2390,6 +2441,18 @@ button.entity-card { grid-template-columns: 1fr; } + .workbench-topbar { + gap: 0.55rem; + } + + .brand-block h1 { + font-size: 1.05rem; + } + + .brand-block .eyebrow { + font-size: 0.62rem; + } + .inspector-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); } @@ -2414,11 +2477,19 @@ button.entity-card { max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; + scroll-snap-type: x proximity; padding-bottom: 0.2rem; } .workbench-sidebar .nav-item { - min-width: 8.75rem; + min-width: 9.5rem; + min-height: 3.2rem; + scroll-snap-align: start; + } + + .nav-item small { + font-size: 0.72rem; + line-height: 1.18; } .workspace-nav-cluster { @@ -2427,11 +2498,13 @@ button.entity-card { max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; + scroll-snap-type: x proximity; padding-bottom: 0.1rem; } .command-chip { min-width: 6.75rem; + scroll-snap-align: start; } .status-strip-grid {