fix(web): unify mode previews and label window positions

This commit is contained in:
Miayui
2026-09-22 14:17:50 +08:00
parent 2b365b3922
commit f4ab5f4867
12 changed files with 176 additions and 51 deletions
+70 -28
View File
@@ -553,6 +553,17 @@
function assistantControlCategory(operation = {}, fallback = "控制指令") {
const capability = String(operation.capability || "");
if (capability.startsWith("window.")) {
const target = String(operation.target?.zone || operation.target?.instance
|| operation.target_scope || (typeof operation.target === "string" ? operation.target : ""))
.replaceAll("-", "_");
return {
front_left: "左前车窗(主驾)", driver: "左前车窗(主驾)",
front_right: "右前车窗(副驾)", passenger: "右前车窗(副驾)",
rear_left: "左后车窗", rear_right: "右后车窗",
all: "全部车窗", front: "前排车窗", rear: "后排车窗",
}[target] || "车窗(位置待确认)";
}
if (capability === "infrared.emit") {
const deviceName = String(
operation.target?.name || operation.target?.device || "红外外设",
@@ -673,8 +684,30 @@
commands = [];
}
if (!commands.length) commands = fallbackAssistantControlCommands(result);
if (result.mode_profile_draft && Array.isArray(result.actions)) {
const operationSignature = (operation = {}) => {
const rawTarget = String(operation.target?.instance || operation.target?.zone
|| operation.target?.device || operation.target_scope
|| (typeof operation.target === "string" ? operation.target : ""))
.replaceAll("-", "_");
const target = ({ passenger: "front_right", driver: "front_left" })[rawTarget] || rawTarget;
const parameters = operation.parameters || {};
return JSON.stringify([operation.capability, target,
Object.keys(parameters).sort().map((key) => [key, parameters[key]])]);
};
const presentationByAction = new Map(projections.map((presentation) => [
operationSignature(presentation.operation), presentation,
]));
commands = result.actions.filter((action) => action && typeof action === "object")
.map((operation, index) => ({
operation, operationRef: `draft-action-${index}`,
semantic: String(operation.capability || ""),
command: String(presentationByAction.get(operationSignature(operation))?.actual_command || ""),
}));
}
return commands.map((item, index) => {
const operation = projectionByRef.get(String(item.operationRef || ""))
const operation = item.operation
|| projectionByRef.get(String(item.operationRef || ""))
|| operationByRef.get(String(item.operationRef || ""))
|| projections[index]?.operation
|| {};
@@ -737,7 +770,13 @@
const temperature = numericValue(parameters.temperature_c);
if (temperature !== null) return `${temperature}°C`;
const position = numericValue(parameters.position_pct ?? parameters.shade_position_pct);
if (position !== null) return `${position}%`;
if (position !== null) {
if (String(item.operation?.capability || "").startsWith("window.")) {
return position === 0 ? "0%(完全关闭)"
: position === 100 ? "100%(完全打开)" : `${position}%`;
}
return `${position}%`;
}
const illumination = String(parameters.color_hex || "").trim();
const brightnessPercent = numericValue(parameters.brightness_pct);
if (illumination && brightnessPercent !== null) return `${illumination} · ${brightnessPercent}%`;
@@ -816,17 +855,20 @@
if (!commands.length) return;
const previewOnly = String(mergedResult.execution_mode || "dry_run") !== "hardware"
|| mergedResult.orchestration_route === "mode_profile_create_preview";
const headingText = previewOnly ? "控制指令预览(未下发)" : "下发控制指令";
const headingText = mergedResult.mode_profile_draft?.name
? `${mergedResult.mode_profile_draft.name}(未下发)`
: previewOnly ? "控制指令预览(未下发)" : "下发控制指令";
const signature = JSON.stringify(commands.map((item) => [
const signature = JSON.stringify([headingText, commands.map((item) => [
item.operationRef,
item.category,
item.command,
item.semantic,
assistantControlValue(item),
item.deliveryState,
item.deliveryLabel,
item.deliveryDetail,
]));
])]);
const vueRenderer = window.CognitiveAgentVueUI?.renderDispatchedControls;
if (typeof vueRenderer === "function") {
let host = $(".assistant-dispatched-controls-vue-host", message.body);
@@ -1114,8 +1156,8 @@
all: "全部车窗",
front: "前排车窗",
rear: "后排车窗",
front_left: "主驾车窗",
front_right: "副驾车窗",
front_left: "左前车窗(主驾)",
front_right: "右前车窗(副驾)",
rear_left: "左后车窗",
rear_right: "右后车窗",
};
@@ -1383,27 +1425,15 @@
rememberModeDraftContext(draft);
supersedeModeDraftCards(draft);
const selector = `[data-assistant-mode-draft="${CSS.escape(String(draft.source_request_id))}"]`;
if ($(selector, message.body)) return;
if ($(selector, message.body)
|| message.modeDraftCard?.dataset.assistantModeDraft === String(draft.source_request_id)) return;
const card = document.createElement("section");
card.className = "assistant-mode-draft";
card.className = "assistant-mode-draft assistant-mode-draft-inline";
card.dataset.assistantModeDraft = String(draft.source_request_id);
card.dataset.assistantModeName = normalizeModeName(draft.name);
message.article?.classList.add("assistant-message-mode-draft");
const heading = document.createElement("header");
const headingCopy = document.createElement("span");
const eyebrow = document.createElement("small");
eyebrow.textContent = draft.existing_mode_id
? "已载入现有功能模式"
: "已为你生成功能模式(未下发)";
const title = document.createElement("strong");
title.textContent = `「${draft.name}」`;
headingCopy.append(eyebrow, title);
const badge = document.createElement("span");
badge.className = "assistant-mode-draft-badge";
message.modeDraftCard = card;
const scheduleView = modeDraftScheduleView(draft);
badge.textContent = `${scheduleView?.operationCount ?? (Number(draft.operation_count) || 0)} 项动作`;
heading.append(headingCopy, badge);
const list = document.createElement("div");
list.className = "assistant-mode-draft-list";
@@ -1431,12 +1461,10 @@
hint.className = "assistant-mode-schedule-hint";
hint.textContent = scheduleView.stopHint;
list.append(hint);
} else {
appendModeDraftRows(list, modeDraftRows(draft, actions));
}
const previewNotice = document.createElement("p");
previewNotice.className = "assistant-mode-draft-preview-notice";
previewNotice.textContent = "创建阶段仅生成并校验控制方案,不会向 CAN 或红外外设发送指令。";
previewNotice.textContent = `${draft.existing_mode_id ? "已载入现有功能模式。" : ""}创建阶段仅生成并校验控制方案,不会向 CAN 或红外外设发送指令。`;
list.append(previewNotice);
const controls = document.createElement("div");
@@ -1464,8 +1492,22 @@
void tryModeProfileDraft(card, draft, actions, buttons, status);
});
card.append(heading, list, controls, status);
message.body.append(card);
card.append(list, controls, status);
appendDispatchedControls(message, {
mode_profile_draft: draft, actions, execution_mode: "dry_run",
orchestration_route: "mode_profile_create_preview",
});
const host = $(".assistant-dispatched-controls-vue-host", message.body);
const attached = host && window.CognitiveAgentVueUI?.setDispatchedControlsFooter?.(
host, card, { hideCommands: Boolean(scheduleView) },
);
if (!attached) {
const panel = $(".assistant-dispatched-controls", message.body);
if (!panel) return;
const commandList = $(".assistant-dispatched-controls-list", panel);
if (commandList) commandList.hidden = Boolean(scheduleView);
panel.append(card);
}
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
followLatest(true);
}