Polish AI Labs density
This commit is contained in:
@@ -85,7 +85,7 @@ export function DetectionLab({
|
||||
onRunQa,
|
||||
}: DetectionLabProps): JSX.Element {
|
||||
return (
|
||||
<section className="workspace-panel">
|
||||
<section className="workspace-panel ai-lab-shell detection-lab-shell">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Object detection</p>
|
||||
@@ -95,109 +95,130 @@ export function DetectionLab({
|
||||
Refresh models
|
||||
</button>
|
||||
</div>
|
||||
{loadingDetectionModels ? (
|
||||
<div className="result-state result-state-loading">
|
||||
<strong>Loading detection models.</strong>
|
||||
<p>Checking backend model registry availability.</p>
|
||||
|
||||
<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>
|
||||
) : null}
|
||||
{detectionModelError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Detection model registry unavailable.</strong>
|
||||
<p>{detectionModelError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionModels.length === 0 && !loadingDetectionModels ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No detection models reported by backend.</strong>
|
||||
<p>Refresh models after the backend is reachable.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="model-list">
|
||||
{detectionModels.map((model) => (
|
||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||
<strong>{model.display_name}</strong>
|
||||
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.status}</span>
|
||||
<div className="entity-meta">
|
||||
<span>{model.model_id}</span>
|
||||
<span>{model.framework}</span>
|
||||
<span>{model.task_type}</span>
|
||||
<div className="ai-lab-state-stack">
|
||||
{loadingDetectionModels ? (
|
||||
<div className="result-state result-state-loading">
|
||||
<strong>Loading detection models.</strong>
|
||||
<p>Checking backend model registry availability.</p>
|
||||
</div>
|
||||
<p className="muted">classes: {model.supported_classes.join(', ')}</p>
|
||||
<p className="muted">{model.limitation_message}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="lab-block">
|
||||
<h3>Run detection</h3>
|
||||
<div className="lab-form-grid">
|
||||
<label>
|
||||
Raster dataset
|
||||
<select value={selectedDetectionDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
|
||||
<option value="">Select raster dataset</option>
|
||||
{rasterDatasets.map((dataset) => (
|
||||
<option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Model
|
||||
<select value={selectedDetectionModelId} onChange={(event) => onSelectModel(event.target.value)}>
|
||||
{detectionModels.map((model) => (
|
||||
<option key={model.model_id} value={model.model_id}>
|
||||
{model.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Min confidence
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={detectionConfidenceThreshold}
|
||||
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
{detectionModelError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Detection model registry unavailable.</strong>
|
||||
<p>{detectionModelError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionModels.length === 0 && !loadingDetectionModels ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No detection models reported by backend.</strong>
|
||||
<p>Refresh models after the backend is reachable.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{selectedDetectionModelId === 'yolo-configured' ? (
|
||||
<label>
|
||||
Tile manifest
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Raster tile manifest path"
|
||||
value={detectionTileManifestPath}
|
||||
onChange={(event) => onSetTileManifestPath(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
<button className="primary-action" type="button" onClick={onRunDetection} disabled={runningDetection || !selectedProjectId || rasterDatasets.length === 0}>
|
||||
Run detection
|
||||
</button>
|
||||
<ul className="model-list">
|
||||
{detectionModels.map((model) => (
|
||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||
<strong>{model.display_name}</strong>
|
||||
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.status}</span>
|
||||
<div className="entity-meta">
|
||||
<span>{model.model_id}</span>
|
||||
<span>{model.framework}</span>
|
||||
<span>{model.task_type}</span>
|
||||
</div>
|
||||
<p className="muted">classes: {model.supported_classes.join(', ')}</p>
|
||||
<p className="muted">{model.limitation_message}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{detectionRunError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Detection run failed.</strong>
|
||||
<p>{detectionRunError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionRunResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {detectionRunResult.status}</p>
|
||||
<p>Message: {detectionRunResult.message}</p>
|
||||
<p>Analysis run: {detectionRunResult.analysis_run_id}</p>
|
||||
<p>Job: {detectionRunResult.job_id}</p>
|
||||
<p>Detections: {detectionRunResult.detection_count}</p>
|
||||
{detectionRunResult.error_code ? <p className="error">Code: {detectionRunResult.error_code}</p> : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="lab-block">
|
||||
<div className="ai-lab-run-surface" aria-label="Detection run controls">
|
||||
<h3>Run detection</h3>
|
||||
<div className="lab-form-grid">
|
||||
<label>
|
||||
Raster dataset
|
||||
<select value={selectedDetectionDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
|
||||
<option value="">Select raster dataset</option>
|
||||
{rasterDatasets.map((dataset) => (
|
||||
<option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Model
|
||||
<select value={selectedDetectionModelId} onChange={(event) => onSelectModel(event.target.value)}>
|
||||
{detectionModels.map((model) => (
|
||||
<option key={model.model_id} value={model.model_id}>
|
||||
{model.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Min confidence
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={detectionConfidenceThreshold}
|
||||
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{selectedDetectionModelId === 'yolo-configured' ? (
|
||||
<label>
|
||||
Tile manifest
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Raster tile manifest path"
|
||||
value={detectionTileManifestPath}
|
||||
onChange={(event) => onSetTileManifestPath(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
<button className="primary-action" type="button" onClick={onRunDetection} disabled={runningDetection || !selectedProjectId || rasterDatasets.length === 0}>
|
||||
Run detection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-state-stack">
|
||||
{detectionRunError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Detection run failed.</strong>
|
||||
<p>{detectionRunError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionRunResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {detectionRunResult.status}</p>
|
||||
<p>Message: {detectionRunResult.message}</p>
|
||||
<p>Analysis run: {detectionRunResult.analysis_run_id}</p>
|
||||
<p>Job: {detectionRunResult.job_id}</p>
|
||||
<p>Detections: {detectionRunResult.detection_count}</p>
|
||||
{detectionRunResult.error_code ? <p className="error">Code: {detectionRunResult.error_code}</p> : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-results-surface" aria-label="Detection results">
|
||||
<div className="panel-title-row">
|
||||
<h3>Detection results</h3>
|
||||
<div>
|
||||
<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}>
|
||||
Refresh runs
|
||||
</button>
|
||||
@@ -244,9 +265,11 @@ export function DetectionLab({
|
||||
<p>Retrieving persisted detections for the selected run.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="result-state result-state-ready">
|
||||
<strong>Detections loaded: {detectionItems.length}</strong>
|
||||
<p>{selectedDetectionRunId ? 'Loaded from persisted detection records.' : 'Select a detection run before loading results.'}</p>
|
||||
<div className="ai-lab-state-stack">
|
||||
<div className="result-state result-state-ready">
|
||||
<strong>Detections loaded: {detectionItems.length}</strong>
|
||||
<p>{selectedDetectionRunId ? 'Loaded from persisted detection records.' : 'Select a detection run before loading results.'}</p>
|
||||
</div>
|
||||
</div>
|
||||
{detectionItems.length > 0 ? (
|
||||
<div className="table-scroll">
|
||||
@@ -273,7 +296,8 @@ export function DetectionLab({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="lab-block">
|
||||
|
||||
<div className="ai-lab-qa-surface" aria-label="Detection QA controls and results">
|
||||
<h3>Detection QA</h3>
|
||||
<label>
|
||||
Reference dataset
|
||||
@@ -292,10 +316,10 @@ export function DetectionLab({
|
||||
{detectionQaError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Detection QA failed.</strong>
|
||||
<p>{detectionQaError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionQaResult ? (
|
||||
<p>{detectionQaError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{detectionQaResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {detectionQaResult.status}</p>
|
||||
<p>Quality check: {detectionQaResult.quality_check_id}</p>
|
||||
|
||||
@@ -85,7 +85,7 @@ export function SegmentationLab({
|
||||
onRunQa,
|
||||
}: SegmentationLabProps): JSX.Element {
|
||||
return (
|
||||
<section className="workspace-panel">
|
||||
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Polygon segmentation</p>
|
||||
@@ -95,109 +95,130 @@ export function SegmentationLab({
|
||||
Refresh models
|
||||
</button>
|
||||
</div>
|
||||
{loadingSegmentationModels ? (
|
||||
<div className="result-state result-state-loading">
|
||||
<strong>Loading segmentation models.</strong>
|
||||
<p>Checking backend model registry availability.</p>
|
||||
|
||||
<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>
|
||||
) : null}
|
||||
{segmentationModelError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Segmentation model registry unavailable.</strong>
|
||||
<p>{segmentationModelError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationModels.length === 0 && !loadingSegmentationModels ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No segmentation models reported by backend.</strong>
|
||||
<p>Refresh models after the backend is reachable.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="model-list">
|
||||
{segmentationModels.map((model) => (
|
||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||
<strong>{model.display_name}</strong>
|
||||
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.status}</span>
|
||||
<div className="entity-meta">
|
||||
<span>{model.model_id}</span>
|
||||
<span>{model.framework}</span>
|
||||
<span>{model.task_type}</span>
|
||||
<div className="ai-lab-state-stack">
|
||||
{loadingSegmentationModels ? (
|
||||
<div className="result-state result-state-loading">
|
||||
<strong>Loading segmentation models.</strong>
|
||||
<p>Checking backend model registry availability.</p>
|
||||
</div>
|
||||
<p className="muted">classes: {model.supported_classes.join(', ')}</p>
|
||||
<p className="muted">{model.limitation_message}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="lab-block">
|
||||
<h3>Run segmentation</h3>
|
||||
<div className="lab-form-grid">
|
||||
<label>
|
||||
Raster dataset
|
||||
<select value={selectedSegmentationDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
|
||||
<option value="">Select raster dataset</option>
|
||||
{rasterDatasets.map((dataset) => (
|
||||
<option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Model
|
||||
<select value={selectedSegmentationModelId} onChange={(event) => onSelectModel(event.target.value)}>
|
||||
{segmentationModels.map((model) => (
|
||||
<option key={model.model_id} value={model.model_id}>
|
||||
{model.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Min confidence
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={segmentationConfidenceThreshold}
|
||||
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
{segmentationModelError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Segmentation model registry unavailable.</strong>
|
||||
<p>{segmentationModelError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationModels.length === 0 && !loadingSegmentationModels ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No segmentation models reported by backend.</strong>
|
||||
<p>Refresh models after the backend is reachable.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
className="primary-action"
|
||||
type="button"
|
||||
onClick={onRunSegmentation}
|
||||
disabled={runningSegmentation || !selectedProjectId || rasterDatasets.length === 0 || !selectedSegmentationModelConfigured}
|
||||
>
|
||||
Run segmentation
|
||||
</button>
|
||||
<ul className="model-list">
|
||||
{segmentationModels.map((model) => (
|
||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||
<strong>{model.display_name}</strong>
|
||||
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.status}</span>
|
||||
<div className="entity-meta">
|
||||
<span>{model.model_id}</span>
|
||||
<span>{model.framework}</span>
|
||||
<span>{model.task_type}</span>
|
||||
</div>
|
||||
<p className="muted">classes: {model.supported_classes.join(', ')}</p>
|
||||
<p className="muted">{model.limitation_message}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{!selectedSegmentationModelConfigured ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>Segmentation model is not ready.</strong>
|
||||
<p>{selectedSegmentationModelLimitation ?? 'Select a configured segmentation model'}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Segmentation run failed.</strong>
|
||||
<p>{segmentationRunError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {segmentationRunResult.status}</p>
|
||||
<p>Message: {segmentationRunResult.message}</p>
|
||||
<p>Analysis run: {segmentationRunResult.analysis_run_id}</p>
|
||||
<p>Job: {segmentationRunResult.job_id}</p>
|
||||
<p>Segmentations: {segmentationRunResult.segmentation_count}</p>
|
||||
{segmentationRunResult.error_code ? <p className="error">Code: {segmentationRunResult.error_code}</p> : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="lab-block">
|
||||
<div className="ai-lab-run-surface" aria-label="Segmentation run controls">
|
||||
<h3>Run segmentation</h3>
|
||||
<div className="lab-form-grid">
|
||||
<label>
|
||||
Raster dataset
|
||||
<select value={selectedSegmentationDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
|
||||
<option value="">Select raster dataset</option>
|
||||
{rasterDatasets.map((dataset) => (
|
||||
<option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Model
|
||||
<select value={selectedSegmentationModelId} onChange={(event) => onSelectModel(event.target.value)}>
|
||||
{segmentationModels.map((model) => (
|
||||
<option key={model.model_id} value={model.model_id}>
|
||||
{model.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Min confidence
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={segmentationConfidenceThreshold}
|
||||
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
className="primary-action"
|
||||
type="button"
|
||||
onClick={onRunSegmentation}
|
||||
disabled={runningSegmentation || !selectedProjectId || rasterDatasets.length === 0 || !selectedSegmentationModelConfigured}
|
||||
>
|
||||
Run segmentation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-state-stack">
|
||||
{!selectedSegmentationModelConfigured ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>Segmentation model is not ready.</strong>
|
||||
<p>{selectedSegmentationModelLimitation ?? 'Select a configured segmentation model'}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>Segmentation run failed.</strong>
|
||||
<p>{segmentationRunError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {segmentationRunResult.status}</p>
|
||||
<p>Message: {segmentationRunResult.message}</p>
|
||||
<p>Analysis run: {segmentationRunResult.analysis_run_id}</p>
|
||||
<p>Job: {segmentationRunResult.job_id}</p>
|
||||
<p>Segmentations: {segmentationRunResult.segmentation_count}</p>
|
||||
{segmentationRunResult.error_code ? <p className="error">Code: {segmentationRunResult.error_code}</p> : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-results-surface" aria-label="Segmentation results">
|
||||
<div className="panel-title-row">
|
||||
<h3>Segmentation results</h3>
|
||||
<div>
|
||||
<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}>
|
||||
Refresh runs
|
||||
</button>
|
||||
@@ -244,9 +265,11 @@ export function SegmentationLab({
|
||||
<p>Retrieving persisted segmentation polygons for the selected run.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="result-state result-state-ready">
|
||||
<strong>Segmentations loaded: {segmentationItems.length}</strong>
|
||||
<p>{selectedSegmentationRunId ? 'Loaded from persisted segmentation records.' : 'Select a segmentation run before loading results.'}</p>
|
||||
<div className="ai-lab-state-stack">
|
||||
<div className="result-state result-state-ready">
|
||||
<strong>Segmentations loaded: {segmentationItems.length}</strong>
|
||||
<p>{selectedSegmentationRunId ? 'Loaded from persisted segmentation records.' : 'Select a segmentation run before loading results.'}</p>
|
||||
</div>
|
||||
</div>
|
||||
{segmentationItems.length > 0 ? (
|
||||
<div className="table-scroll">
|
||||
@@ -277,7 +300,8 @@ export function SegmentationLab({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="lab-block">
|
||||
|
||||
<div className="ai-lab-qa-surface" aria-label="Segmentation QA controls and results">
|
||||
<h3>Segmentation QA</h3>
|
||||
<label>
|
||||
Reference dataset
|
||||
|
||||
@@ -2295,20 +2295,115 @@ button.entity-card {
|
||||
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 {
|
||||
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 {
|
||||
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 select {
|
||||
width: 100%;
|
||||
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 {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user