Polish workbench shell density
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-20 03:32:23 +02:00
parent ff4f17dd24
commit 5fd0124626
7 changed files with 155 additions and 4 deletions
+8
View File
@@ -7,6 +7,14 @@
# Changelog # 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) ## Sprint 81 Result state consistency polish (2026-06-20)
- Added shared result-state styling for compact loading, error, empty and ready states. - Added shared result-state styling for compact loading, error, empty and ready states.
@@ -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
+27
View File
@@ -2984,3 +2984,30 @@ Limitations:
Next recommended pass: Next recommended pass:
- Continue with visual density review for topbar/sidebar/responsive shell after another live browser 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.
+1
View File
@@ -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 accessibility focus polish for primary workbench keyboard navigation.
- [x] Add raster/vector operation form readability polish for dense tool panels. - [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 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 ## Sprint 8 status
+1
View File
@@ -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. - 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. - 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 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. - 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 ## Workbench shell refactor
+5 -2
View File
@@ -452,6 +452,9 @@ function App(): JSX.Element {
return ( return (
<div className="app-shell workbench-shell"> <div className="app-shell workbench-shell">
<a className="skip-link" href="#workspace-main">
Skip to workspace
</a>
<header className="workbench-topbar"> <header className="workbench-topbar">
<div className="brand-block"> <div className="brand-block">
<p className="eyebrow">GeoAI Workbench</p> <p className="eyebrow">GeoAI Workbench</p>
@@ -481,7 +484,7 @@ function App(): JSX.Element {
<div className="workbench-layout"> <div className="workbench-layout">
<aside className="workbench-sidebar" aria-label="Workbench navigation"> <aside className="workbench-sidebar" aria-label="Workbench navigation">
<nav> <nav aria-label="Primary workspaces">
{workspaceNavItems.map((item) => ( {workspaceNavItems.map((item) => (
<button <button
key={item.key} key={item.key}
@@ -499,7 +502,7 @@ function App(): JSX.Element {
</nav> </nav>
</aside> </aside>
<main className="workbench-main"> <main className="workbench-main" id="workspace-main" tabIndex={-1}>
<div className="workspace-heading"> <div className="workspace-heading">
<div> <div>
<p className="eyebrow">Workspace</p> <p className="eyebrow">Workspace</p>
+75 -2
View File
@@ -48,6 +48,31 @@ body {
overflow-x: clip; 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, button,
input, input,
select, select,
@@ -671,6 +696,7 @@ section li strong + div {
min-height: 0; min-height: 0;
max-width: 100%; max-width: 100%;
overflow: auto; overflow: auto;
scroll-margin-top: 8.5rem;
padding: 0.9rem 1.05rem 1.15rem; padding: 0.9rem 1.05rem 1.15rem;
} }
@@ -2325,11 +2351,36 @@ button.entity-card {
} }
.workbench-sidebar nav, .workbench-sidebar nav,
.context-bar,
.quick-action-grid { .quick-action-grid {
grid-template-columns: repeat(2, minmax(0, 1fr)); 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 { .nav-item {
min-height: 3.45rem; min-height: 3.45rem;
} }
@@ -2390,6 +2441,18 @@ button.entity-card {
grid-template-columns: 1fr; 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 { .inspector-tabs {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
@@ -2414,11 +2477,19 @@ button.entity-card {
max-width: 100%; max-width: 100%;
overflow-x: auto; overflow-x: auto;
overscroll-behavior-x: contain; overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
padding-bottom: 0.2rem; padding-bottom: 0.2rem;
} }
.workbench-sidebar .nav-item { .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 { .workspace-nav-cluster {
@@ -2427,11 +2498,13 @@ button.entity-card {
max-width: 100%; max-width: 100%;
overflow-x: auto; overflow-x: auto;
overscroll-behavior-x: contain; overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
padding-bottom: 0.1rem; padding-bottom: 0.1rem;
} }
.command-chip { .command-chip {
min-width: 6.75rem; min-width: 6.75rem;
scroll-snap-align: start;
} }
.status-strip-grid { .status-strip-grid {