Polish AI Labs density
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 88 AI Labs density polish (2026-06-20)
|
||||||
|
|
||||||
|
- Grouped Detection Lab and Segmentation Lab model registry, run controls, result loading and QA controls into focused surfaces.
|
||||||
|
- Added shared AI Lab density CSS for model lists, run forms, result/QA summaries and mobile-safe grids.
|
||||||
|
- Preserved existing detection/segmentation model loading, run, result filtering and QA callbacks without API or persistence changes.
|
||||||
|
- Added static regression coverage for AI Lab hierarchy and density contracts.
|
||||||
|
- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed.
|
||||||
|
|
||||||
## Sprint 87 Change Detection density polish (2026-06-20)
|
## Sprint 87 Change Detection density polish (2026-06-20)
|
||||||
|
|
||||||
- Grouped Change Detection heading, input controls, result states, summary and warnings into focused surfaces.
|
- Grouped Change Detection heading, input controls, result states, summary and warnings into focused surfaces.
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_detection_lab_exposes_structured_surfaces() -> None:
|
||||||
|
lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 'className="workspace-panel ai-lab-shell detection-lab-shell"' in lab
|
||||||
|
assert 'className="ai-lab-model-surface"' in lab
|
||||||
|
assert 'aria-label="Detection model capabilities"' in lab
|
||||||
|
assert 'className="ai-lab-run-surface"' in lab
|
||||||
|
assert 'aria-label="Detection run controls"' in lab
|
||||||
|
assert 'className="ai-lab-state-stack"' in lab
|
||||||
|
assert 'className="ai-lab-results-surface"' in lab
|
||||||
|
assert 'aria-label="Detection results"' in lab
|
||||||
|
assert 'className="ai-lab-qa-surface"' in lab
|
||||||
|
assert 'aria-label="Detection QA controls and results"' in lab
|
||||||
|
|
||||||
|
|
||||||
|
def test_segmentation_lab_exposes_structured_surfaces() -> None:
|
||||||
|
lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 'className="workspace-panel ai-lab-shell segmentation-lab-shell"' in lab
|
||||||
|
assert 'className="ai-lab-model-surface"' in lab
|
||||||
|
assert 'aria-label="Segmentation model capabilities"' in lab
|
||||||
|
assert 'className="ai-lab-run-surface"' in lab
|
||||||
|
assert 'aria-label="Segmentation run controls"' in lab
|
||||||
|
assert 'className="ai-lab-state-stack"' in lab
|
||||||
|
assert 'className="ai-lab-results-surface"' in lab
|
||||||
|
assert 'aria-label="Segmentation results"' in lab
|
||||||
|
assert 'className="ai-lab-qa-surface"' in lab
|
||||||
|
assert 'aria-label="Segmentation QA controls and results"' in lab
|
||||||
|
|
||||||
|
|
||||||
|
def test_ai_lab_preserves_existing_detection_and_segmentation_controls() -> None:
|
||||||
|
detection = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
segmentation = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "onRunDetection" in detection
|
||||||
|
assert "onLoadResults" in detection
|
||||||
|
assert "onRunQa" in detection
|
||||||
|
assert "detectionTileManifestPath" in detection
|
||||||
|
assert "Detections loaded: {detectionItems.length}" in detection
|
||||||
|
assert "Compare detections to reference" in detection
|
||||||
|
|
||||||
|
assert "onRunSegmentation" in segmentation
|
||||||
|
assert "onLoadResults" in segmentation
|
||||||
|
assert "onRunQa" in segmentation
|
||||||
|
assert "selectedSegmentationModelConfigured" in segmentation
|
||||||
|
assert "Segmentations loaded: {segmentationItems.length}" in segmentation
|
||||||
|
assert "Compare segmentations to reference" in segmentation
|
||||||
|
|
||||||
|
|
||||||
|
def test_ai_lab_density_css_contracts() -> None:
|
||||||
|
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert ".ai-lab-shell" in css
|
||||||
|
assert ".ai-lab-model-surface" in css
|
||||||
|
assert ".ai-lab-run-surface" in css
|
||||||
|
assert ".ai-lab-results-surface" in css
|
||||||
|
assert ".ai-lab-qa-surface" in css
|
||||||
|
assert ".ai-lab-state-stack" in css
|
||||||
|
assert ".ai-lab-shell .model-list" in css
|
||||||
|
assert ".ai-lab-shell .lab-form-grid" in css
|
||||||
|
assert ".ai-lab-summary-grid" in css
|
||||||
|
assert "grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));" in css
|
||||||
|
assert "grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));" in css
|
||||||
@@ -3175,3 +3175,38 @@ Limitations:
|
|||||||
|
|
||||||
Next recommended pass:
|
Next recommended pass:
|
||||||
- Continue with AI Labs run-form hierarchy and detection/segmentation result density after this pass is deployed and visually checked.
|
- Continue with AI Labs run-form hierarchy and detection/segmentation result density after this pass is deployed and visually checked.
|
||||||
|
|
||||||
|
## Sprint 88 AI Labs density polish (2026-06-20)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Wrapped Detection Lab and Segmentation Lab in shared `ai-lab-shell` workspace shells.
|
||||||
|
- Grouped model registry states into `ai-lab-model-surface` with a shared `ai-lab-state-stack`.
|
||||||
|
- Grouped run controls into `ai-lab-run-surface`.
|
||||||
|
- Grouped persisted detection/segmentation result loading and tables into `ai-lab-results-surface`.
|
||||||
|
- Grouped QA controls and metric summaries into `ai-lab-qa-surface`.
|
||||||
|
- Added shared AI Lab CSS contracts for compact model grids, form grids and summary grids.
|
||||||
|
- Added `backend/tests/test_sprint88_ai_lab_density.py`.
|
||||||
|
- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`.
|
||||||
|
|
||||||
|
Tested:
|
||||||
|
- Red step: `python -m pytest backend/tests/test_sprint88_ai_lab_density.py -q` failed on missing AI Lab shells/surfaces and CSS contracts.
|
||||||
|
- `python -m pytest backend/tests/test_sprint88_ai_lab_density.py backend/tests/test_sprint8c_detection_visualization_qa.py backend/tests/test_sprint9_segmentation_foundation.py backend/tests/test_sprint39_frontend_orchestration_hooks.py backend/tests/test_sprint47_workbench_interaction_smoke.py -q` (`34 passed`)
|
||||||
|
- `cd frontend && npm run typecheck`
|
||||||
|
- `cd frontend && npm run build`
|
||||||
|
- Local browser UI check against `http://127.0.0.1:5174` passed on desktop and mobile: Detection/Segmentation shells and model/run/result/QA surfaces rendered, no horizontal overflow and no console warnings/errors. The local-only screenshot showed expected API 500 states because only the frontend Vite server was running.
|
||||||
|
- First full readiness run caught legacy AI Lab static contract expectations for exact `lab-block` and `result-summary-card` class names. Kept those compatibility anchors while preserving the new AI Lab surfaces.
|
||||||
|
- `python -m pytest backend/tests/test_sprint50_workspace_usability_polish.py backend/tests/test_sprint75_ai_labs_mobile_polish.py backend/tests/test_sprint88_ai_lab_density.py -q` (`8 passed`)
|
||||||
|
- `bash scripts/run_readiness_check.sh` (`299 passed`; frontend typecheck/build passed; Alembic head `202606120900`; live smoke syntax passed)
|
||||||
|
- Tower deploy via `scripts/deploy_tower.ps1` rebuilt and restarted the all-in-one container on `http://192.168.10.150:1202`.
|
||||||
|
- Tower deploy live migration smoke passed with PostGIS `3.6 USE_GEOS=1 USE_PROJ=1 USE_STATS=1` and required runtime schema objects present.
|
||||||
|
- Browser runtime verification passed for frontend, proxied API and icon.
|
||||||
|
- Live browser AI Labs check against `http://192.168.10.150:1202` passed on desktop and mobile: Detection/Segmentation shells and model/run/result/QA surfaces rendered, no horizontal overflow and no console warnings/errors.
|
||||||
|
|
||||||
|
Open:
|
||||||
|
- None known after full readiness, Tower deploy and live browser validation.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
- Frontend AI Labs presentation hierarchy only; no detection/segmentation behavior, API contract, persistence, migration, provider fetching or AI/model changes.
|
||||||
|
|
||||||
|
Next recommended pass:
|
||||||
|
- Continue with export/system final visual consistency or live workflow guidance once this pass is deployed and visually checked.
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Add Map workspace panel hierarchy and layer-control density polish.
|
- [x] Add Map workspace panel hierarchy and layer-control density polish.
|
||||||
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
||||||
- [x] Add Change Detection panel hierarchy and analysis workspace density polish.
|
- [x] Add Change Detection panel hierarchy and analysis workspace density polish.
|
||||||
|
- [x] Add AI Labs Detection/Segmentation hierarchy and result density polish.
|
||||||
|
|
||||||
## Sprint 8 status
|
## Sprint 8 status
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ Raster and vector operation panels use structured group headings, compact helper
|
|||||||
|
|
||||||
QA/QC, exports and AI lab result panels use shared loading, error, empty and ready state cards. This keeps model registry failures, empty histories and result counts visually consistent across the workbench.
|
QA/QC, exports and AI lab result panels use shared loading, error, empty and ready state cards. This keeps model registry failures, empty histories and result counts visually consistent across the workbench.
|
||||||
|
|
||||||
|
Detection Lab and Segmentation Lab now share the same AI workspace hierarchy: model capabilities, run controls, persisted results and QA controls are separated into focused surfaces. Existing run, filter, result loading and QA callbacks remain unchanged, but the screens are denser and easier to scan on desktop and mobile.
|
||||||
|
|
||||||
## 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
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function DetectionLab({
|
|||||||
onRunQa,
|
onRunQa,
|
||||||
}: DetectionLabProps): JSX.Element {
|
}: DetectionLabProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<section className="workspace-panel">
|
<section className="workspace-panel ai-lab-shell detection-lab-shell">
|
||||||
<div className="panel-title-row">
|
<div className="panel-title-row">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Object detection</p>
|
<p className="eyebrow">Object detection</p>
|
||||||
@@ -95,6 +95,15 @@ export function DetectionLab({
|
|||||||
Refresh models
|
Refresh models
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-model-surface" aria-label="Detection model capabilities">
|
||||||
|
<div className="ai-lab-section-header">
|
||||||
|
<div>
|
||||||
|
<h3>Model registry</h3>
|
||||||
|
<p>Backend-reported detector states and limitations.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
{loadingDetectionModels ? (
|
{loadingDetectionModels ? (
|
||||||
<div className="result-state result-state-loading">
|
<div className="result-state result-state-loading">
|
||||||
<strong>Loading detection models.</strong>
|
<strong>Loading detection models.</strong>
|
||||||
@@ -113,6 +122,7 @@ export function DetectionLab({
|
|||||||
<p>Refresh models after the backend is reachable.</p>
|
<p>Refresh models after the backend is reachable.</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
</div>
|
||||||
<ul className="model-list">
|
<ul className="model-list">
|
||||||
{detectionModels.map((model) => (
|
{detectionModels.map((model) => (
|
||||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||||
@@ -128,7 +138,10 @@ export function DetectionLab({
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="lab-block">
|
<div className="lab-block">
|
||||||
|
<div className="ai-lab-run-surface" aria-label="Detection run controls">
|
||||||
<h3>Run detection</h3>
|
<h3>Run detection</h3>
|
||||||
<div className="lab-form-grid">
|
<div className="lab-form-grid">
|
||||||
<label>
|
<label>
|
||||||
@@ -179,6 +192,9 @@ export function DetectionLab({
|
|||||||
Run detection
|
Run detection
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
{detectionRunError ? (
|
{detectionRunError ? (
|
||||||
<div className="result-state result-state-error">
|
<div className="result-state result-state-error">
|
||||||
<strong>Detection run failed.</strong>
|
<strong>Detection run failed.</strong>
|
||||||
@@ -195,9 +211,14 @@ export function DetectionLab({
|
|||||||
{detectionRunResult.error_code ? <p className="error">Code: {detectionRunResult.error_code}</p> : null}
|
{detectionRunResult.error_code ? <p className="error">Code: {detectionRunResult.error_code}</p> : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="lab-block">
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-results-surface" aria-label="Detection results">
|
||||||
<div className="panel-title-row">
|
<div className="panel-title-row">
|
||||||
|
<div>
|
||||||
<h3>Detection results</h3>
|
<h3>Detection results</h3>
|
||||||
|
<p className="muted">Load persisted detections and filter by class or confidence.</p>
|
||||||
|
</div>
|
||||||
<button className="secondary-action" type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
|
<button className="secondary-action" type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
|
||||||
Refresh runs
|
Refresh runs
|
||||||
</button>
|
</button>
|
||||||
@@ -244,10 +265,12 @@ export function DetectionLab({
|
|||||||
<p>Retrieving persisted detections for the selected run.</p>
|
<p>Retrieving persisted detections for the selected run.</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
<div className="result-state result-state-ready">
|
<div className="result-state result-state-ready">
|
||||||
<strong>Detections loaded: {detectionItems.length}</strong>
|
<strong>Detections loaded: {detectionItems.length}</strong>
|
||||||
<p>{selectedDetectionRunId ? 'Loaded from persisted detection records.' : 'Select a detection run before loading results.'}</p>
|
<p>{selectedDetectionRunId ? 'Loaded from persisted detection records.' : 'Select a detection run before loading results.'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{detectionItems.length > 0 ? (
|
{detectionItems.length > 0 ? (
|
||||||
<div className="table-scroll">
|
<div className="table-scroll">
|
||||||
<table>
|
<table>
|
||||||
@@ -273,7 +296,8 @@ export function DetectionLab({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="lab-block">
|
|
||||||
|
<div className="ai-lab-qa-surface" aria-label="Detection QA controls and results">
|
||||||
<h3>Detection QA</h3>
|
<h3>Detection QA</h3>
|
||||||
<label>
|
<label>
|
||||||
Reference dataset
|
Reference dataset
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function SegmentationLab({
|
|||||||
onRunQa,
|
onRunQa,
|
||||||
}: SegmentationLabProps): JSX.Element {
|
}: SegmentationLabProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<section className="workspace-panel">
|
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
|
||||||
<div className="panel-title-row">
|
<div className="panel-title-row">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Polygon segmentation</p>
|
<p className="eyebrow">Polygon segmentation</p>
|
||||||
@@ -95,6 +95,15 @@ export function SegmentationLab({
|
|||||||
Refresh models
|
Refresh models
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-model-surface" aria-label="Segmentation model capabilities">
|
||||||
|
<div className="ai-lab-section-header">
|
||||||
|
<div>
|
||||||
|
<h3>Model registry</h3>
|
||||||
|
<p>Backend-reported segmenter states and limitations.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
{loadingSegmentationModels ? (
|
{loadingSegmentationModels ? (
|
||||||
<div className="result-state result-state-loading">
|
<div className="result-state result-state-loading">
|
||||||
<strong>Loading segmentation models.</strong>
|
<strong>Loading segmentation models.</strong>
|
||||||
@@ -113,6 +122,7 @@ export function SegmentationLab({
|
|||||||
<p>Refresh models after the backend is reachable.</p>
|
<p>Refresh models after the backend is reachable.</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
</div>
|
||||||
<ul className="model-list">
|
<ul className="model-list">
|
||||||
{segmentationModels.map((model) => (
|
{segmentationModels.map((model) => (
|
||||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||||
@@ -128,7 +138,10 @@ export function SegmentationLab({
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="lab-block">
|
<div className="lab-block">
|
||||||
|
<div className="ai-lab-run-surface" aria-label="Segmentation run controls">
|
||||||
<h3>Run segmentation</h3>
|
<h3>Run segmentation</h3>
|
||||||
<div className="lab-form-grid">
|
<div className="lab-form-grid">
|
||||||
<label>
|
<label>
|
||||||
@@ -173,6 +186,9 @@ export function SegmentationLab({
|
|||||||
Run segmentation
|
Run segmentation
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
{!selectedSegmentationModelConfigured ? (
|
{!selectedSegmentationModelConfigured ? (
|
||||||
<div className="result-state result-state-empty">
|
<div className="result-state result-state-empty">
|
||||||
<strong>Segmentation model is not ready.</strong>
|
<strong>Segmentation model is not ready.</strong>
|
||||||
@@ -195,9 +211,14 @@ export function SegmentationLab({
|
|||||||
{segmentationRunResult.error_code ? <p className="error">Code: {segmentationRunResult.error_code}</p> : null}
|
{segmentationRunResult.error_code ? <p className="error">Code: {segmentationRunResult.error_code}</p> : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="lab-block">
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-lab-results-surface" aria-label="Segmentation results">
|
||||||
<div className="panel-title-row">
|
<div className="panel-title-row">
|
||||||
|
<div>
|
||||||
<h3>Segmentation results</h3>
|
<h3>Segmentation results</h3>
|
||||||
|
<p className="muted">Load persisted segmentation polygons and filter by class or confidence.</p>
|
||||||
|
</div>
|
||||||
<button className="secondary-action" type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
|
<button className="secondary-action" type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
|
||||||
Refresh runs
|
Refresh runs
|
||||||
</button>
|
</button>
|
||||||
@@ -244,10 +265,12 @@ export function SegmentationLab({
|
|||||||
<p>Retrieving persisted segmentation polygons for the selected run.</p>
|
<p>Retrieving persisted segmentation polygons for the selected run.</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
<div className="ai-lab-state-stack">
|
||||||
<div className="result-state result-state-ready">
|
<div className="result-state result-state-ready">
|
||||||
<strong>Segmentations loaded: {segmentationItems.length}</strong>
|
<strong>Segmentations loaded: {segmentationItems.length}</strong>
|
||||||
<p>{selectedSegmentationRunId ? 'Loaded from persisted segmentation records.' : 'Select a segmentation run before loading results.'}</p>
|
<p>{selectedSegmentationRunId ? 'Loaded from persisted segmentation records.' : 'Select a segmentation run before loading results.'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{segmentationItems.length > 0 ? (
|
{segmentationItems.length > 0 ? (
|
||||||
<div className="table-scroll">
|
<div className="table-scroll">
|
||||||
<table>
|
<table>
|
||||||
@@ -277,7 +300,8 @@ export function SegmentationLab({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="lab-block">
|
|
||||||
|
<div className="ai-lab-qa-surface" aria-label="Segmentation QA controls and results">
|
||||||
<h3>Segmentation QA</h3>
|
<h3>Segmentation QA</h3>
|
||||||
<label>
|
<label>
|
||||||
Reference dataset
|
Reference dataset
|
||||||
|
|||||||
@@ -2295,20 +2295,115 @@ button.entity-card {
|
|||||||
margin-top: 0.85rem;
|
margin-top: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.85rem;
|
||||||
|
min-width: 0;
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell > .lab-block {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-grid-ai .workspace-panel {
|
.workspace-grid-ai .workspace-panel {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-lab-model-surface,
|
||||||
|
.ai-lab-run-surface,
|
||||||
|
.ai-lab-results-surface,
|
||||||
|
.ai-lab-qa-surface {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.68rem;
|
||||||
|
min-width: 0;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.78rem;
|
||||||
|
background: linear-gradient(180deg, #ffffff, #f7fbf8);
|
||||||
|
box-shadow: 0 1px 0 rgba(19, 32, 24, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-results-surface {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-section-header {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-section-header h3,
|
||||||
|
.ai-lab-run-surface h3,
|
||||||
|
.ai-lab-qa-surface h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-section-header p,
|
||||||
|
.ai-lab-results-surface .muted {
|
||||||
|
margin: 0.16rem 0 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.84rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-state-stack {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell .model-list {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell .model-card {
|
||||||
|
align-content: start;
|
||||||
|
padding: 0.68rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell .lab-form-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-summary-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-shell .result-summary-card {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-lab-run-surface label,
|
||||||
|
.ai-lab-results-surface label,
|
||||||
|
.ai-lab-qa-surface label,
|
||||||
.lab-block label {
|
.lab-block label {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-lab-run-surface input,
|
||||||
|
.ai-lab-run-surface select,
|
||||||
|
.ai-lab-results-surface input,
|
||||||
|
.ai-lab-results-surface select,
|
||||||
|
.ai-lab-qa-surface input,
|
||||||
|
.ai-lab-qa-surface select,
|
||||||
.lab-block input,
|
.lab-block input,
|
||||||
.lab-block select {
|
.lab-block select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-lab-run-surface .primary-action,
|
||||||
|
.ai-lab-results-surface .primary-action,
|
||||||
|
.ai-lab-qa-surface .primary-action,
|
||||||
.lab-block .primary-action {
|
.lab-block .primary-action {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user