feat(n7web): sync N7 scene cards and optimize runtime
This commit is contained in:
@@ -389,6 +389,11 @@
|
||||
let selectedPetCardId = null;
|
||||
const petCardDrafts = new Map();
|
||||
let nfcPollTimer = null;
|
||||
let nfcDialogFingerprint = "";
|
||||
let nfcDialogDirty = true;
|
||||
let unifiedExtensionFingerprint = "";
|
||||
let unifiedExtensionDirty = true;
|
||||
let gscuHealthFingerprint = "";
|
||||
let legacySameOriginChecked = false;
|
||||
let legacyMigrationCompleted = false;
|
||||
const legacyBridgeAttempts = new Set();
|
||||
@@ -3673,12 +3678,50 @@
|
||||
return reader?.error || "请连接 NFC 读卡器并确认 CH340 / CH341 驱动可用";
|
||||
}
|
||||
|
||||
function renderNfcDialog() {
|
||||
function currentNfcDialogFingerprint() {
|
||||
return JSON.stringify({
|
||||
revision: nfcSnapshot.revision,
|
||||
reader: nfcSnapshot.reader,
|
||||
card: nfcSnapshot.card,
|
||||
petRevision: petCardSnapshot.revision,
|
||||
pets: petCardSnapshot.cards,
|
||||
publisher: petCardSnapshot.publisher,
|
||||
devices: state.devices.map((device) => ({
|
||||
id: device.id,
|
||||
name: device.name,
|
||||
type: device.type,
|
||||
cardUid: device.cardUid || null,
|
||||
})),
|
||||
capabilities: state.capabilities.map((capability) => ({
|
||||
id: capability.id,
|
||||
deviceId: capability.deviceId,
|
||||
name: capability.name,
|
||||
aliases: capability.aliases,
|
||||
slot: capability.slot,
|
||||
status: capability.status,
|
||||
verified: capability.verified,
|
||||
enabled: capability.enabled !== false,
|
||||
})),
|
||||
infraredCommands: state.infraredCommands,
|
||||
});
|
||||
}
|
||||
|
||||
function renderNfcDialog({ force = false } = {}) {
|
||||
const dialog = $("#extensionNfcDialog");
|
||||
if (!dialog) return false;
|
||||
if (!force && !dialog.open) {
|
||||
nfcDialogDirty = true;
|
||||
return false;
|
||||
}
|
||||
const renderFingerprint = currentNfcDialogFingerprint();
|
||||
if (!force && !nfcDialogDirty && renderFingerprint === nfcDialogFingerprint) {
|
||||
return false;
|
||||
}
|
||||
const stage = $("#extensionNfcStage");
|
||||
const body = $("#extensionNfcBody");
|
||||
const status = $("#extensionNfcReaderStatus");
|
||||
const badge = $("#extensionNfcReaderBadge");
|
||||
if (!stage || !body || !status || !badge) return;
|
||||
if (!stage || !body || !status || !badge) return false;
|
||||
|
||||
const reader = nfcSnapshot.reader || {};
|
||||
const card = nfcSnapshot.card;
|
||||
@@ -3725,7 +3768,10 @@
|
||||
${capabilities.length ? `<label class="v2-nfc-select-all"><input type="checkbox" data-nfc-select-all ${allEnabled ? "checked" : ""} /><span><strong>全选</strong><small>统一允许或禁止当前设备 ID 下的全部命令</small></span></label><div class="v2-nfc-command-list">${capabilities.map((capability) => `<label class="v2-nfc-command-option"><input type="checkbox" data-nfc-capability-enabled="${escapeHtml(capability.id)}" ${capability.enabled !== false ? "checked" : ""} /><span><strong>${escapeHtml(capability.name)}</strong><small>IR ${String(capability.slot).padStart(3, "0")} · ${escapeHtml((capability.aliases || []).join("、"))}</small></span></label>`).join("")}</div>` : `<div class="v2-nfc-no-devices"><i data-lucide="triangle-alert"></i><div><strong>该设备 ID 暂无有效命令</strong><span>槽位可能已删除或改绑到其他设备 ID,请重新检查红外设置。</span></div></div>`}
|
||||
<div class="v2-nfc-recognized-actions"><span>复选框状态会持久化为自然语言执行白名单。</span><button type="button" data-nfc-unbind>解绑 NFC</button></div>`;
|
||||
}
|
||||
nfcDialogFingerprint = renderFingerprint;
|
||||
nfcDialogDirty = false;
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildUnifiedNfcDialog() {
|
||||
@@ -3891,8 +3937,8 @@
|
||||
}
|
||||
|
||||
function openNfcDialog() {
|
||||
renderNfcDialog();
|
||||
openDialog("extensionNfcDialog");
|
||||
renderNfcDialog({ force: true });
|
||||
void refreshNfcStatus();
|
||||
}
|
||||
|
||||
@@ -3969,10 +4015,73 @@
|
||||
if (!mount) return false;
|
||||
let snapshot = null;
|
||||
|
||||
const render = () => {
|
||||
const extensionViewFingerprint = (health) => JSON.stringify({
|
||||
transport: health.transport,
|
||||
connected: health.connected,
|
||||
peerConnected: health.peer_connected,
|
||||
peer: health.peer,
|
||||
listening: health.listening,
|
||||
message: health.message,
|
||||
extensionCatalog: (snapshot?.extension?.catalog || []).map((item) => ({
|
||||
id: item.id,
|
||||
deviceId: item.device_id,
|
||||
deviceName: item.device_name,
|
||||
deviceType: item.device_type,
|
||||
typeLabel: item.type_label,
|
||||
action: item.action,
|
||||
name: item.name,
|
||||
cardUid: item.card_uid,
|
||||
status: item.status,
|
||||
enabled: item.enabled,
|
||||
})),
|
||||
lastAction: snapshot?.extension?.last_action ? {
|
||||
command: snapshot.extension.last_action.command,
|
||||
type: snapshot.extension.last_action.type,
|
||||
} : null,
|
||||
lastResult: snapshot?.extension?.result ? {
|
||||
status: snapshot.extension.result.status,
|
||||
message: snapshot.extension.result.message,
|
||||
} : null,
|
||||
nfc: {
|
||||
revision: nfcSnapshot.revision,
|
||||
reader: nfcSnapshot.reader,
|
||||
card: nfcSnapshot.card,
|
||||
},
|
||||
pets: petCardSnapshot,
|
||||
localDevices: state.devices.map((device) => ({
|
||||
id: device.id,
|
||||
name: device.name,
|
||||
type: device.type,
|
||||
cardUid: device.cardUid || null,
|
||||
})),
|
||||
localCapabilities: state.capabilities.map((capability) => ({
|
||||
id: capability.id,
|
||||
deviceId: capability.deviceId,
|
||||
name: capability.name,
|
||||
slot: capability.slot,
|
||||
status: capability.status,
|
||||
verified: capability.verified,
|
||||
enabled: capability.enabled !== false,
|
||||
})),
|
||||
infraredCommands: state.infraredCommands,
|
||||
});
|
||||
|
||||
const render = ({ force = false } = {}) => {
|
||||
const health = snapshot?.extension_health || {};
|
||||
healthState = health;
|
||||
if ($("#extensionGscuTcpDialog")?.open) renderGscuTcpDialog(health, false);
|
||||
const nextGscuHealthFingerprint = JSON.stringify({
|
||||
transport: health.transport,
|
||||
connected: health.connected,
|
||||
peerConnected: health.peer_connected,
|
||||
peer: health.peer,
|
||||
listening: health.listening,
|
||||
message: health.message,
|
||||
});
|
||||
if ($("#extensionGscuTcpDialog")?.open
|
||||
&& (force || nextGscuHealthFingerprint !== gscuHealthFingerprint)) {
|
||||
renderGscuTcpDialog(health, false);
|
||||
}
|
||||
gscuHealthFingerprint = nextGscuHealthFingerprint;
|
||||
const embeddedNfc = health.nfc;
|
||||
if (nfcSnapshot.revision < 0 && embeddedNfc) {
|
||||
nfcSnapshot = {
|
||||
@@ -3989,6 +4098,16 @@
|
||||
}
|
||||
}
|
||||
if (health.pet_cards) applyPetCardSnapshot(health.pet_cards, { render: false });
|
||||
const panel = mount.closest("#v2ExtensionPanel");
|
||||
if (!force && (panel?.hidden || panel?.classList.contains("is-collapsed"))) {
|
||||
unifiedExtensionDirty = true;
|
||||
return false;
|
||||
}
|
||||
const renderFingerprint = extensionViewFingerprint(health);
|
||||
if (!force
|
||||
&& !unifiedExtensionDirty
|
||||
&& renderFingerprint === unifiedExtensionFingerprint
|
||||
&& mount.childElementCount) return false;
|
||||
const physicalConnected = health.transport !== "simulation"
|
||||
&& Boolean(health.connected || health.peer_connected);
|
||||
const listening = Boolean(health.listening);
|
||||
@@ -4114,7 +4233,10 @@
|
||||
<i data-lucide="workflow"></i><div><small>最近联动</small><strong>${escapeHtml(visibleLastAction?.command || visibleLastAction?.type || "等待 NFC 外设感应")}</strong></div>
|
||||
<span>${escapeHtml(visibleLastAction ? (lastResult?.status || lastResult?.message || "STANDBY") : "STANDBY")}</span>
|
||||
</footer>`;
|
||||
unifiedExtensionFingerprint = renderFingerprint;
|
||||
unifiedExtensionDirty = false;
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
return true;
|
||||
};
|
||||
renderUnifiedExtensionView = render;
|
||||
|
||||
@@ -4125,6 +4247,11 @@
|
||||
}
|
||||
render();
|
||||
});
|
||||
window.addEventListener("cognitive-agent:side-panel-visibility", (event) => {
|
||||
if (event.detail?.panel === "extensions" && event.detail?.visible) {
|
||||
render({ force: true });
|
||||
}
|
||||
});
|
||||
buildDialogs();
|
||||
buildUnifiedNfcDialog();
|
||||
bindUnifiedNfcEvents(mount);
|
||||
|
||||
Reference in New Issue
Block a user