Polish inspector mobile layout
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 02:07:24 +02:00
parent c7d00fd8dc
commit 27b9219383
8 changed files with 166 additions and 10 deletions
+8
View File
@@ -7,6 +7,14 @@
# Changelog # Changelog
## Sprint 77 Inspector mobile visual polish (2026-06-20)
- Added compact inspector action button grids for narrow screens.
- Added mobile-safe wrapping for dataset filenames, checksums, bounds, persisted export paths and loaded feature/job JSON.
- Added structured raster/vector tool panel classes so operation inputs and buttons stay inside the inspector.
- Added static regression coverage for inspector mobile CSS and dataset tool markup contracts.
- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed.
## Sprint 76 Export/System mobile visual polish (2026-06-20) ## Sprint 76 Export/System mobile visual polish (2026-06-20)
- Added scan-friendly Provider Capabilities cards with structured status, authority, geometry, query mode and layer chips. - Added scan-friendly Provider Capabilities cards with structured status, authority, geometry, query mode and layer chips.
@@ -0,0 +1,46 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def test_inspector_mobile_density_css_contracts() -> None:
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert ".inspector-card .button-row" in css
assert ".inspector-card .button-row button" in css
assert ".inspector-field strong" in css
assert ".dataset-detail-panel p" in css
assert ".dataset-detail-panel li" in css
assert ".dataset-detail-panel .job-result" in css
assert ".dataset-tool-panel" in css
assert ".dataset-tool-group" in css
assert ".dataset-tool-group label" in css
assert ".dataset-tool-group input," in css
assert ".dataset-tool-group select" in css
assert ".dataset-tool-group button" in css
assert "overflow-wrap: anywhere;" in css
assert "max-height: 18rem;" in css
def test_inspector_components_keep_mobile_tool_markup() -> None:
inspector = (ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx").read_text(
encoding="utf-8"
)
dataset_detail = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(
encoding="utf-8"
)
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(
encoding="utf-8"
)
vector_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "VectorControls.tsx").read_text(
encoding="utf-8"
)
assert 'className="workbench-inspector-panel"' in inspector
assert 'className="inspector-action-bar"' in inspector
assert 'className="dataset-detail-panel"' in dataset_detail
assert 'className="dataset-tool-panel raster-tool-panel"' in raster_controls
assert 'className="dataset-tool-group"' in raster_controls
assert 'className="dataset-tool-panel vector-tool-panel"' in vector_controls
assert 'className="dataset-tool-group"' in vector_controls
+25
View File
@@ -2850,3 +2850,28 @@ Limitations:
Next recommended pass: Next recommended pass:
- Continue with right-side inspector mobile polish, especially long dataset/vector/raster metadata and action groups. - Continue with right-side inspector mobile polish, especially long dataset/vector/raster metadata and action groups.
## Sprint 77 Inspector mobile visual polish (2026-06-20)
Changed:
- Added compact inspector action button grids and mobile-safe wrapping for long inspector values.
- Added scroll/wrap containment for dataset detail text, job JSON and loaded feature metadata.
- Added `dataset-tool-panel` and `dataset-tool-group` classes to raster/vector operation controls.
- Added CSS containment for raster/vector tool labels, inputs, selects and buttons inside the inspector.
- Added `backend/tests/test_sprint77_inspector_mobile_polish.py`.
- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`.
Tested:
- Red step: `python -m pytest backend/tests/test_sprint77_inspector_mobile_polish.py -q` failed on missing inspector mobile CSS and dataset tool markup contracts.
- `python -m pytest backend/tests/test_sprint77_inspector_mobile_polish.py backend/tests/test_sprint76_export_system_mobile_polish.py backend/tests/test_sprint75_ai_labs_mobile_polish.py backend/tests/test_sprint72_mobile_overflow_hardening.py backend/tests/test_sprint52_workbench_inspector_tabs.py backend/tests/test_sprint30_workbench_components.py backend/tests/test_sprint29_dataset_components.py -q` (`18 passed`)
- `cd frontend && npm run typecheck`
- `cd frontend && npm run build`
Open:
- Run full readiness, redeploy Tower and verify live runtime smoke after deployment.
Limitations:
- Frontend presentation polish only; no dataset workflow, raster/vector operation behavior, API, persistence, migration, provider fetching or AI/model changes.
Next recommended pass:
- Continue with report/export preview readability polish, especially large JSON/HTML handoff artifacts.
+1
View File
@@ -66,6 +66,7 @@ This file now starts with the current implementation status. Older preparation/b
- [x] Add Data/Map mobile visual polish for upload forms, action grids and map toolbar density. - [x] Add Data/Map mobile visual polish for upload forms, action grids and map toolbar density.
- [x] Add AI Labs mobile visual polish for model cards, lab forms and result tables. - [x] Add AI Labs mobile visual polish for model cards, lab forms and result tables.
- [x] Add Export/System mobile visual polish for export actions and provider capability cards. - [x] Add Export/System mobile visual polish for export actions and provider capability cards.
- [x] Add inspector mobile polish for dataset metadata, raster/vector tools and action groups.
## Sprint 8 status ## Sprint 8 status
+2
View File
@@ -10,6 +10,8 @@ AI Labs uses the same mobile-density baseline for Detection and Segmentation mod
Exports and System provider capabilities now use compact, mobile-safe cards for handoff actions, artifact filters and provider metadata. Long provider limitations, export ids and artifact paths wrap inside their cards instead of widening the workbench. Exports and System provider capabilities now use compact, mobile-safe cards for handoff actions, artifact filters and provider metadata. Long provider limitations, export ids and artifact paths wrap inside their cards instead of widening the workbench.
The right-side inspector also uses mobile-safe metadata and tool panels. Dataset filenames, checksums, bounds, job JSON, raster/vector operation inputs and inspector action groups wrap or scroll within the inspector instead of widening the workbench.
## Scope implemented ## Scope implemented
- API client layer (`src/services/api`) - API client layer (`src/services/api`)
- Project and area list/create flows - Project and area list/create flows
@@ -94,7 +94,7 @@ export function RasterControls({
onRunRasterNdbi, onRunRasterNdbi,
}: RasterControlsProps) { }: RasterControlsProps) {
return ( return (
<div> <div className="dataset-tool-panel raster-tool-panel">
<p>Raster driver: {selectedRasterMetadata?.driver ?? 'n/a'}</p> <p>Raster driver: {selectedRasterMetadata?.driver ?? 'n/a'}</p>
<p>Raster size: {selectedRasterMetadata ? `${selectedRasterMetadata.width} x ${selectedRasterMetadata.height}` : 'n/a'}</p> <p>Raster size: {selectedRasterMetadata ? `${selectedRasterMetadata.width} x ${selectedRasterMetadata.height}` : 'n/a'}</p>
<p>Raster checksum: {selectedRasterMetadata?.checksum_sha256 ?? 'n/a'}</p> <p>Raster checksum: {selectedRasterMetadata?.checksum_sha256 ?? 'n/a'}</p>
@@ -132,7 +132,7 @@ export function RasterControls({
</ul> </ul>
</div> </div>
) : null} ) : null}
<div> <div className="dataset-tool-group">
<label> <label>
Reproject CRS Reproject CRS
<input <input
@@ -153,7 +153,7 @@ export function RasterControls({
Reproject raster Reproject raster
</button> </button>
</div> </div>
<div> <div className="dataset-tool-group">
<label> <label>
Clip area Clip area
<select value={selectedClipAreaId} onChange={(event) => onSetSelectedClipAreaId(event.target.value)}> <select value={selectedClipAreaId} onChange={(event) => onSetSelectedClipAreaId(event.target.value)}>
@@ -169,7 +169,7 @@ export function RasterControls({
</button> </button>
{areas.length === 0 ? <p className="error">Create an area before raster clipping.</p> : null} {areas.length === 0 ? <p className="error">Create an area before raster clipping.</p> : null}
</div> </div>
<div> <div className="dataset-tool-group">
<label> <label>
Tile size Tile size
<input <input
@@ -208,7 +208,7 @@ export function RasterControls({
<h4>Spectral indices</h4> <h4>Spectral indices</h4>
<div> <div>
<p>Use available band indexes from the raster file (1-based).</p> <p>Use available band indexes from the raster file (1-based).</p>
<div> <div className="dataset-tool-group">
<p>NDVI</p> <p>NDVI</p>
<label> <label>
Nir band Nir band
@@ -222,7 +222,7 @@ export function RasterControls({
Compute NDVI Compute NDVI
</button> </button>
</div> </div>
<div> <div className="dataset-tool-group">
<p>NDWI</p> <p>NDWI</p>
<label> <label>
Nir band Nir band
@@ -236,7 +236,7 @@ export function RasterControls({
Compute NDWI Compute NDWI
</button> </button>
</div> </div>
<div> <div className="dataset-tool-group">
<p>NDBI</p> <p>NDBI</p>
<label> <label>
Swir band Swir band
@@ -24,9 +24,9 @@ export function VectorControls({
onRunVectorIntersect, onRunVectorIntersect,
}: VectorControlsProps) { }: VectorControlsProps) {
return ( return (
<div> <div className="dataset-tool-panel vector-tool-panel">
<h3>Vector operations</h3> <h3>Vector operations</h3>
<div> <div className="dataset-tool-group">
<label> <label>
Clip area Clip area
<select value={selectedClipAreaId} onChange={(event) => onSetSelectedClipAreaId(event.target.value)}> <select value={selectedClipAreaId} onChange={(event) => onSetSelectedClipAreaId(event.target.value)}>
@@ -44,7 +44,7 @@ export function VectorControls({
<button type="button" onClick={onRunVectorBuffer}> <button type="button" onClick={onRunVectorBuffer}>
Run buffer (25m) Run buffer (25m)
</button> </button>
<div> <div className="dataset-tool-group">
<label> <label>
Intersect target Intersect target
<select value={selectedIntersectTargetId} onChange={(event) => onSetSelectedIntersectTargetId(event.target.value)}> <select value={selectedIntersectTargetId} onChange={(event) => onSetSelectedIntersectTargetId(event.target.value)}>
+74
View File
@@ -2001,12 +2001,27 @@ button.entity-card {
} }
.inspector-field strong { .inspector-field strong {
min-width: 0;
overflow: hidden; overflow: hidden;
font-size: 0.88rem; font-size: 0.88rem;
line-height: 1.28; line-height: 1.28;
overflow-wrap: anywhere;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.inspector-card .button-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
gap: 0.42rem;
}
.inspector-card .button-row button {
width: 100%;
min-width: 0;
min-height: 2.2rem;
margin-top: 0;
}
.workbench-inspector-panel .dataset-detail-panel { .workbench-inspector-panel .dataset-detail-panel {
border: 0; border: 0;
padding: 0; padding: 0;
@@ -2020,6 +2035,51 @@ button.entity-card {
gap: 0.55rem; gap: 0.55rem;
} }
.dataset-detail-panel p,
.dataset-detail-panel li {
min-width: 0;
overflow-wrap: anywhere;
}
.dataset-detail-panel .job-result {
max-height: 18rem;
overflow: auto;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.dataset-tool-panel {
display: grid;
min-width: 0;
gap: 0.62rem;
}
.dataset-tool-group {
display: grid;
min-width: 0;
gap: 0.5rem;
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.62rem;
background: #ffffff;
}
.dataset-tool-group label {
min-width: 0;
}
.dataset-tool-group input,
.dataset-tool-group select {
width: 100%;
min-width: 0;
}
.dataset-tool-group button {
width: fit-content;
max-width: 100%;
min-width: 0;
}
@media (max-width: 1360px) { @media (max-width: 1360px) {
.workbench-layout { .workbench-layout {
grid-template-columns: 12rem minmax(0, 1fr); grid-template-columns: 12rem minmax(0, 1fr);
@@ -2151,6 +2211,20 @@ button.entity-card {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.inspector-card,
.dataset-tool-group {
padding: 0.58rem;
}
.inspector-action-bar,
.inspector-card .button-row {
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.dataset-tool-group button {
width: 100%;
}
.workbench-sidebar nav { .workbench-sidebar nav {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;