feat: make mode creation preview-only and cache-safe
This commit is contained in:
@@ -552,7 +552,8 @@
|
||||
}
|
||||
|
||||
.assistant-mode-schedule-summary,
|
||||
.assistant-mode-schedule-hint {
|
||||
.assistant-mode-schedule-hint,
|
||||
.assistant-mode-draft-preview-notice {
|
||||
margin: 0;
|
||||
padding: 9px 12px;
|
||||
color: var(--assistant-muted, #64748b);
|
||||
@@ -568,10 +569,18 @@
|
||||
}
|
||||
|
||||
.assistant-mode-schedule-phase + .assistant-mode-schedule-phase,
|
||||
.assistant-mode-schedule-hint {
|
||||
.assistant-mode-schedule-hint,
|
||||
.assistant-mode-draft-preview-notice {
|
||||
border-top: 1px solid var(--assistant-border, #e5eaf1);
|
||||
}
|
||||
|
||||
.assistant-mode-draft-preview-notice {
|
||||
margin: 0;
|
||||
color: var(--status-success-foreground, #087854);
|
||||
background: var(--status-success-soft, #ebf9f3);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.assistant-mode-schedule-phase > header {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
|
||||
@@ -569,6 +569,11 @@
|
||||
"seat.set_backrest_recline": "靠背",
|
||||
"seat.set_legrest_height": "腿托",
|
||||
"seat.set_legrest_extension": "腿托",
|
||||
"seat.set_ventilation": "通风",
|
||||
"seat.set_heating": "加热",
|
||||
"seat.set_driver_ventilation_native": "通风",
|
||||
"seat.set_driver_heating_native": "加热",
|
||||
"seat.set_driver_massage_native": "按摩",
|
||||
}[capability];
|
||||
if (component) return `${seat}${component}`;
|
||||
if (capability === "seat.set_driver_axis_native") {
|
||||
@@ -585,7 +590,19 @@
|
||||
return seat;
|
||||
}
|
||||
if (capability.startsWith("outlet.")) return "空调风口";
|
||||
return String(fallback || capability || "控制指令");
|
||||
const domain = String(operation.domain || capability.split(".", 1)[0] || "");
|
||||
return {
|
||||
climate: "空调",
|
||||
window: "车窗",
|
||||
roof: "遮阳帘",
|
||||
ambient_light: "氛围灯",
|
||||
interior_light: "室内灯",
|
||||
self_extension: "红外外设",
|
||||
door: "车门",
|
||||
outlet: "空调风口",
|
||||
}[domain] || (/^[\u4e00-\u9fff]/.test(String(fallback || ""))
|
||||
? String(fallback)
|
||||
: "控制指令");
|
||||
}
|
||||
|
||||
function fallbackAssistantControlCommands(result = {}) {
|
||||
@@ -620,6 +637,8 @@
|
||||
}
|
||||
|
||||
function assistantDispatchedCommands(result = {}) {
|
||||
const previewOnly = String(result.execution_mode || "dry_run") !== "hardware"
|
||||
|| result.orchestration_route === "mode_profile_create_preview";
|
||||
const projections = Array.isArray(result.validated_control_commands)
|
||||
? result.validated_control_commands
|
||||
: [];
|
||||
@@ -652,22 +671,30 @@
|
||||
} catch (_error) {
|
||||
commands = [];
|
||||
}
|
||||
if (!commands.length) return fallbackAssistantControlCommands(result);
|
||||
if (!commands.length) commands = fallbackAssistantControlCommands(result);
|
||||
return commands.map((item, index) => {
|
||||
const operation = projectionByRef.get(String(item.operationRef || ""))
|
||||
|| operationByRef.get(String(item.operationRef || ""))
|
||||
|| projections[index]?.operation
|
||||
|| {};
|
||||
const pendingDelivery = result.status === "processing"
|
||||
const pendingDelivery = result.status === "processing" && !previewOnly
|
||||
? {
|
||||
deliveryState: "submitting",
|
||||
deliveryLabel: "正在下发",
|
||||
deliveryDetail: "",
|
||||
}
|
||||
: {};
|
||||
const previewDelivery = previewOnly
|
||||
? {
|
||||
deliveryState: "not_submitted",
|
||||
deliveryLabel: "仅预览 · 未下发",
|
||||
deliveryDetail: "创建模式期间不会发送控制指令",
|
||||
}
|
||||
: {};
|
||||
return {
|
||||
...item,
|
||||
...pendingDelivery,
|
||||
...previewDelivery,
|
||||
category: assistantControlCategory(operation, item.category),
|
||||
operation,
|
||||
};
|
||||
@@ -691,6 +718,11 @@
|
||||
manual: "手动",
|
||||
internal: "内循环",
|
||||
external: "外循环",
|
||||
romantic: "浪漫",
|
||||
cinema: "影院",
|
||||
rest: "休息",
|
||||
camping: "露营",
|
||||
meditation: "冥想",
|
||||
}[normalized] || String(value ?? "").trim();
|
||||
}
|
||||
|
||||
@@ -764,6 +796,9 @@
|
||||
message.dispatchedControlResult = mergedResult;
|
||||
const commands = assistantDispatchedCommands(mergedResult);
|
||||
if (!commands.length) return;
|
||||
const previewOnly = String(mergedResult.execution_mode || "dry_run") !== "hardware"
|
||||
|| mergedResult.orchestration_route === "mode_profile_create_preview";
|
||||
const headingText = previewOnly ? "控制指令预览(未下发)" : "下发控制指令";
|
||||
|
||||
const signature = JSON.stringify(commands.map((item) => [
|
||||
item.operationRef,
|
||||
@@ -794,7 +829,7 @@
|
||||
deliveryLabel: String(item.deliveryLabel || "未下发"),
|
||||
deliveryDetail: String(item.deliveryDetail || ""),
|
||||
}));
|
||||
if (vueRenderer(host, viewCommands)) {
|
||||
if (vueRenderer(host, viewCommands, { heading: headingText })) {
|
||||
host.dataset.commandSignature = signature;
|
||||
message.article?.classList.add("assistant-message-with-dispatched-controls");
|
||||
focusLatestDispatchedControls(host);
|
||||
@@ -821,7 +856,7 @@
|
||||
headingIcon.className = "assistant-dispatched-controls-heading-icon";
|
||||
headingIcon.innerHTML = '<i data-lucide="list-tree"></i>';
|
||||
const heading = document.createElement("b");
|
||||
heading.textContent = "下发控制指令";
|
||||
heading.textContent = headingText;
|
||||
const count = document.createElement("strong");
|
||||
count.className = "assistant-dispatched-controls-count";
|
||||
const chevron = document.createElement("i");
|
||||
@@ -835,6 +870,8 @@
|
||||
}
|
||||
|
||||
card.dataset.commandSignature = signature;
|
||||
const heading = $("summary b", card);
|
||||
if (heading) heading.textContent = headingText;
|
||||
const count = $(".assistant-dispatched-controls-count", card);
|
||||
const list = $(".assistant-dispatched-controls-list", card);
|
||||
if (count) count.textContent = String(commands.length);
|
||||
@@ -925,6 +962,30 @@
|
||||
indexes.forEach((index) => consumed.add(index));
|
||||
rows.push({ label, value, icon, order });
|
||||
};
|
||||
const domainLabels = {
|
||||
climate: "空调",
|
||||
window: "车窗",
|
||||
roof: "遮阳帘",
|
||||
seat: "座椅",
|
||||
ambient_light: "氛围灯",
|
||||
interior_light: "室内灯",
|
||||
self_extension: "红外外设",
|
||||
door: "车门",
|
||||
outlet: "空调风口",
|
||||
};
|
||||
const capabilityLabels = {
|
||||
"climate.set_power": "空调电源",
|
||||
"climate.set_temperature": "空调温度",
|
||||
"climate.set_fan_level": "空调风量",
|
||||
"climate.set_cycle_mode": "空调循环",
|
||||
"climate.set_control_mode": "空调控制模式",
|
||||
"climate.set_outlet_mode": "空调出风模式",
|
||||
"window.set_position": "车窗位置",
|
||||
"roof.set_shade_position": "遮阳帘位置",
|
||||
"ambient_light.set_power": "氛围灯",
|
||||
"ambient_light.set_mode": "氛围灯模式",
|
||||
"infrared.emit": "红外外设",
|
||||
};
|
||||
|
||||
const zeroGravityIndexes = actions
|
||||
.map((action, index) => ({ action, index }))
|
||||
@@ -978,6 +1039,65 @@
|
||||
if (position === 100) return `完全打开(${target})`;
|
||||
return target;
|
||||
};
|
||||
const localizedParameterValue = (value) => ({
|
||||
on: "开启", off: "关闭", open: "打开", close: "关闭", stop: "停止",
|
||||
auto: "自动", manual: "手动", internal: "内循环", external: "外循环",
|
||||
romantic: "浪漫", cinema: "影院", rest: "休息", camping: "露营",
|
||||
}[String(value)] || String(value));
|
||||
const genericParameterText = (params) => Object.entries(params)
|
||||
.filter(([key]) => key !== "slot" && key !== "capability_ref")
|
||||
.map(([key, rawValue]) => {
|
||||
const name = {
|
||||
state: "状态", motion: "动作", action: "动作", mode: "模式",
|
||||
level: "档位", temperature_c: "温度", position_pct: "目标开度",
|
||||
brightness_pct: "亮度", brightness_level: "亮度档位",
|
||||
color_hex: "颜色", color_index: "色号",
|
||||
}[key] || "参数";
|
||||
const suffix = key === "temperature_c" ? "℃"
|
||||
: ["position_pct", "brightness_pct"].includes(key) ? "%"
|
||||
: key === "level" || key === "brightness_level" ? "档" : "";
|
||||
return `${name}:${localizedParameterValue(rawValue)}${suffix}`;
|
||||
})
|
||||
.join(" · ");
|
||||
|
||||
const passengerComfort = actions
|
||||
.map((action, index) => ({ action, index }))
|
||||
.filter(({ action, index }) => !consumed.has(index)
|
||||
&& ["passenger", "front_right"].includes(targetZone(action))
|
||||
&& [
|
||||
"seat.set_ventilation",
|
||||
"seat.set_backrest_recline",
|
||||
"seat.set_legrest_height",
|
||||
].includes(String(action.capability || "")));
|
||||
const comfortByCapability = new Map(passengerComfort.map(({ action, index }) => [
|
||||
String(action.capability || ""), { action, index },
|
||||
]));
|
||||
if ([
|
||||
"seat.set_ventilation",
|
||||
"seat.set_backrest_recline",
|
||||
"seat.set_legrest_height",
|
||||
].every((capability) => comfortByCapability.has(capability))) {
|
||||
const ventilation = parameters(comfortByCapability.get("seat.set_ventilation").action);
|
||||
const backrest = parameters(comfortByCapability.get("seat.set_backrest_recline").action);
|
||||
const legrest = parameters(comfortByCapability.get("seat.set_legrest_height").action);
|
||||
const comfortMentions = ["座椅", "副驾", "舒适"]
|
||||
.map((term) => instruction.indexOf(term))
|
||||
.filter((index) => index >= 0);
|
||||
const comfortOrder = comfortMentions.length
|
||||
? Math.min(...comfortMentions)
|
||||
: 10_000 + Math.max(...passengerComfort.map(({ index }) => index));
|
||||
addRow(
|
||||
"副驾座椅舒适模式",
|
||||
[
|
||||
`通风${ventilation.state === "off" ? "关闭" : `开启 ${ventilation.level ?? 1}档`}`,
|
||||
`靠背 ${backrest.position_pct ?? "--"}%`,
|
||||
`腿托 ${legrest.position_pct ?? "--"}%`,
|
||||
].join(" · "),
|
||||
"armchair",
|
||||
comfortOrder,
|
||||
passengerComfort.map(({ index }) => index),
|
||||
);
|
||||
}
|
||||
actions.forEach((action, index) => {
|
||||
if (consumed.has(index)) return;
|
||||
const capability = String(action.capability || "");
|
||||
@@ -988,12 +1108,20 @@
|
||||
front_row: "前排", rear_row: "后排", rear_left: "左后", rear_right: "右后",
|
||||
}[zone] || "";
|
||||
const params = parameters(action);
|
||||
let label = capability || domain || "座舱设备";
|
||||
let label = capabilityLabels[capability] || domainLabels[domain] || "座舱设备";
|
||||
let value = "已配置";
|
||||
let icon = "sliders-horizontal";
|
||||
let order = 10_000 + index;
|
||||
|
||||
if (capability === "roof.set_shade_motion" || capability === "roof.set_shade_position") {
|
||||
if (capability === "infrared.emit" || domain === "self_extension") {
|
||||
label = String(action?.target?.name || action?.target?.device || "红外外设").trim()
|
||||
|| "红外外设";
|
||||
value = params.action != null
|
||||
? `动作:${localizedParameterValue(params.action)}`
|
||||
: "动作已配置";
|
||||
icon = "radio-tower";
|
||||
order = instructionOrder(label, "外设", "灯");
|
||||
} else if (capability === "roof.set_shade_motion" || capability === "roof.set_shade_position") {
|
||||
label = "遮阳帘";
|
||||
value = params.position_pct != null
|
||||
? shadePositionLabel(params.position_pct)
|
||||
@@ -1011,7 +1139,13 @@
|
||||
rear_right: "右后车窗",
|
||||
};
|
||||
label = windowLabels[zone] || "车窗";
|
||||
value = params.position_pct != null ? `${params.position_pct}%` : motionLabel(params.motion);
|
||||
value = params.position_pct != null
|
||||
? Number(params.position_pct) === 0
|
||||
? "完全关闭(目标开度 0%)"
|
||||
: Number(params.position_pct) === 100
|
||||
? "完全打开(目标开度 100%)"
|
||||
: `目标开度 ${params.position_pct}%`
|
||||
: motionLabel(params.motion);
|
||||
icon = "panels-top-left";
|
||||
order = instructionOrder("车窗");
|
||||
} else if (domain === "seat") {
|
||||
@@ -1071,7 +1205,8 @@
|
||||
face_feet_defrost: "吹面 / 吹脚 / 除霜", face_defrost: "吹面 / 除霜",
|
||||
},
|
||||
};
|
||||
label = capability === "climate.set_fan_level" ? "空调风量"
|
||||
label = capability === "climate.set_power" ? "空调电源"
|
||||
: capability === "climate.set_fan_level" ? "空调风量"
|
||||
: capability === "climate.set_cycle_mode" ? "空调循环"
|
||||
: capability === "climate.set_control_mode" ? "空调控制模式"
|
||||
: capability === "climate.set_outlet_mode" ? "空调出风模式" : "空调";
|
||||
@@ -1104,6 +1239,9 @@
|
||||
icon = "lightbulb";
|
||||
order = instructionOrder(label, "灯光");
|
||||
}
|
||||
if (value === "已配置") {
|
||||
value = genericParameterText(params) || "参数已配置";
|
||||
}
|
||||
addRow(label, value, icon, order, [index]);
|
||||
});
|
||||
return rows.sort((left, right) => left.order - right.order);
|
||||
@@ -1219,12 +1357,12 @@
|
||||
card.classList.add("is-busy");
|
||||
status.hidden = false;
|
||||
status.className = "assistant-mode-draft-status working";
|
||||
status.textContent = "正在进行模拟试运行…";
|
||||
status.textContent = "正在重新进行 dry-run 校验(不会下发)…";
|
||||
try {
|
||||
const result = await window.CognitiveAgentConsole?.tryModeDraft?.({ ...draft, actions });
|
||||
if (!result || result.status !== "succeeded") throw new Error("试运行未通过,请检查动作设置。");
|
||||
status.className = "assistant-mode-draft-status success";
|
||||
status.textContent = "试运行通过 · 尚未保存";
|
||||
status.textContent = "校验通过 · 未下发 · 尚未保存";
|
||||
} catch (error) {
|
||||
status.className = "assistant-mode-draft-status failed";
|
||||
status.textContent = error.message || "试运行失败,请重试。";
|
||||
@@ -1275,7 +1413,7 @@
|
||||
const eyebrow = document.createElement("small");
|
||||
eyebrow.textContent = draft.existing_mode_id
|
||||
? "已载入现有功能模式"
|
||||
: "已为你生成功能模式";
|
||||
: "已为你生成功能模式(未下发)";
|
||||
const title = document.createElement("strong");
|
||||
title.textContent = `「${draft.name}」`;
|
||||
headingCopy.append(eyebrow, title);
|
||||
@@ -1314,6 +1452,10 @@
|
||||
} else {
|
||||
appendModeDraftRows(list, modeDraftRows(draft, actions));
|
||||
}
|
||||
const previewNotice = document.createElement("p");
|
||||
previewNotice.className = "assistant-mode-draft-preview-notice";
|
||||
previewNotice.textContent = "创建阶段仅生成并校验控制方案,不会向 CAN 或红外外设发送指令。";
|
||||
list.append(previewNotice);
|
||||
|
||||
const controls = document.createElement("div");
|
||||
controls.className = "assistant-mode-draft-actions";
|
||||
@@ -1326,7 +1468,7 @@
|
||||
const trial = document.createElement("button");
|
||||
trial.type = "button";
|
||||
trial.className = "assistant-mode-draft-trial";
|
||||
trial.textContent = "先试运行";
|
||||
trial.textContent = "重新校验(不下发)";
|
||||
controls.append(save, trial);
|
||||
const status = document.createElement("p");
|
||||
status.className = "assistant-mode-draft-status";
|
||||
@@ -1379,12 +1521,23 @@
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
}
|
||||
|
||||
function isModeCreationPreviewRequest(message) {
|
||||
const compact = String(message || "").trim().replace(/\s+/g, "");
|
||||
if (!compact || /取消(?:创建|构建|生成)|(?:不要|别|禁止)(?:创建|构建|生成)/.test(compact)) {
|
||||
return false;
|
||||
}
|
||||
return /(?:构建|创建|新建|新增|生成|添加)(?:一下)?(?:一个|个|一套|套)?[^,。!?!?]{1,64}?模式/.test(compact);
|
||||
}
|
||||
|
||||
function requestBody(message) {
|
||||
const executionMode = window.CognitiveAgentConsole?.getExecutionMode?.() || "dry_run";
|
||||
const configuredExecutionMode = window.CognitiveAgentConsole?.getExecutionMode?.() || "dry_run";
|
||||
const controlTransport = localStorage.getItem("cognitive-agent.control-transport") || "wired_can";
|
||||
const hardwareClientId = localStorage.getItem("cognitive-agent.hardware-client-id");
|
||||
const vehicleId = $("#vehicleSelect")?.value || null;
|
||||
const modeProfileDraftContext = requestModeDraftContext(message);
|
||||
const modeConfigurationPreview = Boolean(modeProfileDraftContext)
|
||||
|| isModeCreationPreviewRequest(message);
|
||||
const executionMode = modeConfigurationPreview ? "dry_run" : configuredExecutionMode;
|
||||
return {
|
||||
message,
|
||||
conversation_id: conversationId(),
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<script src="/pwa.js?v=android-pad-3" defer></script>
|
||||
<script src="/console-v2.js?v=local-first-77-performance" defer></script>
|
||||
<script src="/self-extension.js?v=nfc-mqtt-56" defer></script>
|
||||
<script src="/assistant-chat.js?v=codex-assistant-34" defer></script>
|
||||
<script src="/assistant-chat.js?v=codex-assistant-35" defer></script>
|
||||
<script src="/energy-monitor.js?v=energy-monitor-5-performance" defer></script>
|
||||
<script src="/firmware-workbench-entry.js?v=3" defer></script>
|
||||
</head>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE_VERSION = "cognitive-agent-pad-v328-nfc-mqtt";
|
||||
const CACHE_VERSION = "cognitive-agent-pad-v329-mode-preview";
|
||||
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
||||
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
|
||||
const APP_SHELL = [
|
||||
@@ -16,13 +16,13 @@ const APP_SHELL = [
|
||||
"/speech-input.css?v=sensevoice-7",
|
||||
"/speech-input.js?v=sensevoice-13",
|
||||
"/speech-audio-worklet.js?v=sensevoice-5",
|
||||
"/assistant-chat.css?v=codex-assistant-29",
|
||||
"/assistant-chat.js?v=codex-assistant-34",
|
||||
"/assistant-chat.css?v=codex-assistant-30",
|
||||
"/assistant-chat.js?v=codex-assistant-35",
|
||||
"/energy-monitor.css?v=energy-monitor-5-dialog-size",
|
||||
"/energy-monitor.js?v=energy-monitor-5-performance",
|
||||
"/ui-consistency.css?v=spatial-ui-8",
|
||||
"/codepen-glass-theme.css?v=creative-cloud-6-performance",
|
||||
"/vue-ui.js?v=vue-presentation-3",
|
||||
"/vue-ui.js?v=vue-presentation-4",
|
||||
"/firmware-workbench-entry.css?v=2",
|
||||
"/firmware-workbench-entry.js?v=3",
|
||||
"/firmware-workbench.html",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { cpSync, copyFileSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import {
|
||||
cpSync,
|
||||
copyFileSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { basename, dirname, extname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
@@ -45,6 +53,74 @@ const runtimeFiles = [
|
||||
"codepen-glass-theme.css",
|
||||
"vue-ui.js"
|
||||
];
|
||||
const fingerprintedRuntimeFiles = runtimeFiles.filter((file) => (
|
||||
/\.(?:css|js)$/.test(file)
|
||||
&& !["service-worker.js", "speech-audio-worklet.js"].includes(file)
|
||||
));
|
||||
const rewrittenHtmlFiles = [
|
||||
"index.html",
|
||||
"login.html",
|
||||
"hardware-control.html",
|
||||
"firmware-workbench.html",
|
||||
];
|
||||
|
||||
function contentHash(value) {
|
||||
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
||||
}
|
||||
|
||||
function hashedRuntimeName(file, hash) {
|
||||
const extension = extname(file);
|
||||
return `${basename(file, extension)}.${hash}${extension}`;
|
||||
}
|
||||
|
||||
function rewriteRuntimeReferences(value, manifest) {
|
||||
let rewritten = value;
|
||||
for (const [source, target] of Object.entries(manifest)) {
|
||||
const escaped = source.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
rewritten = rewritten.replace(
|
||||
new RegExp(`/${escaped}(?:\\?[^\\s\"'<>)]*)?`, "g"),
|
||||
`/${target}`,
|
||||
);
|
||||
}
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
function fingerprintRuntimeAssets() {
|
||||
const manifest = {};
|
||||
for (const file of fingerprintedRuntimeFiles) {
|
||||
const source = resolve(outputRoot, file);
|
||||
if (!existsSync(source)) continue;
|
||||
const contents = readFileSync(source);
|
||||
const hashedName = hashedRuntimeName(file, contentHash(contents));
|
||||
writeFileSync(resolve(outputRoot, hashedName), contents);
|
||||
manifest[file] = hashedName;
|
||||
}
|
||||
|
||||
for (const file of rewrittenHtmlFiles) {
|
||||
const target = resolve(outputRoot, file);
|
||||
if (!existsSync(target)) continue;
|
||||
const source = readFileSync(target, "utf8");
|
||||
writeFileSync(target, rewriteRuntimeReferences(source, manifest), "utf8");
|
||||
}
|
||||
|
||||
const serviceWorkerPath = resolve(outputRoot, "service-worker.js");
|
||||
const buildHash = contentHash(JSON.stringify(manifest));
|
||||
if (existsSync(serviceWorkerPath)) {
|
||||
const serviceWorker = rewriteRuntimeReferences(
|
||||
readFileSync(serviceWorkerPath, "utf8"),
|
||||
manifest,
|
||||
).replace(
|
||||
/const CACHE_VERSION = "[^"]+";/,
|
||||
`const CACHE_VERSION = "cognitive-agent-pad-build-${buildHash}";`,
|
||||
);
|
||||
writeFileSync(serviceWorkerPath, serviceWorker, "utf8");
|
||||
}
|
||||
writeFileSync(
|
||||
resolve(outputRoot, "runtime-assets.json"),
|
||||
`${JSON.stringify({ build_hash: buildHash, assets: manifest }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
function copyRuntimeAssets() {
|
||||
return {
|
||||
@@ -64,6 +140,7 @@ function copyRuntimeAssets() {
|
||||
mkdirSync(dirname(target), { recursive: true });
|
||||
copyFileSync(source, target);
|
||||
}
|
||||
fingerprintRuntimeAssets();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ import { Fragment, createApp, h, nextTick, reactive } from "vue";
|
||||
}
|
||||
|
||||
function createCommandMount(container) {
|
||||
const state = reactive({ commands: [], open: true });
|
||||
const state = reactive({ commands: [], open: true, heading: "下发控制指令" });
|
||||
const app = createApp({
|
||||
name: "N7DispatchedControlsCard",
|
||||
setup() {
|
||||
@@ -131,7 +131,7 @@ import { Fragment, createApp, h, nextTick, reactive } from "vue";
|
||||
h("span", { class: "assistant-dispatched-controls-heading-icon" }, [
|
||||
h("i", { "data-lucide": "list-tree" }),
|
||||
]),
|
||||
h("b", null, "下发控制指令"),
|
||||
h("b", null, state.heading),
|
||||
h("strong", { class: "assistant-dispatched-controls-count" }, String(state.commands.length)),
|
||||
h("i", {
|
||||
class: "assistant-dispatched-controls-chevron",
|
||||
@@ -148,12 +148,13 @@ import { Fragment, createApp, h, nextTick, reactive } from "vue";
|
||||
return mount;
|
||||
}
|
||||
|
||||
function renderDispatchedControls(container, commands) {
|
||||
function renderDispatchedControls(container, commands, options = {}) {
|
||||
if (!(container instanceof Element) || !Array.isArray(commands) || !commands.length) {
|
||||
return false;
|
||||
}
|
||||
const mount = commandMounts.get(container) || createCommandMount(container);
|
||||
replaceReactiveArray(mount.state.commands, commands);
|
||||
mount.state.heading = text(options.heading, "下发控制指令");
|
||||
container.dataset.vueSurface = "dispatched-controls";
|
||||
refreshIcons();
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user