export type PageTemplateCategory = "map" | "assistant" | "analytics" | "monitoring" | "administration" | "workspace"; export type PageTemplateFeature = { id: string; label: string; description: string; defaultVisible: boolean; required?: boolean; }; export type PageTemplateSlotKind = "entity-stream" | "trace-stream" | "route-stream" | "zone-stream" | "selection" | "command" | "assistant"; export type PageTemplateSlot = { id: string; kind: PageTemplateSlotKind; label: string; description: string; required: boolean; multiple: boolean; }; export type PageTemplateAction = { id: string; label: string; command: string; feature?: string; }; export type PageTemplateDefinition = { schemaVersion: "0.1.0"; id: string; version: string; title: string; description: string; category: PageTemplateCategory; page: { id: string; title: string; path: string; navigationLabel: string; }; features: PageTemplateFeature[]; slots: PageTemplateSlot[]; actions: PageTemplateAction[]; }; export const mapPageTemplate = { schemaVersion: "0.1.0", id: "map", version: "0.1.0", title: "Map Page", description: "Full-map NODE.DC page with fixed system actions, Inspector, Toolbar and typed spatial data slots.", category: "map", page: { id: "map", title: "Map", path: "/", navigationLabel: "Map", }, features: [ { id: "inspector", label: "Inspector", description: "Canonical draggable Glass Inspector for map settings and selected entities.", defaultVisible: true, required: true }, { id: "toolbar", label: "Toolbar", description: "Canonical positional Toolbar with template-owned actions.", defaultVisible: true }, { id: "assistant", label: "Assistant", description: "Floating NODE.DC assistant entry point and overlay slot.", defaultVisible: false }, ], slots: [ { id: "points", kind: "entity-stream", label: "Points", description: "Live point entities with position and semantic state.", required: false, multiple: true }, { id: "traces", kind: "trace-stream", label: "Traces", description: "Time-ordered entity traces.", required: false, multiple: true }, { id: "routes", kind: "route-stream", label: "Routes", description: "Planned or computed routes.", required: false, multiple: true }, { id: "zones", kind: "zone-stream", label: "Zones", description: "Polygons, geofences and operational areas.", required: false, multiple: true }, { id: "selection", kind: "selection", label: "Selection", description: "Selected map entity and contextual state.", required: false, multiple: false }, { id: "commands", kind: "command", label: "Commands", description: "Capability-backed actions for selected entities.", required: false, multiple: true }, { id: "assistant", kind: "assistant", label: "Assistant context", description: "Context exposed to the assistant overlay.", required: false, multiple: false }, ], actions: [ { id: "open-inspector", label: "Inspector", command: "map.inspector.open", feature: "inspector" }, { id: "toggle-toolbar", label: "Toolbar", command: "map.toolbar.toggle", feature: "toolbar" }, { id: "open-assistant", label: "Assistant", command: "assistant.open", feature: "assistant" }, ], } as const satisfies PageTemplateDefinition; export const pageTemplates = [mapPageTemplate] as const satisfies readonly PageTemplateDefinition[]; export function getPageTemplate(templateId: string, version?: string) { return pageTemplates.find((template) => template.id === templateId && (!version || template.version === version)); } export function createTemplateFeatures(template: PageTemplateDefinition) { return Object.fromEntries(template.features.map((feature) => [feature.id, feature.required ? true : feature.defaultVisible])); }