(function () { "use strict"; const pageDefinitions = [ { index: 0, label: "整车联合数字孪生", shortLabel: "数字孪生", target: ".joint-twin-shell" }, { index: 1, label: "决策轨迹与校验", shortLabel: "决策校验", target: "#panel-trace" }, { index: 2, label: "指令配置与硬件映射", shortLabel: "配置映射", target: "#panel-config" }, { index: 3, label: "CAN 编译计划", shortLabel: "CAN 计划", target: "#panel-plan" }, { index: 4, label: "执行结果与硬件回读", shortLabel: "执行回读", target: "#panel-result" }, ]; const $ = (selector, root = document) => root.querySelector(selector); const $$ = (selector, root = document) => [...root.querySelectorAll(selector)]; const ui = { controlMode: sessionStorage.getItem("cognitive-agent.control-mode") === "gesture" ? "gesture" : "natural", activeModule: "self-orchestration", activePage: 0, sidebarCollapsed: sessionStorage.getItem("cognitive-agent.sidebar-collapsed") === "true", }; const dropdownControls = new Map(); let reasoningPickerControl = null; function make(tag, className, html) { const node = document.createElement(tag); if (className) node.className = className; if (html != null) node.innerHTML = html; return node; } function escapeHtml(value) { return String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function closeCustomDropdowns(except = null) { dropdownControls.forEach((control) => { if (control !== except) control.close(); }); } function closeReasoningPicker() { reasoningPickerControl?.close(); } function installReasoningPicker(reasoningField, originalForm) { const composer = $(".command-composer", originalForm); if (!composer || !reasoningField) return; reasoningField.classList.add("v2-reasoning-field"); reasoningField.hidden = true; const trigger = make("button", "v2-reasoning-trigger", ` 模型智能 均衡 `); trigger.id = "v2ReasoningTrigger"; trigger.type = "button"; trigger.setAttribute("aria-haspopup", "listbox"); trigger.setAttribute("aria-expanded", "false"); trigger.setAttribute("aria-controls", "reasoningField"); trigger.setAttribute("aria-label", "选择模型智能程度,当前均衡"); $(".reasoning-segmented", reasoningField)?.setAttribute("role", "listbox"); const options = () => $$('[data-reasoning-effort]', reasoningField); const place = () => { if (reasoningField.hidden) return; const triggerBox = trigger.getBoundingClientRect(); const width = Math.min(320, Math.max(250, window.innerWidth - 24)); const left = Math.min( Math.max(12, triggerBox.left), Math.max(12, window.innerWidth - width - 12), ); reasoningField.style.width = `${width}px`; reasoningField.style.left = `${left}px`; const height = reasoningField.offsetHeight; const above = triggerBox.top - height - 8; const top = above >= 12 ? above : Math.min(triggerBox.bottom + 8, window.innerHeight - height - 12); reasoningField.style.top = `${Math.max(12, top)}px`; }; const sync = () => { const selected = options().find((button) => button.classList.contains("active")); const label = selected?.querySelector("strong")?.textContent?.trim() || "均衡"; const unavailable = options().length > 0 && options().every((button) => button.disabled); $("#v2ReasoningValue", trigger).textContent = label; trigger.classList.toggle("unavailable", unavailable); trigger.setAttribute("aria-disabled", String(unavailable)); trigger.setAttribute("aria-label", `选择模型智能程度,当前${label}`); options().forEach((button) => { button.setAttribute("role", "option"); button.setAttribute("aria-selected", String(button === selected)); }); }; const close = () => { reasoningField.hidden = true; trigger.classList.remove("open"); trigger.setAttribute("aria-expanded", "false"); }; const open = () => { closeCustomDropdowns(); reasoningField.hidden = false; trigger.classList.add("open"); trigger.setAttribute("aria-expanded", "true"); place(); }; reasoningPickerControl = { close, open, place, sync, trigger }; const resetButton = $("#newConversation", originalForm); const runButton = $("#runButton", originalForm); if (resetButton) composer.append(resetButton); composer.append(trigger); if (runButton) composer.append(runButton); document.body.append(reasoningField); trigger.addEventListener("click", (event) => { event.stopPropagation(); if (reasoningField.hidden) open(); else close(); }); reasoningField.addEventListener("click", (event) => { event.stopPropagation(); if (!event.target.closest("[data-reasoning-effort]")) return; requestAnimationFrame(() => { sync(); close(); trigger.focus({ preventScroll: true }); }); }); new MutationObserver(sync).observe(reasoningField, { attributes: true, attributeFilter: ["class", "disabled"], subtree: true, }); window.addEventListener("resize", place); sync(); } function enhanceSelect(select) { if (!select || dropdownControls.has(select)) return; const shell = select.parentElement; if (!shell) return; shell.classList.add("v2-custom-select"); select.classList.add("v2-native-select"); const trigger = make("button", "v2-dropdown-trigger", ` `); trigger.type = "button"; trigger.setAttribute("aria-haspopup", "listbox"); trigger.setAttribute("aria-expanded", "false"); trigger.setAttribute("aria-label", select.getAttribute("aria-label") || "打开下拉菜单"); const menu = make("div", "v2-dropdown-menu", ""); menu.id = `${select.id}Menu`; menu.hidden = true; menu.setAttribute("role", "listbox"); menu.setAttribute("aria-label", select.getAttribute("aria-label") || "选项"); trigger.setAttribute("aria-controls", menu.id); shell.append(trigger, menu); const valueNode = $(".v2-dropdown-value", trigger); const render = () => { const selected = select.selectedOptions[0] || select.options[0]; valueNode.textContent = selected?.textContent?.trim() || "请选择"; trigger.disabled = select.disabled; menu.innerHTML = ""; [...select.options].forEach((option) => { const item = make("button", "v2-dropdown-option", ""); item.type = "button"; item.dataset.value = option.value; item.textContent = option.textContent.trim(); item.disabled = option.disabled; item.setAttribute("role", "option"); item.setAttribute("aria-selected", String(option.value === select.value)); menu.append(item); }); }; const close = () => { menu.hidden = true; shell.classList.remove("v2-dropdown-open"); trigger.setAttribute("aria-expanded", "false"); }; const open = () => { if (trigger.disabled) return; closeCustomDropdowns(); render(); menu.hidden = false; shell.classList.add("v2-dropdown-open"); trigger.setAttribute("aria-expanded", "true"); }; const control = { close, menu, open, select, shell, sync: render, trigger }; dropdownControls.set(select, control); trigger.addEventListener("click", (event) => { event.stopPropagation(); if (menu.hidden) open(); else close(); }); trigger.addEventListener("keydown", (event) => { if (event.key === "ArrowDown") { event.preventDefault(); open(); requestAnimationFrame(() => { $(".v2-dropdown-option[aria-selected=\"true\"]:not(:disabled)", menu)?.focus(); }); } else if (event.key === "Escape") { close(); } }); menu.addEventListener("click", (event) => { event.stopPropagation(); const item = event.target.closest(".v2-dropdown-option"); if (!item || item.disabled) return; select.value = item.dataset.value; select.dispatchEvent(new Event("change", { bubbles: true })); render(); close(); trigger.focus(); }); menu.addEventListener("keydown", (event) => { if (event.key !== "Escape") return; event.preventDefault(); close(); trigger.focus(); }); select.addEventListener("change", render); new MutationObserver(render).observe(select, { attributes: true, characterData: true, childList: true, subtree: true, }); render(); } function enhanceConfiguredDropdowns() { [$("#vehicleSelect")] .filter(Boolean) .forEach(enhanceSelect); } function buildSidebar(commandPanel) { commandPanel.classList.add("v2-sidebar"); commandPanel.innerHTML = ""; const profile = make("section", "v2-profile", `
WELCOME TO THE CABIN
Hi,user
`); const control = make("section", "v2-sidebar-section v2-control-section", `
CONTROL DESK

控制台

控制方式
`); const config = make("section", "v2-sidebar-section v2-config-section", `
配置信息
`); const footer = make("footer", "v2-sidebar-footer", `
智舱控制链路正在连接服务
`); commandPanel.append(profile, control, config, footer); return $("#v2ConfigStack"); } function buildConfigFields(configStack, originalForm) { const vehicleField = $(".vehicle-field", originalForm); const transportField = $("#vehicleTransportField", originalForm); const providerField = $(".field-group:has(.segmented [data-provider])", originalForm); const reasoningField = $("#reasoningField", originalForm); const executionField = $(".field-group:has(.execution-mode-options)", originalForm); const simulationFaultField = $("#simulationFaultField", originalForm); if (vehicleField) { vehicleField.classList.add("v2-config-field", "v2-vehicle-field"); const vehicleControl = $(".vehicle-select-control", vehicleField); const vehicleSelect = $("#vehicleSelect", vehicleField); const vehicleIcon = $("svg, [data-lucide]", vehicleControl); if (vehicleControl && vehicleSelect && vehicleIcon) { const copy = make("div", "v2-config-card-copy", "车型配置"); const selectSlot = make("div", "v2-config-card-select", ""); const state = make("span", "v2-field-state", "CHECK"); state.id = "v2VehicleState"; selectSlot.append(vehicleSelect); copy.append(selectSlot); vehicleControl.classList.add("v2-config-card", "v2-config-select-card"); vehicleControl.id = "v2VehicleCard"; vehicleControl.dataset.state = "checking"; vehicleControl.replaceChildren(vehicleIcon, copy, state); $(".field-label", vehicleField)?.remove(); } configStack.append(vehicleField); } if (transportField) { transportField.classList.add("v2-config-field", "v2-transport-field"); configStack.append(transportField); } const modelField = make("div", "v2-config-field v2-config-card v2-model-field", `
推理模型模型检测中
AUTO

模型由服务端安全配置

`); modelField.id = "v2ModelField"; configStack.append(modelField); installReasoningPicker(reasoningField, originalForm); const gestureModelField = make("div", "v2-config-field v2-config-card v2-gesture-model-field", `
手势模型MediaPipe GR
VISION Tasks Vision · 0.10.14`); gestureModelField.id = "v2GestureModelField"; configStack.append(gestureModelField); const userHardwareState = make("div", "v2-user-target-status", ` 正在准备虚拟 CAN`); configStack.append(userHardwareState); if (providerField) { providerField.classList.add("v2-provider-field"); providerField.hidden = true; configStack.append(providerField); } if (executionField) { executionField.classList.add("v2-native-execution-field"); executionField.hidden = true; configStack.append(executionField); } const advanced = make("div", "v2-advanced-stack", ""); [simulationFaultField].forEach((field) => { if (!field) return; field.classList.add("v2-config-field", "v2-advanced-field"); advanced.append(field); }); configStack.append(advanced); } function buildOperation(mainPanel, originalForm, panelHeading, modeRail, requestHeader, gestureDrawer) { const operation = make("section", "v2-operation-shell", `
`); mainPanel.prepend(operation); const naturalPanel = $("#v2NaturalPanel"); if (panelHeading) { panelHeading.classList.add("v2-operation-heading", "v2-module-heading"); const kicker = $(".section-kicker", panelHeading); const title = $("h2", panelHeading); const intro = $(".panel-intro", panelHeading); if (kicker) kicker.textContent = "REMOTE ORCHESTRATION · LIVE SYNC"; if (title) title.textContent = "一句话远程编排"; if (intro) intro.textContent = "网页指令将通过同步链路发送至车机;车机任务与执行反馈在右侧实时更新"; naturalPanel.append(panelHeading); } const naturalGrid = make("div", "v2-natural-grid", ""); naturalGrid.id = "v2NaturalGrid"; originalForm.classList.add("v2-request-form"); naturalGrid.append(originalForm); if (requestHeader) { requestHeader.classList.add("v2-request-status"); naturalGrid.append(requestHeader); } naturalPanel.append(naturalGrid); if (modeRail) { modeRail.classList.add("v2-mode-rail"); naturalPanel.append(modeRail); } const gesturePanel = $("#v2GesturePanel"); const gestureHeading = make("header", "v2-operation-heading v2-module-heading v2-gesture-operation-heading", ` SELF-ORCHESTRATION

抬抬手,控制整车

通过摄像头识别手势,直观控制车舱功能

`); gesturePanel.append(gestureHeading); if (gestureDrawer) { gestureDrawer.classList.add("v2-inline-gesture"); const cameraStage = $(".gesture-camera-stage", gestureDrawer); const cameraToggle = $("#gestureStartButton", gestureDrawer); const actionRail = $(".gesture-actions", gestureDrawer); const statusStrip = $(".gesture-status-strip", gestureDrawer); const gestureNow = $(".gesture-now", gestureDrawer); const guideCard = $(".gesture-guide-card", gestureDrawer); if (cameraToggle) { cameraToggle.classList.add("v2-camera-toggle"); cameraToggle.setAttribute("aria-label", "开启摄像头"); cameraToggle.setAttribute("title", "开启摄像头"); } if (actionRail && cameraToggle) { actionRail.classList.add("v2-gesture-action-rail"); actionRail.append(cameraToggle); } const resultStack = make("div", "v2-gesture-result-stack", ""); resultStack.id = "v2GestureResultStack"; const recognitionCard = make("section", "v2-gesture-recognition-card", ` 手势识别结果`); const executionSlot = make("section", "v2-gesture-execution-slot", ` 执行结果`); executionSlot.id = "v2GestureExecutionSlot"; if (statusStrip) recognitionCard.append(statusStrip); if (gestureNow) recognitionCard.append(gestureNow); resultStack.append(recognitionCard, executionSlot); if (cameraStage) gestureDrawer.append(cameraStage); if (actionRail) gestureDrawer.append(actionRail); gestureDrawer.append(resultStack); if (guideCard) gestureDrawer.append(guideCard); gesturePanel.append(gestureDrawer); } return operation; } function buildModuleSwitcher(mainPanel) { const switcher = make("nav", "v2-module-switcher", ""); switcher.id = "v2ModuleSwitcher"; switcher.dataset.unifiedSync = "true"; switcher.setAttribute("aria-label", "智能座舱远程控制与同步状态"); switcher.innerHTML = `
COCKPIT REMOTE · LIVE MIRROR

511工程-智能座舱

网页控制链路已启用
LIVE--:--:--
`; mainPanel.prepend(switcher); } function buildOutput(mainPanel) { const output = make("section", "v2-output-shell", `
00
INFORMATION OUTPUT

整车联合数字孪生

LIVE STATE
`); mainPanel.append(output); const content = $("#v2OutputContent"); const twin = $(".joint-twin-shell", mainPanel); const resultWorkspace = $(".result-workspace", mainPanel); if (twin) content.append(twin); if (resultWorkspace) content.append(resultWorkspace); pageDefinitions.forEach((page) => { const target = $(page.target, content); if (!target) return; target.dataset.v2Page = String(page.index); target.classList.add("v2-output-page"); }); pageDefinitions.forEach((page) => { const button = make("button", "v2-page-button", ` ${String(page.index).padStart(2, "0")} ${page.shortLabel}`); button.type = "button"; button.dataset.v2PageTarget = String(page.index); button.setAttribute("aria-label", `${String(page.index).padStart(2, "0")} ${page.label}`); $("#v2PageRail").append(button); }); } const displaySync = { revision: -1, requestId: null, pollTimer: null, deviceFrame: null, deviceFingerprint: "", activeDevice: null, pinnedDevice: null, previewClearTimer: null, hoverSuppressedUntil: 0, controlDevice: null, deviceDrafts: {}, pendingControls: {}, logs: [], logKeys: new Set(), extensionFingerprint: "", }; const readText = (selector, fallback = "--") => $(selector)?.textContent?.trim() || fallback; const clampControlNumber = (value, min, max, fallback = min) => { const numeric = Number.parseFloat(String(value ?? "").replace(/[^\d.-]/g, "")); return Number.isFinite(numeric) ? Math.max(min, Math.min(max, numeric)) : fallback; }; const splitReadout = (selector) => { const [front = "-", rear = "-"] = readText(selector, "- / -").split("/"); return [front.trim() || "-", rear.trim() || "-"]; }; function seatFunctionValue(seat, feature) { return $(`.seat-card[data-seat="${seat}"] [data-function="${feature}"] b`)?.textContent?.trim() || "--"; } function seatAxisValue(seat, axis) { return $(`.seat-card[data-seat="${seat}"] [data-axis="${axis}"] b`)?.textContent?.trim() || "--"; } function seatLevelControlValue(seat, feature) { const text = seatFunctionValue(seat, feature); const level = text.match(/L(\d+)/i)?.[1]; return /OFF|关闭|--/.test(text) ? "off" : level || "1"; } function massageReadout() { const text = seatFunctionValue("driver", "massage"); if (text === "--") return ["--", "--"]; if (/OFF|关闭/.test(text)) return ["关闭", "关闭"]; const [mode = "开启", strength = "默认"] = text.split("·").map((item) => item.trim()); return [mode, strength]; } const levelOptions = [ ["off", "关闭"], ["1", "1 档"], ["2", "2 档"], ["3", "3 档"], ]; const toggleOptions = [["off", "关闭"], ["on", "开启"]]; const massageModeOptions = [ ["off", "关闭"], ["relax", "舒缓"], ["stretch", "伸展"], ["waist", "腰部"], ["wave", "波浪"], ]; const massageStrengthOptions = [ ["off", "关闭"], ["low", "低"], ["medium", "中"], ["high", "高"], ]; const colorIndexNames = ["皓月白", "冰川蓝", "松石绿", "星云紫", "琥珀橙", "玫瑰红"]; const colorIndexOptions = Array.from({ length: 16 }, (_item, index) => [ String(index), colorIndexNames[index] ? `${index} · ${colorIndexNames[index]}` : `色号 ${index}`, ]); const rangeControl = (key, label, value, min = 0, max = 100, step = 1, unit = "%", hint = "") => ({ key, label, type: "range", value: clampControlNumber(value, min, max), min, max, step, unit, hint, }); const selectControl = (key, label, value, options) => ({ key, label, type: "select", value, options }); function deviceControlValue(deviceId, control) { const draft = displaySync.deviceDrafts[deviceId]; return draft && Object.hasOwn(draft, control.key) ? draft[control.key] : control.value; } function optionLabel(options, value) { return options.find(([optionValue]) => String(optionValue) === String(value))?.[1] || String(value); } const syncedDevices = [ { id: "passenger", name: "副驾座椅", icon: "armchair", rows: () => [["通风", seatFunctionValue("passenger", "ventilation")], ["加热", seatFunctionValue("passenger", "heating")], ["水平", seatAxisValue("passenger", "longitudinal")], ["坐垫", seatAxisValue("passenger", "cushion_tilt")], ["靠背", seatAxisValue("passenger", "backrest_recline")], ["腿托", seatAxisValue("passenger", "legrest_height")]], controls: () => [ selectControl("ventilation", "通风", seatLevelControlValue("passenger", "ventilation"), levelOptions), selectControl("heating", "加热", seatLevelControlValue("passenger", "heating"), levelOptions), rangeControl("longitudinal", "水平", seatAxisValue("passenger", "longitudinal")), rangeControl("cushion_tilt", "坐垫", seatAxisValue("passenger", "cushion_tilt")), rangeControl("backrest_recline", "靠背", seatAxisValue("passenger", "backrest_recline")), rangeControl("legrest_height", "腿托", seatAxisValue("passenger", "legrest_height")), ], command: (values) => `调节副驾座椅:通风${optionLabel(levelOptions, values.ventilation)},加热${optionLabel(levelOptions, values.heating)},水平${values.longitudinal}%,坐垫${values.cushion_tilt}%,靠背${values.backrest_recline}%,腿托${values.legrest_height}%`, }, { id: "driver", name: "主驾座椅", icon: "armchair", rows: () => { const [massageMode, massageStrength] = massageReadout(); return [["通风", seatFunctionValue("driver", "ventilation")], ["加热", seatFunctionValue("driver", "heating")], ["按摩模式", massageMode], ["按摩强度", massageStrength]]; }, controls: () => { const [massageMode, massageStrength] = massageReadout(); return [ selectControl("ventilation", "通风", seatLevelControlValue("driver", "ventilation"), levelOptions), selectControl("heating", "加热", seatLevelControlValue("driver", "heating"), levelOptions), selectControl("massage_mode", "按摩模式", /关闭|--/.test(massageMode) ? "off" : massageMode.toLowerCase(), massageModeOptions), selectControl("massage_strength", "按摩强度", /关闭|--/.test(massageStrength) ? "off" : massageStrength.toLowerCase(), massageStrengthOptions), ]; }, command: (values) => `调节主驾座椅:通风${optionLabel(levelOptions, values.ventilation)},加热${optionLabel(levelOptions, values.heating)},按摩模式${optionLabel(massageModeOptions, values.massage_mode)},按摩强度${optionLabel(massageStrengthOptions, values.massage_strength)}`, }, { id: "windows", name: "车窗", icon: "gallery-vertical-end", rows: () => [["左前窗", readText("#frontLeftWindow")], ["右前窗", readText("#frontRightWindow")], ["左后窗", readText("#rearLeftWindow")], ["右后窗", readText("#rearRightWindow")]], controls: () => [ rangeControl("front_left", "左前窗", readText("#frontLeftWindow"), 0, 100, 1, "%", "0% 关闭 · 100% 打开"), rangeControl("front_right", "右前窗", readText("#frontRightWindow")), rangeControl("rear_left", "左后窗", readText("#rearLeftWindow")), rangeControl("rear_right", "右后窗", readText("#rearRightWindow")), ], command: (values) => `调节车窗开合比例:左前窗${values.front_left}%,右前窗${values.front_right}%,左后窗${values.rear_left}%,右后窗${values.rear_right}%`, }, { id: "shade", name: "遮阳帘", icon: "panels-top-left", rows: () => [["当前位置", readText("#shadePositionValue")]], controls: () => [rangeControl("position", "当前位置", readText("#shadePositionValue"), 0, 100, 1, "%", "0% 关闭 · 100% 打开")], command: (values) => `将遮阳帘开合位置调整为${values.position}%`, }, { id: "reading", name: "阅读灯", icon: "book-open", rows: () => [["开关状态", readText("#interiorLightPower", "等待反馈")]], controls: () => [selectControl("power", "开关状态", /开启|ON/.test(readText("#interiorLightPower")) ? "on" : "off", toggleOptions)], command: (values) => `${values.power === "on" ? "打开" : "关闭"}阅读灯`, }, { id: "ambient", name: "氛围灯", icon: "sparkles", rows: () => { const [frontBrightness, rearBrightness] = splitReadout("#ambientBrightness"); const [frontColor, rearColor] = splitReadout("#ambientColorIndex"); return [["开关状态", /OFF|关闭/.test(readText("#ambientPower")) ? "关闭" : "开启"], ["前排亮度", frontBrightness], ["前排颜色", frontColor], ["后排亮度", rearBrightness], ["后排颜色", rearColor]]; }, controls: () => { const [frontBrightness, rearBrightness] = splitReadout("#ambientBrightness"); const [frontColor, rearColor] = splitReadout("#ambientColorIndex"); return [ selectControl("power", "开关状态", /OFF|关闭/.test(readText("#ambientPower")) ? "off" : "on", toggleOptions), rangeControl("front_brightness", "前排亮度", frontBrightness, 0, 5, 1, " 级"), selectControl("front_color", "前排颜色", String(clampControlNumber(frontColor, 0, 15, 0)), colorIndexOptions), rangeControl("rear_brightness", "后排亮度", rearBrightness, 0, 5, 1, " 级"), selectControl("rear_color", "后排颜色", String(clampControlNumber(rearColor, 0, 15, 0)), colorIndexOptions), ]; }, command: (values) => `调节氛围灯:${values.power === "on" ? "开启" : "关闭"},前排亮度${values.front_brightness}级、颜色${optionLabel(colorIndexOptions, values.front_color)},后排亮度${values.rear_brightness}级、颜色${optionLabel(colorIndexOptions, values.rear_color)}`, }, { id: "headlight", name: "前大灯", icon: "car-front", rows: () => [["近光灯", readText("#lowBeamState")], ["远光灯", readText("#highBeamState")], ["雾灯", readText("#fogLightState")], ["转向灯", readText("#turnSignalState")]], controls: () => [["low_beam", "近光灯", "#lowBeamState"], ["high_beam", "远光灯", "#highBeamState"], ["fog_light", "雾灯", "#fogLightState"], ["turn_signal", "转向灯", "#turnSignalState"]].map(([key, label, selector]) => selectControl(key, label, /开启|ON/.test(readText(selector)) ? "on" : "off", toggleOptions)), command: (values) => `调节前大灯:近光灯${optionLabel(toggleOptions, values.low_beam)},远光灯${optionLabel(toggleOptions, values.high_beam)},雾灯${optionLabel(toggleOptions, values.fog_light)},转向灯${optionLabel(toggleOptions, values.turn_signal)}`, }, { id: "climate", name: "空调", icon: "fan", rows: () => [["开关状态", readText("#climatePower", "等待反馈")], ["主驾温度", readText("#driverTemperature")], ["副驾温度", readText("#passengerTemperature")], ["风量", readText("#fanLevel")], ["循环模式", readText("#cycleMode")]], controls: () => [ selectControl("power", "开关状态", /ON|ACTIVE|开启/.test(readText("#climatePower")) ? "on" : "off", toggleOptions), rangeControl("driver_temperature", "主驾温度", readText("#driverTemperature"), 16, 32, 0.5, "℃"), rangeControl("passenger_temperature", "副驾温度", readText("#passengerTemperature"), 16, 32, 0.5, "℃"), rangeControl("fan_level", "风量", readText("#fanLevel"), 0, 7, 1, " 档"), selectControl("cycle_mode", "循环模式", readText("#cycleMode").includes("内") ? "internal" : "external", [["internal", "内循环"], ["external", "外循环"]]), ], command: (values) => `调节空调:${values.power === "on" ? "开启" : "关闭"},主驾${values.driver_temperature}℃,副驾${values.passenger_temperature}℃,风量${values.fan_level}档,${values.cycle_mode === "internal" ? "内循环" : "外循环"}`, }, ]; function renderDeviceControl(device, control) { const value = deviceControlValue(device.id, control); if (control.type === "select") { const options = control.options.map(([optionValue, label]) => ``).join(""); return ``; } return ``; } function renderSyncedDevices() { const grid = $("#v2DeviceSyncGrid"); if (!grid) return; grid.innerHTML = syncedDevices.map((device) => { const rows = device.rows(); const waiting = rows.some(([, value]) => /--|等待|同步中|未知/.test(value)); const controlOpen = displaySync.controlDevice === device.id; const hasDraft = Boolean(displaySync.deviceDrafts[device.id]); const pending = Boolean(displaySync.pendingControls[device.id]); const stateLabel = pending ? "已发送 · 等待同步" : hasDraft ? "参数待发送" : `${rows.length} 项关键状态`; return `
${rows.map(([label, value]) => `
${escapeHtml(label)}
${escapeHtml(value)}
`).join("")}
手动调节参数确认后通过编排链路发送至车机
${device.controls().map((control) => renderDeviceControl(device, control)).join("")}
`; }).join(""); if (displaySync.activeDevice) { $(`.v2-synced-device[data-device="${displaySync.activeDevice}"]`)?.classList.add("active"); } displaySync.deviceFingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, device.rows()])); window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); } function storeDeviceControlDraft(input) { const deviceId = input.dataset.deviceControl; const key = input.dataset.controlKey; if (!deviceId || !key) return; displaySync.deviceDrafts[deviceId] ||= {}; displaySync.deviceDrafts[deviceId][key] = input.value; const controls = input.closest("[data-device-controls]"); const output = controls?.querySelector(`[data-control-output="${key}"]`); if (output) output.textContent = `${input.value}${input.dataset.controlUnit || ""}`; const card = input.closest("[data-device]"); if (card) { card.dataset.hasDraft = "true"; const state = $("[data-device-card-state]", card); if (state) state.textContent = "参数待发送"; } window.dispatchEvent(new CustomEvent("cognitive-agent:vehicle-control-draft", { detail: { device_id: deviceId, control: key, value: input.value, source: "web_console" }, })); } function sendDeviceControl(deviceId) { const device = syncedDevices.find((item) => item.id === deviceId); if (!device) return; const values = Object.fromEntries(device.controls().map((control) => [control.key, String(deviceControlValue(device.id, control))])); const utterance = device.command(values); const detail = { device_id: device.id, device_name: device.name, controls: values, utterance, source: "web_console", requested_at: new Date().toISOString(), }; window.dispatchEvent(new CustomEvent("cognitive-agent:vehicle-control-request", { detail })); const accepted = window.CognitiveAgentConsole?.executeUtterance?.(utterance, { autoSubmit: true }) || false; if (!accepted) { window.CognitiveAgentConsole?.notify?.("上一条指令仍在执行,请稍候"); return; } displaySync.pendingControls[device.id] = utterance; appendSyncLog({ category: "device", title: `网页端调节${device.name}`, detail: utterance, status: "info" }); renderSyncedDevices(); } function scheduleSyncedDeviceRender() { if (displaySync.deviceFrame) return; displaySync.deviceFrame = requestAnimationFrame(() => { displaySync.deviceFrame = null; const fingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, device.rows()])); if (fingerprint === displaySync.deviceFingerprint && $("#v2DeviceSyncGrid")?.childElementCount) return; renderSyncedDevices(); if (displaySync.activeDevice) presentTwinDevice(displaySync.activeDevice, { pinned: Boolean(displaySync.pinnedDevice), selectSeat: false, }); }); } function appendSyncLog({ key = "", category = "sync", title, detail = "", status = "info" }) { if (!title || (key && displaySync.logKeys.has(key))) return; const item = { key, category, title, detail, status, time: new Intl.DateTimeFormat("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, }).format(new Date()), }; if (key) displaySync.logKeys.add(key); displaySync.logs.unshift(item); if (displaySync.logs.length > 40) { const removed = displaySync.logs.pop(); if (removed?.key) displaySync.logKeys.delete(removed.key); } renderSyncLogs(); } function renderSyncLogs() { const list = $("#v2SyncLogList"); if (!list) return; const filter = $("#v2LogFilter")?.value || "all"; const items = displaySync.logs.filter((item) => filter === "all" || item.category === filter); const icons = { task: "route", sync: "refresh-cw", extension: "usb", device: "activity" }; list.innerHTML = items.length ? items.map((item) => `
  • ${escapeHtml(item.title)}${escapeHtml(item.detail)}
  • `).join("") : '
  • 暂无记录车机任务、设备反馈与外设事件会显示在这里
  • '; window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); } function panelVisible(key) { const panel = key === "devices" ? $("#v2DevicePanel") : $("#v2ExtensionPanel"); if (panel) return !panel.classList.contains("is-collapsed"); return sessionStorage.getItem(`cognitive-agent.sync-panel.${key}`) !== "hidden"; } function setSidePanelVisibility(key, visible) { const panel = key === "devices" ? $("#v2DevicePanel") : $("#v2ExtensionPanel"); if (!panel) return; panel.hidden = false; panel.classList.toggle("is-collapsed", !visible); panel.dataset.collapsed = String(!visible); sessionStorage.setItem(`cognitive-agent.sync-panel.${key}`, visible ? "visible" : "hidden"); $$(`[data-panel-toggle="${key}"], [data-panel-close="${key}"]`).forEach((button) => { button.classList.toggle("active", visible); button.setAttribute("aria-pressed", String(visible)); button.setAttribute("aria-label", `${visible ? "收起" : "展开"}${key === "devices" ? "车端设备状态" : "外设与扩展能力"}`); const actionLabel = $("[data-panel-action-label]", button); if (actionLabel) actionLabel.textContent = visible ? "收起" : "展开"; const stateLabel = $("[data-panel-state-label]", button); if (stateLabel) stateLabel.textContent = visible ? "已展开" : "已收起"; }); const openPanels = ["devices", "extensions"].filter((panelKey) => { const candidate = panelKey === "devices" ? $("#v2DevicePanel") : $("#v2ExtensionPanel"); return candidate && !candidate.classList.contains("is-collapsed"); }); $(".joint-twin-shell")?.setAttribute("data-open-panels", openPanels.join(" ") || "none"); window.dispatchEvent(new Event("resize")); } function presentTwinDevice(deviceId, { pinned = false, selectSeat = pinned } = {}) { const device = syncedDevices.find((item) => item.id === deviceId); const popover = $("#v2TwinDevicePopover"); if (!device || !popover) return; if (displaySync.previewClearTimer) { window.clearTimeout(displaySync.previewClearTimer); displaySync.previewClearTimer = null; } displaySync.activeDevice = deviceId; if (pinned) displaySync.pinnedDevice = deviceId; const rows = device.rows(); popover.hidden = false; popover.dataset.device = deviceId; popover.dataset.pinned = String(Boolean(displaySync.pinnedDevice)); popover.innerHTML = `
    LIVE DEVICE FEEDBACK${escapeHtml(device.name)}
    ${pinned ? "已固定" : "靠近查看"}
    ${rows.map(([label, value]) => `
    ${escapeHtml(label)}
    ${escapeHtml(value)}
    `).join("")}
    `; $$("[data-twin-device]").forEach((button) => button.classList.toggle("active", button.dataset.twinDevice === deviceId)); $$(".v2-synced-device").forEach((button) => button.classList.toggle("active", button.dataset.device === deviceId)); if (selectSeat && ["driver", "passenger"].includes(deviceId)) { $(`[data-twin-seat="${deviceId}"]`)?.click(); } window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); } function clearTwinDevicePreview({ force = false } = {}) { if (displaySync.previewClearTimer) { window.clearTimeout(displaySync.previewClearTimer); displaySync.previewClearTimer = null; } if (displaySync.pinnedDevice && !force) return; displaySync.activeDevice = null; if (force) displaySync.pinnedDevice = null; const popover = $("#v2TwinDevicePopover"); if (popover) popover.hidden = true; $$("[data-twin-device], .v2-synced-device").forEach((button) => button.classList.remove("active")); } function scheduleTwinDevicePreviewClear() { if (displaySync.pinnedDevice) return; if (displaySync.previewClearTimer) window.clearTimeout(displaySync.previewClearTimer); displaySync.previewClearTimer = window.setTimeout(() => { displaySync.previewClearTimer = null; clearTwinDevicePreview(); }, 45); } function buildTwinDeviceInteractions() { const stage = $("#seatTwin"); if (!stage || $("#v2TwinHotspots")) return; const hotspots = make("div", "v2-twin-hotspots", syncedDevices.map((device) => ` `).join("")); hotspots.id = "v2TwinHotspots"; const popover = make("aside", "v2-twin-device-popover", ""); popover.id = "v2TwinDevicePopover"; popover.hidden = true; popover.setAttribute("role", "dialog"); popover.setAttribute("aria-label", "车端设备状态详情"); popover.setAttribute("aria-live", "polite"); stage.append(hotspots, popover); hotspots.addEventListener("pointerdown", (event) => event.stopPropagation()); $$("[data-twin-device]", hotspots).forEach((button) => { button.addEventListener("pointerenter", () => { if (!displaySync.pinnedDevice && performance.now() >= displaySync.hoverSuppressedUntil) { presentTwinDevice(button.dataset.twinDevice); } }); button.addEventListener("pointerleave", (event) => { if (event.relatedTarget?.closest?.("#v2TwinDevicePopover")) return; scheduleTwinDevicePreviewClear(); }); button.addEventListener("focus", () => presentTwinDevice(button.dataset.twinDevice)); button.addEventListener("blur", scheduleTwinDevicePreviewClear); }); hotspots.addEventListener("click", (event) => { const button = event.target.closest("[data-twin-device]"); if (!button) return; event.stopPropagation(); const samePinned = displaySync.pinnedDevice === button.dataset.twinDevice; if (samePinned) clearTwinDevicePreview({ force: true }); else presentTwinDevice(button.dataset.twinDevice, { pinned: true }); }); popover.addEventListener("pointerdown", (event) => event.stopPropagation()); popover.addEventListener("pointerenter", () => { if (displaySync.previewClearTimer) { window.clearTimeout(displaySync.previewClearTimer); displaySync.previewClearTimer = null; } }); popover.addEventListener("pointerleave", scheduleTwinDevicePreviewClear); popover.addEventListener("click", (event) => { if (!event.target.closest("[data-twin-popover-close]")) return; event.stopPropagation(); displaySync.hoverSuppressedUntil = performance.now() + 160; clearTwinDevicePreview({ force: true }); }); stage.addEventListener("click", (event) => { if (event.target.closest("[data-twin-device], #v2TwinDevicePopover")) return; clearTwinDevicePreview({ force: true }); }); window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); } function taskKind(utterance, result, source = "") { if (source === "web_console") return "网页远程编排"; if (result?.mode_profile) return "自定义功能模式"; const preset = $$(".mode-chips [data-mode-command]").find((button) => ( String(utterance).includes(button.textContent.trim()) || button.dataset.modeCommand === utterance )); return preset ? "预设功能模式" : utterance ? "一句话自定义编排" : "等待编排任务"; } function renderTaskTimeline(task) { const timeline = $("#v2TaskTimeline"); if (!timeline) return; const result = task?.result; const trace = Array.isArray(result?.trace_events) ? result.trace_events : []; const domains = Object.keys(result?.function_configs || {}); const stages = task?.status === "running" ? ["接收编排指令", "解析意图", "构建功能模式", "等待设备反馈"] : trace.length ? trace.slice(-4).map((item) => item.label || item.stage || item.event || "执行步骤") : domains.length ? ["接收编排指令", `编排 ${domains.length} 个设备域`, "下发执行计划", "同步执行结果"] : ["等待车机或网页端下发编排任务"]; timeline.innerHTML = stages.map((stage, index) => ` ${String(index + 1).padStart(2, "0")}${escapeHtml(stage)} `).join(""); } function setTaskPresentation(task = {}) { const utterance = String(task.utterance || ""); const result = task.result && typeof task.result === "object" ? task.result : null; const input = $("#utterance"); if (input && utterance && document.activeElement !== input) input.value = utterance; const kind = taskKind(utterance, result, String(task.source || "")); if ($("#v2TaskKind")) $("#v2TaskKind").textContent = kind; if ($("#v2TaskCommand")) $("#v2TaskCommand").textContent = utterance || "等待车机端或网页端选择预设模式、输入一句话指令"; const normalizedStatus = String(task.status || "idle").toLowerCase(); const statusMap = { idle: ["等待任务", "idle"], running: ["构建 / 执行中", "running"], succeeded: ["执行完成", "succeeded"], partially_succeeded: ["部分完成", "partial"], needs_confirmation: ["等待确认", "partial"], failed: ["执行失败", "failed"], }; const [label, state] = statusMap[normalizedStatus] || statusMap.idle; const badge = $("#v2TaskState"); if (badge) { badge.textContent = label; badge.dataset.state = state; } $$(".mode-chips [data-mode-command]").forEach((button) => { const active = Boolean(utterance) && ( utterance === button.dataset.modeCommand || utterance.includes(button.textContent.trim()) ); button.classList.toggle("v2-sync-active", active); }); renderTaskTimeline(task); } function applyDisplaySnapshot(snapshot) { const health = snapshot?.extension_health || {}; const physicallyConnected = health.transport !== "simulation" && Boolean(health.connected || health.peer_connected); const synchronizedCatalog = Array.isArray(snapshot?.extension?.catalog) ? snapshot.extension.catalog : []; const connected = physicallyConnected || synchronizedCatalog.length > 0; const listening = Boolean(health.listening); const usbState = $("#v2UsbState"); if (usbState) { const usbLabel = $("span", usbState); if (usbLabel) usbLabel.textContent = physicallyConnected ? "USB 外设已接入" : synchronizedCatalog.length ? `${new Set(synchronizedCatalog.map((item) => item.device_id)).size} 个外设已同步` : listening ? "等待 USB 外设" : "扩展链路检查中"; usbState.dataset.state = connected ? "online" : listening ? "listening" : "offline"; } if ($("#v2SyncTransport")) { $("#v2SyncTransport").textContent = `${String(health.transport || "GSCU").toUpperCase()} · ${health.peer || health.listen || "等待连接"}`; } const vehicleOnline = health.vehicle?.connected !== false; const vehicleBadge = $("#v2HeadUnitState"); if (vehicleBadge) { vehicleBadge.dataset.state = vehicleOnline ? "online" : "offline"; $("span", vehicleBadge).textContent = vehicleOnline ? "车机链路在线" : "车机链路离线"; } const revision = Number(snapshot?.revision ?? -1); if (revision !== displaySync.revision) { displaySync.revision = revision; const task = snapshot?.task || {}; setTaskPresentation(task); const sourceLabel = task.source === "web_console" ? "网页端" : "车机端"; if (task.utterance) { appendSyncLog({ key: `task:${revision}:${task.status}`, category: "task", title: `${sourceLabel}${task.status === "running" ? "下发编排任务" : "任务状态已同步"}`, detail: `${task.utterance} · ${task.status || "idle"}`, status: ["succeeded", "partially_succeeded"].includes(task.status) ? "success" : task.status === "failed" ? "error" : "info", }); } const result = task.result; const requestId = result?.request_id || null; if (result && requestId !== displaySync.requestId) { displaySync.requestId = requestId; window.CognitiveAgentConsole?.applySyncedResult?.(result, { utterance: task.utterance || "", }); } } const extensionFingerprint = synchronizedCatalog .map((item) => `${item.device_id || "external"}:${item.action || item.name || "capability"}`) .sort() .join("|"); if (extensionFingerprint && extensionFingerprint !== displaySync.extensionFingerprint) { displaySync.extensionFingerprint = extensionFingerprint; appendSyncLog({ key: `extension:${extensionFingerprint}`, category: "extension", title: "外设能力目录已同步", detail: `${new Set(synchronizedCatalog.map((item) => item.device_id || "external")).size} 个外设 · ${synchronizedCatalog.length} 项能力`, status: "success", }); } window.dispatchEvent(new CustomEvent("cognitive-agent:display-sync", { detail: snapshot })); } async function pollVehicleDisplaySync() { try { const response = await fetch("/api/vehicle-display-sync", { credentials: "same-origin", cache: "no-store", headers: { Accept: "application/json" }, }); if (!response.ok) throw new Error(`HTTP ${response.status}`); applyDisplaySnapshot(await response.json()); $("#v2MirrorState")?.setAttribute("data-state", "online"); if ($("#v2MirrorState span")) $("#v2MirrorState span").textContent = "同步正常"; appendSyncLog({ key: "sync:online", category: "sync", title: "网页同步链路已建立", detail: "车机任务与设备状态每 2 秒同步", status: "success" }); } catch (_error) { console.warn("[vehicle-display-sync]", _error); $("#v2MirrorState")?.setAttribute("data-state", "offline"); if ($("#v2MirrorState span")) $("#v2MirrorState span").textContent = "等待同步服务"; } } function enableRemoteOrchestration() { const form = $("#requestForm"); const input = $("#utterance"); if (input) { input.readOnly = false; input.removeAttribute("aria-readonly"); input.setAttribute("aria-label", "输入一句话远程编排车端功能"); input.placeholder = "例如:副驾调到舒适姿态,空调 24℃,氛围灯改成暖橙色"; } form?.addEventListener("submit", () => { const utterance = input?.value.trim(); if (utterance) appendSyncLog({ category: "task", title: "网页端发送编排指令", detail: utterance, status: "info", }); }); $$("[data-mode-command]").forEach((button) => { button.removeAttribute("aria-disabled"); button.title = "点击后由网页端发送至车机执行"; }); } function syncSidebarLogBaseline() { const sidebar = $(".v2-sidebar"); const lowerPanel = $("#v2DevicePanel") || $(".joint-twin-shell > .twin-band"); if (!sidebar || !lowerPanel) return; const offset = Math.max(0, lowerPanel.getBoundingClientRect().top - sidebar.getBoundingClientRect().top); sidebar.style.setProperty("--v2-sync-log-top", `${offset}px`); } function buildSyncControlLayer() { document.body.classList.add("v2-passive-display", "v2-interactive-sync"); document.body.dataset.module = "self-orchestration"; enableRemoteOrchestration(); const sidebar = $(".v2-sidebar"); const sidebarFooter = $(".v2-sidebar-footer", sidebar); const syncSidebar = make("section", "v2-sync-sidebar", `
    REMOTE CLIENT

    511工程-智能座舱

    网页可远程编排,车机任务、设备反馈与 USB 外设能力保持同步。

    扩展链路GSCU · 等待连接
    RUNTIME / SYNC

    运行日志 / 同步记录

      `); if (sidebar && sidebarFooter) { sidebar.insertBefore(syncSidebar, sidebarFooter); $(".v2-sync-sidebar-overview", syncSidebar)?.append(sidebarFooter); const sidebarToggle = $("#v2SidebarToggle", sidebar); if (sidebarToggle) $(".v2-sync-sidebar-heading", syncSidebar)?.append(sidebarToggle); } const naturalPanel = $("#v2NaturalPanel"); const taskOverview = make("section", "v2-task-overview", `
      当前车机任务等待车机端或网页端选择预设模式、输入一句话指令
      等待任务
      01等待车机或网页端下发编排任务
      `); naturalPanel?.append(taskOverview); const jointShell = $(".joint-twin-shell"); const cabinBand = $(".cabin-band", jointShell); if (jointShell && cabinBand) { const rightRail = make("aside", "v2-unified-right-rail", `
      LIVE VEHICLE DEVICES

      车端设备状态

      实时反馈
      `); jointShell.insertBefore(rightRail, cabinBand); const panelControls = make("div", "v2-panel-visibility-controls", ` `); $(".twin-toolbar", jointShell)?.prepend(panelControls); buildTwinDeviceInteractions(); cabinBand.classList.add("v2-source-state-only"); new MutationObserver(scheduleSyncedDeviceRender).observe(cabinBand, { attributes: true, childList: true, characterData: true, subtree: true, }); const stateBand = $(".state-band"); if (stateBand) new MutationObserver(scheduleSyncedDeviceRender).observe(stateBand, { attributes: true, childList: true, characterData: true, subtree: true, }); } setSidePanelVisibility("devices", panelVisible("devices")); setSidePanelVisibility("extensions", panelVisible("extensions")); window.addEventListener("resize", syncSidebarLogBaseline); requestAnimationFrame(syncSidebarLogBaseline); document.addEventListener("click", (event) => { const toggle = event.target.closest("[data-panel-toggle], [data-panel-close]"); if (toggle) { const key = toggle.dataset.panelToggle || toggle.dataset.panelClose; setSidePanelVisibility(key, !panelVisible(key)); } }); const deviceGrid = $("#v2DeviceSyncGrid"); deviceGrid?.addEventListener("click", (event) => { const controlToggle = event.target.closest("[data-device-control-toggle]"); if (controlToggle) { const deviceId = controlToggle.dataset.deviceControlToggle; displaySync.controlDevice = displaySync.controlDevice === deviceId ? null : deviceId; if (displaySync.controlDevice) presentTwinDevice(deviceId, { pinned: true }); else if (displaySync.pinnedDevice === deviceId) clearTwinDevicePreview({ force: true }); renderSyncedDevices(); return; } const apply = event.target.closest("[data-device-control-apply]"); if (apply) { sendDeviceControl(apply.dataset.deviceControlApply); return; } const focus = event.target.closest("[data-device-focus]"); if (focus) { const samePinned = displaySync.pinnedDevice === focus.dataset.deviceFocus; if (samePinned) clearTwinDevicePreview({ force: true }); else presentTwinDevice(focus.dataset.deviceFocus, { pinned: true }); } }); deviceGrid?.addEventListener("input", (event) => { if (event.target.matches("[data-device-control]")) storeDeviceControlDraft(event.target); }); deviceGrid?.addEventListener("change", (event) => { if (event.target.matches("select[data-device-control]")) storeDeviceControlDraft(event.target); }); $("#v2LogFilter")?.addEventListener("change", renderSyncLogs); window.addEventListener("cognitive-agent:result", (event) => { const result = event.detail || {}; appendSyncLog({ key: result.request_id ? `result:${result.request_id}` : "", category: "task", title: ["succeeded", "partially_succeeded"].includes(result.status) ? "编排任务执行完成" : "编排任务返回结果", detail: `${result.summary || result.message || "执行反馈已同步"} · ${result.status || "unknown"}`, status: ["succeeded", "partially_succeeded"].includes(result.status) ? "success" : result.status === "failed" ? "error" : "info", }); }); window.addEventListener("cognitive-agent:request-complete", (event) => { const pendingDevice = Object.entries(displaySync.pendingControls).find(([, utterance]) => utterance === event.detail?.utterance)?.[0]; if (pendingDevice) { const device = syncedDevices.find((item) => item.id === pendingDevice); delete displaySync.pendingControls[pendingDevice]; if (!event.detail?.error) delete displaySync.deviceDrafts[pendingDevice]; appendSyncLog({ category: "device", title: event.detail?.error ? `${device?.name || "设备"}控制发送失败` : `${device?.name || "设备"}控制已执行`, detail: event.detail?.error?.message || "控制结果已进入车机状态同步链路", status: event.detail?.error ? "error" : "success", }); renderSyncedDevices(); } if (event.detail?.error) { appendSyncLog({ category: "task", title: "编排任务执行失败", detail: event.detail.error.message || "请求失败", status: "error" }); } }); window.addEventListener("cognitive-agent:connection", (event) => { const online = event.detail?.status === "online"; appendSyncLog({ key: `connection:${event.detail?.status}`, category: "sync", title: online ? "车机服务连接正常" : "车机服务连接异常", detail: online ? "远程控制与状态同步通道可用" : String(event.detail?.status || "offline"), status: online ? "success" : "error", }); }); renderSyncedDevices(); setTaskPresentation({ status: "idle" }); appendSyncLog({ key: "client:ready", category: "sync", title: "同步控制台已就绪", detail: "正在连接车机任务与设备状态服务", status: "info" }); pollVehicleDisplaySync(); displaySync.pollTimer = window.setInterval(pollVehicleDisplaySync, 2000); const updateClock = () => { if ($("#v2SyncClock")) $("#v2SyncClock").textContent = new Intl.DateTimeFormat("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, }).format(new Date()); }; updateClock(); window.setInterval(updateClock, 1000); } function setPage(pageIndex) { ui.activePage = 0; const definition = pageDefinitions[ui.activePage]; $(".v2-output-shell")?.classList.toggle("v2-twin-active", definition.index === 0); $$(".v2-output-page").forEach((page) => { const active = Number(page.dataset.v2Page) === ui.activePage; page.classList.toggle("v2-active-output", active); page.setAttribute("aria-hidden", active ? "false" : "true"); }); $$(".v2-page-button").forEach((button) => { const active = Number(button.dataset.v2PageTarget) === ui.activePage; button.classList.toggle("active", active); button.setAttribute("aria-current", active ? "page" : "false"); }); $("#v2PageIndex").textContent = String(definition.index).padStart(2, "0"); $("#v2PageTitle").textContent = definition.label; $("#v2OutputState").textContent = definition.index === 0 ? "LIVE STATE" : "AUDIT VIEW"; requestAnimationFrame(updateScrollCues); } function updateScrollCues() { $$(".result-workspace .tab-panel").forEach((panel) => { const active = panel.classList.contains("v2-active-output"); panel.classList.toggle( "v2-scrollable", active && panel.scrollHeight > panel.clientHeight + 6, ); }); } function updateModeEdgeHints() { const chips = $(".v2-mode-rail .mode-chips"); if (!chips) return; const buttons = $$('button', chips); buttons.forEach((button) => { button.classList.remove("v2-edge-fade-left", "v2-edge-fade-right"); button.style.removeProperty("--v2-edge-fade-start"); button.style.removeProperty("--v2-edge-fade-end"); }); if (buttons.length < 2 || chips.clientWidth <= 0) return; const viewport = chips.getBoundingClientRect(); const visible = buttons.map((button) => ({ button, rect: button.getBoundingClientRect(), })).filter(({ rect }) => { return rect.right > viewport.left + 1 && rect.left < viewport.right - 1; }); if (!visible.length) return; const fadeSpan = 30; const firstVisible = visible[0]; const lastVisible = visible[visible.length - 1]; if (firstVisible.rect.left < viewport.left - 1) { const clippedAt = Math.min(100, Math.max( 0, ((viewport.left - firstVisible.rect.left) / firstVisible.rect.width) * 100, )); firstVisible.button.style.setProperty("--v2-edge-fade-start", `${clippedAt}%`); firstVisible.button.style.setProperty( "--v2-edge-fade-end", `${Math.min(100, clippedAt + fadeSpan)}%`, ); firstVisible.button.classList.add("v2-edge-fade-left"); } if (lastVisible.rect.right > viewport.right + 1) { const clippedAt = Math.min(100, Math.max( 0, ((viewport.right - lastVisible.rect.left) / lastVisible.rect.width) * 100, )); lastVisible.button.style.setProperty( "--v2-edge-fade-start", `${Math.max(0, clippedAt - fadeSpan)}%`, ); lastVisible.button.style.setProperty("--v2-edge-fade-end", `${clippedAt}%`); lastVisible.button.classList.add("v2-edge-fade-right"); } } function setControlMode(mode, { triggerGesture = true } = {}) { ui.controlMode = mode === "gesture" ? "gesture" : "natural"; sessionStorage.setItem("cognitive-agent.control-mode", ui.controlMode); document.body.dataset.controlMode = ui.controlMode; const requestStatus = $(".v2-request-status"); const statusTarget = ui.controlMode === "gesture" ? $("#v2GestureExecutionSlot") : $("#v2NaturalGrid"); if (requestStatus && statusTarget && requestStatus.parentElement !== statusTarget) { statusTarget.append(requestStatus); } $$('[data-v2-control]').forEach((button) => { const active = button.dataset.v2Control === ui.controlMode; button.classList.toggle("active", active); button.setAttribute("aria-pressed", String(active)); }); requestAnimationFrame(updateModeEdgeHints); const drawer = $("#gestureDrawer"); if (!triggerGesture) { const gestureVisible = ui.controlMode === "gesture"; drawer?.setAttribute("aria-hidden", String(!gestureVisible)); $("#gestureToggleButton")?.setAttribute("aria-expanded", String(gestureVisible)); return; } if (ui.controlMode === "gesture") { if (!drawer?.classList.contains("open")) $("#gestureToggleButton")?.click(); } else if (drawer?.classList.contains("open")) { $("#gestureCloseButton")?.click(); } } function setModule(module, { persist = true } = {}) { const allowed = new Set(["self-orchestration", "self-extension"]); ui.activeModule = allowed.has(module) ? module : "self-orchestration"; if (persist) { sessionStorage.setItem("cognitive-agent.active-module", ui.activeModule); } document.body.dataset.module = ui.activeModule; $$('.v2-module-tab').forEach((button) => { const active = button.dataset.module === ui.activeModule; button.classList.toggle("active", active); button.setAttribute("aria-selected", String(active)); button.tabIndex = active ? 0 : -1; }); if (ui.activeModule === "self-extension") { const drawer = $("#gestureDrawer"); if (drawer?.classList.contains("open")) $("#gestureCloseButton")?.click(); } else { setControlMode(ui.controlMode, { triggerGesture: false }); } window.dispatchEvent(new CustomEvent("cognitive-agent:module-change", { detail: { module: ui.activeModule }, })); window.setTimeout(() => window.dispatchEvent(new Event("resize")), 120); } function setSidebarCollapsed(collapsed) { ui.sidebarCollapsed = Boolean(collapsed); sessionStorage.setItem( "cognitive-agent.sidebar-collapsed", String(ui.sidebarCollapsed), ); document.body.classList.toggle("v2-sidebar-collapsed", ui.sidebarCollapsed); const button = $("#v2SidebarToggle"); if (button) { button.setAttribute("aria-expanded", String(!ui.sidebarCollapsed)); button.setAttribute( "aria-label", ui.sidebarCollapsed ? "展开左侧控制台" : "折叠左侧控制台", ); button.innerHTML = `${ui.sidebarCollapsed ? "展开" : "收起"}`; } window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); window.setTimeout(() => window.dispatchEvent(new Event("resize")), 180); } function requestUserSafeExecutionMode() { const dryRunButton = $('[data-execution-mode="dry_run"]'); const note = $("#v2ExecutionNote"); const userTarget = $(".v2-user-target-status"); const userTargetText = $("#v2UserTargetText"); const runButton = $("#runButton"); if (!dryRunButton) return; if (!dryRunButton.classList.contains("active")) dryRunButton.click(); if (note) note.textContent = "user 固定使用虚拟 CAN 安全执行环境"; if (userTarget) userTarget.dataset.state = "ready"; if (userTargetText) userTargetText.textContent = "执行目标:虚拟 CAN"; if (runButton && !runButton.classList.contains("loading")) runButton.disabled = false; syncExecutionSelector(); } function syncExecutionSelector() { const select = $("#v2ExecutionSelect"); if (!select) return; const active = $('[data-execution-mode].active'); if (active) select.value = active.dataset.executionMode; ["dry_run", "simulated_hardware", "hardware"].forEach((value) => { const option = $(`option[value="${value}"]`, select); const button = $(`[data-execution-mode="${value}"]`); if (option && button) option.disabled = button.disabled; }); dropdownControls.get(select)?.sync(); } function syncRuntimePresentation() { const modelBadge = $("#modelBadge"); const connectionBadge = $("#connectionBadge"); const modelValue = $("#v2ModelValue"); const modelNote = $("#v2ModelNote"); const connectionText = $("#v2ConnectionText"); if (modelBadge && modelValue) { const text = modelBadge.textContent.trim() || "模型检测中"; modelValue.textContent = text; const unavailable = modelBadge.classList.contains("danger"); $("#v2ModelField")?.classList.toggle("unavailable", unavailable); $("#v2ModelState").textContent = unavailable ? "LOCAL" : "READY"; if (modelNote) modelNote.textContent = unavailable ? "GPT 不可用,自动使用本地规则" : "服务端安全托管 · 失败自动降级"; } if (connectionBadge && connectionText) { connectionText.textContent = connectionBadge.textContent.trim() || "正在连接服务"; const online = !connectionBadge.classList.contains("danger") && !connectionBadge.classList.contains("warning"); $(".v2-sidebar-footer")?.classList.toggle("offline", !online); } syncExecutionSelector(); requestUserSafeExecutionMode(); } function setVehicleLinkState(connected) { const card = $("#v2VehicleCard"); const state = $("#v2VehicleState"); if (!card || !state) return; card.dataset.state = connected ? "online" : "offline"; state.textContent = connected ? "READY" : "OFFLINE"; } async function refreshVehicleLinkState() { const card = $("#v2VehicleCard"); const state = $("#v2VehicleState"); if (card) card.dataset.state = "checking"; if (state) state.textContent = "CHECK"; try { const response = await fetch("/api/extension/health", { credentials: "same-origin", headers: { Accept: "application/json" }, }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const health = await response.json(); setVehicleLinkState(Boolean(health.vehicle?.connected)); } catch (_error) { setVehicleLinkState(false); } } function bindEvents() { $$('[data-v2-control]').forEach((button) => { button.addEventListener("click", () => { setControlMode(button.dataset.v2Control); }); }); $$('.v2-module-tab').forEach((button) => { button.addEventListener("click", () => { if (!button.disabled) setModule(button.dataset.module); }); }); $("#v2ExecutionSelect")?.addEventListener("change", (event) => { $(`[data-execution-mode="${event.target.value}"]`)?.click(); syncExecutionSelector(); }); $$(".v2-page-button").forEach((button) => { button.addEventListener("click", () => setPage(button.dataset.v2PageTarget)); }); $("#v2LogoutButton")?.addEventListener("click", () => $("#logoutButton")?.click()); window.addEventListener("cognitive-agent:connection", (event) => { if (event.detail?.status === "online") refreshVehicleLinkState(); else if (["offline", "degraded"].includes(event.detail?.status)) setVehicleLinkState(false); }); $("#v2SidebarToggle")?.addEventListener( "click", () => setSidebarCollapsed(!ui.sidebarCollapsed), ); document.addEventListener("click", () => { closeCustomDropdowns(); closeReasoningPicker(); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") { closeCustomDropdowns(); closeReasoningPicker(); clearTwinDevicePreview({ force: true }); } }); window.addEventListener("resize", updateScrollCues); window.addEventListener("resize", updateModeEdgeHints); window.addEventListener("cognitive-agent:result", () => requestAnimationFrame(updateScrollCues)); const modeChips = $(".v2-mode-rail .mode-chips"); modeChips?.addEventListener( "scroll", () => requestAnimationFrame(updateModeEdgeHints), { passive: true }, ); modeChips?.addEventListener("wheel", (event) => { if (modeChips.scrollWidth <= modeChips.clientWidth + 2) return; const distance = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY; if (!distance) return; event.preventDefault(); modeChips.scrollBy({ left: distance, behavior: "smooth" }); window.requestAnimationFrame(updateModeEdgeHints); }, { passive: false }); if (modeChips) { new MutationObserver(() => requestAnimationFrame(updateModeEdgeHints)) .observe(modeChips, { childList: true }); } const observer = new MutationObserver(syncRuntimePresentation); [$("#modelBadge"), $("#connectionBadge"), $("#hardwareMode"), $("#simulationMode")] .filter(Boolean) .forEach((node) => observer.observe(node, { attributes: true, childList: true, subtree: true, characterData: true, })); } function init() { const workspace = $(".workspace"); const commandPanel = $(".command-panel"); const mainPanel = $(".main-panel"); const originalForm = $("#requestForm"); if (!workspace || !commandPanel || !mainPanel || !originalForm) return; document.body.classList.add("console-v2"); const panelHeading = $(".panel-heading", commandPanel); const modeRail = $(".mode-rail", commandPanel); const exampleGroup = $(".example-group", commandPanel); const safetyStrip = $("#safetyStrip", commandPanel); const requestHeader = $(".request-header", mainPanel); const gestureDrawer = $("#gestureDrawer"); const detached = { panelHeading, modeRail, requestHeader, gestureDrawer, originalForm, exampleGroup, safetyStrip, }; Object.values(detached).filter(Boolean).forEach((node) => node.remove()); const legacyParking = make("div", "v2-legacy-parking v2-legacy-hidden", ""); [exampleGroup, safetyStrip].filter(Boolean).forEach((node) => legacyParking.append(node)); document.body.append(legacyParking); const configStack = buildSidebar(commandPanel); buildConfigFields(configStack, originalForm); enhanceConfiguredDropdowns(); buildOperation( mainPanel, originalForm, panelHeading, modeRail, requestHeader, gestureDrawer, ); buildModuleSwitcher(mainPanel); $(".pipeline", mainPanel)?.classList.add("v2-legacy-hidden"); $(".topbar")?.classList.add("v2-legacy-hidden"); $(".gesture-backdrop")?.classList.add("v2-legacy-hidden"); buildOutput(mainPanel); buildSyncControlLayer(); bindEvents(); setControlMode(ui.controlMode, { triggerGesture: false }); setModule(ui.activeModule, { persist: false }); setSidebarCollapsed(ui.sidebarCollapsed); setPage(0); syncRuntimePresentation(); refreshVehicleLinkState(); window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } }); requestAnimationFrame(() => { updateModeEdgeHints(); document.body.classList.add("v2-ready"); }); } window.CognitiveAgentConsoleV2 = { enhanceSelect, getModule: () => ui.activeModule, getRole: () => "user", setModule, }; document.addEventListener("DOMContentLoaded", init); }());