feat: add monitoring settings tab, workbench view, and inspection alert improvements
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled

This commit is contained in:
2026-10-30 12:12:04 +08:00
committed by trae-solo-agent
parent 16dd04f746
commit 4a02370006
26 changed files with 2569 additions and 233 deletions

View File

@@ -947,6 +947,66 @@ const allowedOrigins = getAllowedRendererOrigins();
if (isTrustedRendererLocation(allowedOrigins)) {
contextBridge.exposeInMainWorld("netcatty", {
...existing, ...api, ...figSpecApi,
// 设置同步:renderer localStorage 变化通知主进程权威源
settingsSync: (key, value) => ipcRenderer.invoke("netcatty:settings:sync", {
key,
value,
delete: value === null,
}),
settingsSyncBatch: (entries) => ipcRenderer.invoke("netcatty:settings:syncBatch", { entries }),
settingsGetAll: () => ipcRenderer.invoke("netcatty:settings:getAll"),
// 远程浏览器改了设置 → 主窗口实时拉取更新 localStorage
onSettingsChanged: (callback) => {
const handler = () => {
ipcRenderer.invoke("netcatty:settings:getAll").then((r) => {
if (r?.ok && r.settings && typeof r.settings === "object") {
// 逐键对比更新(不暴力清空,避免触发过度重渲染)
try {
const changedKeys = [];
const changedEntries = {};
const existingKeys = new Set(Object.keys(localStorage));
for (const [k, v] of Object.entries(r.settings)) {
if (v === null || v === undefined) {
if (localStorage.getItem(k) !== null) {
localStorage.removeItem(k);
changedKeys.push(k);
changedEntries[k] = null;
}
} else {
const strVal = String(v);
if (localStorage.getItem(k) !== strVal) {
localStorage.setItem(k, strVal);
changedKeys.push(k);
changedEntries[k] = strVal;
}
}
existingKeys.delete(k);
}
// 权威源里没有但本地有的键——保留(可能是 Electron 专有设置)
// 只有实际有变化时才触发事件
if (changedKeys.length > 0) {
// 触发同窗口 CustomEvent 让 React hooks 感知(携带完整 settings 和变化的 key 列表)
try {
window.dispatchEvent(new CustomEvent("netcatty-settings-batch", {
detail: { changedKeys, changedEntries, settings: r.settings },
}));
} catch {}
try { window.dispatchEvent(new Event("monitoring-camera-changed")); } catch {}
// 向后兼容:触发原生 storage 事件(不带 key 信息,同窗口内依赖它的 hook 至少能收到通知)
try { window.dispatchEvent(new Event("storage")); } catch {}
// 回调同时传递完整 settings 字典和逐 key 变化信息
try { callback({ settings: r.settings, changedKeys, changedEntries }); } catch {}
} else {
// 即使没有变化也通知(权威源同步确认)
try { callback({ settings: r.settings, changedKeys: [], changedEntries: {} }); } catch {}
}
} catch {}
}
}).catch(() => {});
};
ipcRenderer.on("netcatty:settings:changed", handler);
return () => ipcRenderer.removeListener("netcatty:settings:changed", handler);
},
// portable 版标志:renderer 据此跳过 LS hosts_v1 加密读取,
// 直接走 inspection-data.json 明文(DPAPI 绑定 userData 路径解不开 dev 加密 blob)。
isPortable: process.env.NETCATTY_PORTABLE === "1",