feat: refine workbench visuals and map controls

Unify the Agent history extension with the header acrylic surface, preserve the full conversation body, and consolidate shared control and status styling.

Restore map flow and SCADA controller behavior, remove obsolete rendering paths, and extend regression coverage. Button press coverage now releases outside the target so state assertions cannot accidentally toggle the control.
This commit is contained in:
2026-07-28 16:39:36 +08:00
parent ade88b4fbf
commit f4318a9b9c
96 changed files with 5480 additions and 1875 deletions
+24 -14
View File
@@ -2,14 +2,13 @@ import type { StyleSpecification, VectorSourceSpecification } from "maplibre-gl"
import { GEOSERVER_WORKSPACE, MAP_URL } from "./geoserver-config";
import { MAP_STYLE_TOKENS } from "./map-colors";
import {
SCADA_ANALYSIS_SOURCE_ID,
createEmptyScadaAnalysisCollection
} from "./scada-analysis";
export const WATER_NETWORK_GLOBAL_VIEW = {
bbox3857: [
13508801.930066336,
3608163.3499832638,
13555650.638648884,
3633685.137885742
]
bbox3857: [13508801.930066336, 3608163.3499832638, 13555650.638648884, 3633685.137885742]
} as const;
export const SOURCE_LAYERS = {
@@ -102,21 +101,27 @@ export const SUPPLY_LAYER_CATALOG = [
}
] as const satisfies readonly SupplyLayerCatalogItem[];
export const AVAILABLE_WATER_NETWORK_SOURCE_IDS = SUPPLY_LAYER_CATALOG
.filter((layer) => layer.available)
.map((layer) => layer.id) as Extract<WaterNetworkSourceId, "pipes" | "junctions" | "valves" | "reservoirs" | "scada">[];
export const AVAILABLE_WATER_NETWORK_SOURCE_IDS = SUPPLY_LAYER_CATALOG.filter(
(layer) => layer.available
).map((layer) => layer.id) as Extract<
WaterNetworkSourceId,
"pipes" | "junctions" | "valves" | "reservoirs" | "scada"
>[];
export type AvailableWaterNetworkSourceId = (typeof AVAILABLE_WATER_NETWORK_SOURCE_IDS)[number];
export function isAvailableWaterNetworkSourceId(value: string): value is AvailableWaterNetworkSourceId {
export function isAvailableWaterNetworkSourceId(
value: string
): value is AvailableWaterNetworkSourceId {
return (AVAILABLE_WATER_NETWORK_SOURCE_IDS as readonly string[]).includes(value);
}
export function createWaterNetworkSources() {
return Object.fromEntries(
SUPPLY_LAYER_CATALOG
.filter((layer) => layer.available)
.map((layer) => [layer.id, createGeoServerVectorSource(layer.sourceLayer)])
SUPPLY_LAYER_CATALOG.filter((layer) => layer.available).map((layer) => [
layer.id,
createGeoServerVectorSource(layer.sourceLayer)
])
) as Record<AvailableWaterNetworkSourceId, VectorSourceSpecification>;
}
@@ -135,7 +140,12 @@ function createGeoServerVectorSource(
}
export function createBaseStyle(mapboxToken?: string): StyleSpecification {
const sources: StyleSpecification["sources"] = {};
const sources: StyleSpecification["sources"] = {
[SCADA_ANALYSIS_SOURCE_ID]: {
type: "geojson",
data: createEmptyScadaAnalysisCollection()
}
};
const layers: StyleSpecification["layers"] = [
{
id: "background",