feat(agent): refine ready state actions
This commit is contained in:
@@ -55,6 +55,14 @@ describe("toTrustedMapAction", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("converts EPSG:3857 zoom coordinates", () => {
|
||||
const action = toTrustedMapAction("zoom_to_map", { x: 13046644.321, y: 4736005.854, source_crs: "EPSG:3857" });
|
||||
|
||||
expect(action?.type).toBe("zoom_to_map");
|
||||
expect(action?.type === "zoom_to_map" ? action.center[0] : undefined).toBeCloseTo(117.2, 3);
|
||||
expect(action?.type === "zoom_to_map" ? action.center[1] : undefined).toBeCloseTo(39.1, 3);
|
||||
});
|
||||
|
||||
it("normalizes legacy locate tool actions", () => {
|
||||
expect(toTrustedMapAction("locate_pipes", { pipe_ids: "P1,P2" })).toEqual({
|
||||
type: "locate_features",
|
||||
|
||||
@@ -9,6 +9,7 @@ export type TrustedMapAction =
|
||||
type: "zoom_to_map";
|
||||
center: [number, number];
|
||||
zoom?: number;
|
||||
durationMs?: number;
|
||||
fallbackText?: string;
|
||||
}
|
||||
| {
|
||||
@@ -49,6 +50,7 @@ export function toTrustedMapAction(action: string, params: unknown, fallbackText
|
||||
}
|
||||
|
||||
const zoom = numberValue(params.zoom);
|
||||
const durationMs = numberValue(params.duration_ms ?? params.durationMs);
|
||||
if (center[0] < -180 || center[0] > 180 || center[1] < -90 || center[1] > 90 ||
|
||||
(zoom !== undefined && (zoom < 0 || zoom > 24))) return null;
|
||||
|
||||
@@ -56,6 +58,7 @@ export function toTrustedMapAction(action: string, params: unknown, fallbackText
|
||||
type: "zoom_to_map",
|
||||
center,
|
||||
zoom,
|
||||
...(durationMs === undefined ? {} : { durationMs }),
|
||||
fallbackText
|
||||
};
|
||||
}
|
||||
@@ -91,22 +94,39 @@ export function toTrustedMapAction(action: string, params: unknown, fallbackText
|
||||
}
|
||||
|
||||
function parseCenter(params: Record<string, unknown>): [number, number] | null {
|
||||
const sourceCrs = params.source_crs ?? params.sourceCrs;
|
||||
|
||||
if (Array.isArray(params.center) && params.center.length >= 2) {
|
||||
const lng = numberValue(params.center[0]);
|
||||
const lat = numberValue(params.center[1]);
|
||||
return lng === undefined || lat === undefined ? null : [lng, lat];
|
||||
return lng === undefined || lat === undefined ? null : normalizeCenter(lng, lat, sourceCrs);
|
||||
}
|
||||
|
||||
const rawCoordinate = params.coordinate ?? params.coordinates;
|
||||
if (Array.isArray(rawCoordinate) && rawCoordinate.length >= 2) {
|
||||
const lng = numberValue(rawCoordinate[0]);
|
||||
const lat = numberValue(rawCoordinate[1]);
|
||||
return lng === undefined || lat === undefined ? null : [lng, lat];
|
||||
return lng === undefined || lat === undefined ? null : normalizeCenter(lng, lat, sourceCrs);
|
||||
}
|
||||
|
||||
const lng = numberValue(params.lng ?? params.lon ?? params.longitude ?? params.x);
|
||||
const lat = numberValue(params.lat ?? params.latitude ?? params.y);
|
||||
return lng === undefined || lat === undefined ? null : [lng, lat];
|
||||
return lng === undefined || lat === undefined ? null : normalizeCenter(lng, lat, sourceCrs);
|
||||
}
|
||||
|
||||
function normalizeCenter(x: number, y: number, sourceCrs: unknown): [number, number] {
|
||||
if (sourceCrs === "EPSG:3857") {
|
||||
return webMercatorToLngLat(x, y);
|
||||
}
|
||||
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
function webMercatorToLngLat(x: number, y: number): [number, number] {
|
||||
const lng = (x / WEB_MERCATOR_RADIUS) * (180 / Math.PI);
|
||||
const lat = (2 * Math.atan(Math.exp(y / WEB_MERCATOR_RADIUS)) - Math.PI / 2) * (180 / Math.PI);
|
||||
|
||||
return [lng, lat];
|
||||
}
|
||||
|
||||
function stringValue(value: unknown) {
|
||||
@@ -145,6 +165,7 @@ const LEGACY_LOCATE_ACTION_LAYERS: Record<string, string> = {
|
||||
|
||||
const RESULT_REF_PATTERN = /^res-[A-Za-z0-9_-]{8,128}$/;
|
||||
const ALLOWED_LAYER_IDS = new Set(["junctions", "pipes"]);
|
||||
const WEB_MERCATOR_RADIUS = 6378137;
|
||||
|
||||
const LOCATE_ID_PARAM_KEYS = [
|
||||
"feature_ids",
|
||||
|
||||
@@ -5,10 +5,7 @@ import type { UIRegistry } from "./types";
|
||||
const registry: UIRegistry = {
|
||||
schema_version: "agent-ui-registry@1",
|
||||
chart_grammars: ["echarts-safe-subset"],
|
||||
components: [
|
||||
{ id: "HistoryPanel", supportedSurfaces: ["side_panel", "canvas"] },
|
||||
{ id: "ScadaPanel", supportedSurfaces: ["side_panel", "canvas"] }
|
||||
],
|
||||
components: [{ id: "HistoryPanel", supportedSurfaces: ["side_panel", "canvas"] }],
|
||||
actions: [{ id: "zoom_to_map", supportedSurfaces: ["map_overlay"] }]
|
||||
};
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import type { UIEnvelope, UIEnvelopePayload, UIRegistry, UISurface } from "./typ
|
||||
const surfaces = new Set<UISurface>(["chat_inline", "side_panel", "canvas", "map_overlay"]);
|
||||
const MAX_ID_LENGTH = 128;
|
||||
const LOCAL_COMPONENT_SURFACES = new Map<string, UISurface[]>([
|
||||
["HistoryPanel", ["side_panel", "canvas"]],
|
||||
["ScadaPanel", ["side_panel", "canvas"]]
|
||||
["HistoryPanel", ["side_panel", "canvas"]]
|
||||
]);
|
||||
const LOCAL_ACTION_SURFACES = new Map<string, UISurface[]>([
|
||||
["locate_features", ["map_overlay"]],
|
||||
@@ -183,11 +182,6 @@ function isValidComponentProps(component: string, props: unknown) {
|
||||
items.every((item) => Array.isArray(item) && item.length === 2 && item.every((part) => boundedString(part, 128))) &&
|
||||
(props.data_type === "realtime" || props.data_type === "scheme" || props.data_type === "none");
|
||||
}
|
||||
if (component === "ScadaPanel") {
|
||||
if (!hasOnlyKeys(props, ["reason", "device_ids", "device_id", "start_time", "end_time"])) return false;
|
||||
const ids = Array.isArray(props.device_ids) ? props.device_ids : props.device_id ? [props.device_id] : [];
|
||||
return ids.length > 0 && ids.length <= 100 && ids.every((id) => Boolean(boundedString(id, 128)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user