dsh-life-pack 0.3.40 → 0.3.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +735 -1089
- package/dist/client.js.map +1 -1
- package/dist/config-panel-view.d.ts.map +1 -1
- package/dist/config-panel-view.js +11 -0
- package/dist/config-panel-view.js.map +1 -1
- package/dist/seal-paper-schemes.d.ts +67 -0
- package/dist/seal-paper-schemes.d.ts.map +1 -0
- package/dist/seal-paper-schemes.js +135 -0
- package/dist/seal-paper-schemes.js.map +1 -0
- package/dist/seal-scroll.d.ts +9 -1
- package/dist/seal-scroll.d.ts.map +1 -1
- package/dist/seal-scroll.js +59 -8
- package/dist/seal-scroll.js.map +1 -1
- package/dist/seal-stamp.d.ts.map +1 -1
- package/dist/seal-stamp.js +4 -2
- package/dist/seal-stamp.js.map +1 -1
- package/dist/update-dialog.d.ts +7 -6
- package/dist/update-dialog.d.ts.map +1 -1
- package/dist/update-dialog.js +12 -26
- package/dist/update-dialog.js.map +1 -1
- package/package.json +2 -2
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["title","escapeHtml","isObject","asSnapshot","getTimer","defaultCopyText","escapeHtml","isObject","S","React","React","S","S","React","S","S","React","React","React"],"sources":["../src/nav.ts","../src/update-contract.ts","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/config.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/service.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/changelog.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/batch.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/redaction.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/panel.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/panel-batch.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/entry.js","../src/install.ts","../src/update-targets.ts","../src/update-dialog.ts","../src/config-panel-contract.ts","../src/directory-browser-contract.ts","../src/directory-browser-state.ts","../src/directory-browser-parts.ts","../src/directory-browser-ui.ts","../src/title-seal.ts","../src/seal-scroll.ts","../src/seal-stamp.ts","../src/config-panel-view.ts","../src/health-contract.ts","../src/health-fetch.ts","../src/health-panel.ts","../src/health-view.ts","../src/directory-browser-roots.ts","../src/client.ts"],"sourcesContent":["/** dsh-life-pack 导航表(P10 脚手架,P3 #4 定案 order)。\n *\n * 总管只导航:6 tab 描述子仅做 openTab 路径导航,内容由各单品包自注册\n * slot 提供;总管不 import 任何单品页/单品包(见 boundaries 断言)。\n * 缺席纯条件渲染:present 缺谁就回谁的 reco(推荐安装+补装提示),不轮询。\n * 子页不占栏:卡路里 diet/exercise/goal 等 ilife:calorie:* 后缀只在单品\n * 主面板内导航,绝不在此表另起 tab 行。\n * 60 deck:map 为 matt 探针已占位,本表不动它。\n *\n * 面板外壳的两张文案表也在本文件(票 #679):右上角两个入口 PANEL_LINKS、\n * 底部「作者其他插件」卡 MORE_PLUGINS。文案与网址集中在这里,\n * 组件只负责画——改文案不碰组件(map #671 的 Q11 决议)。\n */\n\nexport interface ManagerTab {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n /** 页签名(屏上逐字印这个)=**产品名**(票 #738:作息管家/居家管家/私家大厨/饼干记账)。\n *\n * 它是各家自己那个 `SLOT_TITLE` 的镜像,两处必须同值:页签注册缺席时由这一格兜底,\n * 更新列表那一行的行首也用它(`update-targets.ts` 原样取 `title`)。\n * 对齐由 `test/plugin-p10-boundaries.test.mjs` 逐家咬住——真 import 两边的产物来比,不搜字符串。 */\n readonly title: string;\n /** 提供该 tab 的单品插件包名(仅文档级引用,不 import)。 */\n readonly plugin: string;\n /** 本家自己那条客户端通道(单段,例 `/ilife-calorie`):面板按它取配置体检(体检那个端点挂在上面)。\n *\n * 这条事实为什么住在这里(票 #735):各家注册页签槽时**也**会交一份(各包契约件的 `RPC_CHANNEL`),\n * 但装机上的槽位面**不透传自定义注册选项**——条目 options 只留 `{key,id,order,label,priority}`,\n * 总管从账本上读到的那一格恒是空串。后果是六家恒被判成「没有体检出口」,\n * 面板的体检目标表恒为空,「体检一次」按下去毫无反应(第 1 版的实现就是这么哑掉的)。\n * 这份镜像由 `test/plugin-p10-boundaries.test.mjs` 逐家与各包契约件的常量对齐守着。\n * 各家注册里那一格**留着不删**:上游哪天开始透传自定义选项,可以一行切回账本。 */\n readonly channel: string;\n}\n\nexport interface RecoTab extends ManagerTab {\n readonly kind: 'reco';\n /** 补装命令(单命令双包口径,总管必须已在位)。 */\n readonly installCmd: string;\n readonly hint: string;\n}\n\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\n/** 版本号**不在本文件**:它由构建期从本包 `package.json` 注入面板产物(票 #986,见\n * `tsdown.config.ts` 与 `client.ts` 的 `__LIFE_PACK_VERSION__`)——手写常量会与包版本\n * 无声漂开一整版(#737 实测过),而注入值由构造派生、并有一条构建门咬住\n * 「产物里的注入值 ≡ 包版本」;定版仍只剩改 `package.json` 一处。 */\n\nexport const DUAL_ADD_PREFIX = 'dsh plugin add dsh-life-pack' as const;\n\nexport function dualInstallCmd(singlePlugin: string): string {\n return DUAL_ADD_PREFIX + ' ' + singlePlugin;\n}\n\n/** P3 定案 6 tab(order 升序即 + 号菜单顺序;已打开条不受 order 控制)。 */\nexport const MANAGER_TABS: readonly ManagerTab[] = [\n { skill: 'bill', slotId: 'ilife:cookie', order: 70, title: '饼干记账', plugin: 'dsh-bill-ilife', channel: '/ilife-bill-ilife' },\n { skill: 'calorie', slotId: 'ilife:calorie', order: 75, title: '卡路里', plugin: 'dsh-calorie', channel: '/ilife-calorie' },\n { skill: 'memo', slotId: 'ilife:memo', order: 80, title: '备忘录', plugin: 'dsh-memo-ilife', channel: '/ilife-memo' },\n { skill: 'schedule', slotId: 'ilife:schedule', order: 85, title: '作息管家', plugin: 'dsh-schedule-ilife', channel: '/ilife-schedule-ilife' },\n { skill: 'home', slotId: 'ilife:home', order: 90, title: '居家管家', plugin: 'dsh-home-ilife', channel: '/ilife-home-ilife' },\n { skill: 'chef', slotId: 'ilife:chef', order: 95, title: '私家大厨', plugin: 'dsh-chef', channel: '/ilife-chef' },\n];\n\nexport function recoFor(tab: ManagerTab): RecoTab {\n const installCmd = dualInstallCmd(tab.plugin);\n return {\n ...tab,\n kind: 'reco',\n installCmd,\n hint: '未安装[' + tab.plugin + '],请补装后使用:' + installCmd,\n };\n}\n\nexport type Presence = ReadonlySet<string>;\n\nexport function tabsForPresence(present: Presence): readonly (ManagerTab | RecoTab)[] {\n return MANAGER_TABS.map((t) => (present.has(t.slotId) ? t : recoFor(t)));\n}\n\nexport interface OpenPort {\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\nexport interface HasTabsPort extends OpenPort {\n hasTabs(): boolean;\n}\n\n/** 总管导航:只做 openTab 路径导航(内容型打开),缺席由调用方渲染 reco。 */\nexport function openManagerTab(port: HasTabsPort, slotId: string, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: slotId, path: slotId }, sessionId ? { sessionId } : undefined);\n}\n\n/** 面板右上角一个入口:图标 + 悬停说明 + 目标网址。 */\nexport interface PanelLinkRow {\n readonly key: 'star' | 'feedback';\n /** 图标字形(照参照实现 dsh-mattpocock-skills-deck,星星只留 ⭐、反馈只留 💬)。 */\n readonly glyph: string;\n /** 鼠标悬停给出的说明文字。 */\n readonly tip: string;\n readonly url: string;\n}\n\n/** 面板右上角两个入口:星(去本仓点 star)+ 气泡(去本仓开 issue)。\n *\n * 两处都指本仓 FeatherHunter/ilife——这是 map #671 裁定过的(「星与气泡指向哪个仓」\n * 一问:反馈要落在本仓的需求池,「引流到作者其他技能」是底部那张卡的事)。\n * 三件并排里的第三件「检查更新」住隔壁票(#678),本表不管它。\n */\nexport const PANEL_LINKS: readonly PanelLinkRow[] = [\n { key: 'star', glyph: '⭐', tip: '你的🌟是我夜空中最亮的星。', url: 'https://github.com/FeatherHunter/ilife' },\n { key: 'feedback', glyph: '💬', tip: '反馈问题', url: 'https://github.com/FeatherHunter/ilife/issues/new' },\n];\n\n/** 底部「作者其他插件」卡里的一行:包名 + 一句说明 + 行尾外链图标指向的仓库。 */\nexport interface MorePluginRow {\n readonly pkg: string;\n readonly desc: string;\n readonly url: string;\n}\n\n/** 「作者其他插件」四行:包名 + 一句说明 + 仓库地址。\n *\n * 文案口径(用户 2026-09-18 拍板):**不写数字**——数字会过期(用户截图里的「34 款」实测已到 38 款),\n * 写错最伤推荐位的可信度;第一行也不用 deck 自己面板里的自指「本面板自己」,改成不自指的说法。\n * 只收真实存在的仓库,网址已逐个验真。第一行的说明描述的是 deck 那个包(工程技能面板),不是爱生活面板。\n */\nexport const MORE_PLUGINS: readonly MorePluginRow[] = [\n {\n pkg: 'dsh-mattpocock-skills-deck',\n desc: '工程技能面板:装好就能在右侧直接用',\n url: 'https://github.com/FeatherHunter/dsh-mattpocock-skills-deck',\n },\n {\n pkg: 'dsh-opencode-palette',\n desc: '多款长时间编程护眼配色,一键换上',\n url: 'https://github.com/FeatherHunter/dsh-opencode-palette',\n },\n {\n pkg: 'dsh-prompt',\n desc: '常用提示模板随手点,不用来回复制粘贴',\n url: 'https://github.com/FeatherHunter/dsh-prompt',\n },\n {\n pkg: 'dsh-im-companion',\n desc: '聊天机器人的伴侣插件:扫码或填凭据就把飞书、微信等聊天接进来',\n url: 'https://github.com/FeatherHunter/dsh-im-companion',\n },\n];\n","/** 总管与面板之间的共享常量:载体、槽名、电话名、版本降级字面量。\n *\n * 说明(票 1168 缺席态):本仓更新代码已全部移除,更新区藏入口。\n * 本文件仅保留仍被非更新代码取用的共享位,更新专用部分已随更新代码删除。\n * 0.5.1 新接线由 wire 票 1170 重建,此处不预留。\n */\n\nexport const CONFIG_TAB_SLOT = 'ilife.config-tab' as const;\n\nexport const CARRIER_BASE = '/api' as const;\n\nexport const MANAGER_RPC = {\n base: CARRIER_BASE,\n channel: '/ilife-manager',\n endpoint: 'ilife-manager',\n path: '/api/ilife-manager',\n} as const;\n\nexport const MANAGER_ACTIONS = {\n roots: 'ilife-manager.roots',\n} as const;\n\nexport const VERSION_UNKNOWN = 'unknown' as const;\n\nconst REASON_TEXT: Record<string, string> = {\n 'manager-unreachable': '面板连不上总管插件。重启 DSH 后重试。',\n 'bad-request': '面板与总管之间的请求出错了(属程序缺陷)。',\n internal: '面板与总管之间的请求出错了(属程序缺陷)。',\n};\n\nexport function reasonText(code: string): string {\n return REASON_TEXT[code] ?? '面板不认识这个原因码(' + code + ')。';\n}\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/config.ts\nconst DEFAULT_PREFIX = \"wf\";\nconst PHONE_ACTIONS = [\"updateStatus\", \"updateCheck\", \"updateInstall\"];\nconst CHANGELOG_PHONE_ACTION = \"updateChangelog\";\nconst DEFAULT_TARGET_PACKAGE = \"dsh-mattpocock-skills-deck\";\nconst DEFAULT_REGISTRY = \"https://registry.npmjs.org/\";\nconst LEGACY_PLUGIN_ID = \"dsh-mattpocock-skills-deck\";\nconst LEGACY_DIR_SEGMENT = \"updates\";\nconst STATE_FILE = \"state.json\";\nconst LOCK_FILE = \"install.lock\";\nconst BACKUP_FILE = \"before.json\";\nconst SKIPPED_FILE = \"skipped.json\";\nconst RELEASE_CHANNELS = [\"stable\", \"prerelease\"];\nconst DEFAULT_CHECK_TIMEOUT_MS = 1e4;\nconst DEFAULT_CONFIRMATION_TTL_MS = 10 * 6e4;\nconst DEFAULT_INSTALL_TIMEOUT_MS = 15 * 6e4;\nconst DEFAULT_PANEL_POLL_MS = 1e3;\nconst RECHECK_WINDOW_MS = 2e3;\nconst MIN_PANEL_POLL_MS = 250;\nfunction assertPluginId(value) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n if (value.includes(\"/\") || value.includes(\"\\\\\") || value.includes(\"\\0\")) {\n throw new Error(\"[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u542B\\u6709\\u8DEF\\u5F84\\u5206\\u9694\\u7B26\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction assertPrefix(value, role) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n if (value.includes(\".\") || value.includes(\"/\") || value.includes(\"\\\\\") || /\\s/.test(value)) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u542B\\u6709\\u70B9\\u3001\\u8DEF\\u5F84\\u5206\\u9694\\u7B26\\u6216\\u7A7A\\u767D\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction assertPositiveFinite(value, role) {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u6709\\u9650\\u5927\\u4E8E 0 \\u7684\\u6570\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction resolveUpdateConfig(input) {\n if (!input || typeof input !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u5EFA\\u66F4\\u65B0\\u80FD\\u529B\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = assertPluginId(input.pluginId);\n const prefix = input.prefix === void 0 ? DEFAULT_PREFIX : assertPrefix(input.prefix, \"\\u7535\\u8BDD\\u540D\\u524D\\u7F00 prefix\");\n const targetPackageName = input.targetPackageName === void 0 ? DEFAULT_TARGET_PACKAGE : String(input.targetPackageName);\n if (!targetPackageName) {\n throw new Error(\"[dsh-plugin-update] \\u76EE\\u6807\\u5305\\u540D targetPackageName \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\");\n }\n const registryUrl = input.registryUrl === void 0 ? DEFAULT_REGISTRY : String(input.registryUrl);\n if (!registryUrl) {\n throw new Error(\"[dsh-plugin-update] \\u5B98\\u65B9\\u6E90 registryUrl \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\");\n }\n const homeDir = input.homeDir === void 0 ? null : String(input.homeDir);\n const checkTimeoutMs = input.checkTimeoutMs === void 0 ? DEFAULT_CHECK_TIMEOUT_MS : assertPositiveFinite(input.checkTimeoutMs, \"\\u8054\\u7F51\\u8D85\\u65F6 checkTimeoutMs\");\n const confirmationTtlMs = input.confirmationTtlMs === void 0 ? DEFAULT_CONFIRMATION_TTL_MS : assertPositiveFinite(input.confirmationTtlMs, \"\\u51ED\\u8BC1\\u6709\\u6548\\u671F confirmationTtlMs\");\n const installTimeoutMs = input.installTimeoutMs === void 0 ? DEFAULT_INSTALL_TIMEOUT_MS : assertPositiveFinite(input.installTimeoutMs, \"\\u5B89\\u88C5\\u65F6\\u9650 installTimeoutMs\");\n const panelPollMs = input.panelPollMs === void 0 ? DEFAULT_PANEL_POLL_MS : assertPositiveFinite(input.panelPollMs, \"\\u9762\\u677F\\u8F6E\\u8BE2 panelPollMs\");\n if (panelPollMs < MIN_PANEL_POLL_MS) {\n throw new Error(\"[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2 panelPollMs \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 \" + JSON.stringify(input.panelPollMs) + \"\\uFF09\");\n }\n const releaseChannel = input.releaseChannel === void 0 ? \"stable\" : input.releaseChannel;\n if (releaseChannel !== \"stable\" && releaseChannel !== \"prerelease\") {\n throw new Error(\"[dsh-plugin-update] \\u7248\\u672C\\u901A\\u9053 releaseChannel \\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 stable \\u6216 prerelease\\uFF08\\u6536\\u5230 \" + JSON.stringify(input.releaseChannel) + \"\\uFF09\");\n }\n return { pluginId, prefix, targetPackageName, registryUrl, homeDir, checkTimeoutMs, confirmationTtlMs, installTimeoutMs, panelPollMs, releaseChannel };\n}\nfunction buildPhoneNames(prefix) {\n const checked = assertPrefix(prefix, \"\\u7535\\u8BDD\\u540D\\u524D\\u7F00 prefix\");\n return {\n updateStatus: checked + \".updateStatus\",\n updateCheck: checked + \".updateCheck\",\n updateInstall: checked + \".updateInstall\",\n updateChangelog: checked + \".updateChangelog\"\n };\n}\nfunction buildChangelogPhoneName(prefix) {\n return buildPhoneNames(prefix).updateChangelog;\n}\nfunction buildPhoneName(prefix, action) {\n return buildPhoneNames(prefix)[action];\n}\nexport {\n BACKUP_FILE,\n CHANGELOG_PHONE_ACTION,\n DEFAULT_CHECK_TIMEOUT_MS,\n DEFAULT_CONFIRMATION_TTL_MS,\n DEFAULT_INSTALL_TIMEOUT_MS,\n DEFAULT_PANEL_POLL_MS,\n DEFAULT_PREFIX,\n DEFAULT_REGISTRY,\n DEFAULT_TARGET_PACKAGE,\n LEGACY_DIR_SEGMENT,\n LEGACY_PLUGIN_ID,\n LOCK_FILE,\n MIN_PANEL_POLL_MS,\n PHONE_ACTIONS,\n RECHECK_WINDOW_MS,\n RELEASE_CHANNELS,\n SKIPPED_FILE,\n STATE_FILE,\n assertPluginId,\n assertPrefix,\n buildChangelogPhoneName,\n buildPhoneName,\n buildPhoneNames,\n resolveUpdateConfig\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/service.ts\nconst PACKAGE_NAME = \"dsh-mattpocock-skills-deck\";\nconst NPM_REGISTRY = \"https://registry.npmjs.org/\";\nconst CHECK_TIMEOUT_MS = 1e4;\nconst CONFIRMATION_TTL_MS = 10 * 6e4;\nconst RECHECK_WINDOW_MS = 2e3;\nconst MAX_METADATA_BYTES = 256 * 1024;\nconst INTEGRITY_PATTERN = \"^sha512-[A-Za-z0-9+/]{86}==$\";\nfunction validRequestId(v) {\n if (typeof v !== \"string\") return false;\n const id = v.trim();\n if (id.length < 1 || id.length > 128) return false;\n if (!/^[A-Za-z0-9._~-]+$/.test(id)) return false;\n if (/^(npm_|gh[pousr]_|github_pat_|sk-|bearer)/i.test(id)) return false;\n return true;\n}\nfunction updateError(code) {\n return Object.assign(new Error(code), { code });\n}\nfunction validVersion(v) {\n return typeof v === \"string\" && /^\\d+\\.\\d+\\.\\d+$/.test(v);\n}\nconst RELEASE_CHANNELS = [\"stable\", \"prerelease\"];\nfunction validReleaseChannel(v) {\n return v === \"stable\" || v === \"prerelease\";\n}\nfunction validPrereleaseIds(ids) {\n if (typeof ids !== \"string\" || !ids) return false;\n const parts = ids.split(\".\");\n if (parts.length === 0) return false;\n for (const p of parts) {\n if (!p || !/^[0-9A-Za-z-]+$/.test(p)) return false;\n if (/^\\d+$/.test(p) && p.length > 1 && p.startsWith(\"0\")) return false;\n }\n return true;\n}\nfunction validReleaseVersion(v) {\n if (typeof v !== \"string\" || !v) return false;\n const dash = v.indexOf(\"-\");\n if (dash < 0) return validVersion(v);\n const core = v.slice(0, dash);\n const ids = v.slice(dash + 1);\n if (!validVersion(core) || !validPrereleaseIds(ids)) return false;\n if (ids.includes(\"+\")) return false;\n return true;\n}\nfunction isPrereleaseVersion(v) {\n return validReleaseVersion(v) && !validVersion(v);\n}\nfunction isVersionAllowedInChannel(version, channel = \"stable\") {\n if (channel === \"prerelease\") return validReleaseVersion(version);\n return validVersion(version);\n}\nfunction parseTriple(v) {\n const parts = String(v).split(\".\");\n if (parts.length > 3) return null;\n const nums = [];\n for (const p of parts) {\n if (!/^\\d+$/.test(p)) return null;\n const n = Number(p);\n if (!Number.isSafeInteger(n)) return null;\n nums.push(n);\n }\n while (nums.length < 3) nums.push(0);\n return [nums[0], nums[1], nums[2]];\n}\nfunction compareVersions(a, b) {\n const pa = parseTriple(a);\n const pb = parseTriple(b);\n if (!pa || !pb) throw updateError(\"invalid-release\");\n for (let i = 0; i < 3; i++) {\n if (pa[i] < pb[i]) return -1;\n if (pa[i] > pb[i]) return 1;\n }\n return 0;\n}\nfunction parseReleaseIds(ids) {\n return ids.split(\".\").map((p) => /^\\d+$/.test(p) ? Number(p) : p);\n}\nfunction compareReleaseVersions(a, b) {\n if (!validReleaseVersion(a) || !validReleaseVersion(b)) throw updateError(\"invalid-release\");\n const dashA = String(a).indexOf(\"-\");\n const dashB = String(b).indexOf(\"-\");\n const coreA = dashA < 0 ? String(a) : String(a).slice(0, dashA);\n const coreB = dashB < 0 ? String(b) : String(b).slice(0, dashB);\n const order = compareVersions(coreA, coreB);\n if (order !== 0) return order;\n const preA = dashA < 0 ? null : parseReleaseIds(String(a).slice(dashA + 1));\n const preB = dashB < 0 ? null : parseReleaseIds(String(b).slice(dashB + 1));\n if (preA === null && preB === null) return 0;\n if (preA === null) return 1;\n if (preB === null) return -1;\n const width = Math.max(preA.length, preB.length);\n for (let i = 0; i < width; i++) {\n const x = preA[i];\n const y = preB[i];\n if (x === void 0) return -1;\n if (y === void 0) return 1;\n if (typeof x === \"number\" && typeof y === \"number\") {\n if (x < y) return -1;\n if (x > y) return 1;\n continue;\n }\n if (typeof x === \"number\") return -1;\n if (typeof y === \"number\") return 1;\n if (x < y) return -1;\n if (x > y) return 1;\n }\n return 0;\n}\nfunction compareOne(node, op, target) {\n const order = compareTriple(node, target);\n if (op === \"=\" || op === \"\") return order === 0;\n if (op === \">\") return order === 1;\n if (op === \">=\") return order >= 0;\n if (op === \"<\") return order === -1;\n if (op === \"<=\") return order <= 0;\n return true;\n}\nfunction compareTriple(a, b) {\n for (let i = 0; i < 3; i++) {\n if (a[i] < b[i]) return -1;\n if (a[i] > b[i]) return 1;\n }\n return 0;\n}\nfunction upperBound(t, kind, kept) {\n if (kind === \"^\") {\n if (kept >= 1) return [t[0] + 1, 0, 0];\n return [0, t[1] + 1, 0];\n }\n if (kept >= 2) return [t[0], t[1] + 1, 0];\n return [t[0] + 1, 0, 0];\n}\nfunction satisfiesNodeRange(nodeVersion, range) {\n if (range === void 0 || range === null) return true;\n const text = String(range).trim();\n if (text === \"\" || text === \"*\") return true;\n const node = parseTriple(String(nodeVersion).replace(/^v/, \"\"));\n if (!node) return true;\n if (text.includes(\"||\")) return true;\n const parts = text.split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return true;\n for (const part of parts) {\n const m = part.match(/^(\\^|~|>=|<=|>|<|=)?(.+)$/);\n if (!m) return true;\n const op = m[1] || \"\";\n const target = parseTriple(m[2]);\n if (!target) return true;\n if (op === \"^\" || op === \"~\") {\n const kept = m[2].split(\".\").length;\n if (compareTriple(node, target) < 0) return false;\n if (compareTriple(node, upperBound(target, op, kept)) >= 0) return false;\n continue;\n }\n if (!compareOne(node, op, target)) return false;\n }\n return true;\n}\nfunction byteLength(text) {\n try {\n return new TextEncoder().encode(text).length;\n } catch {\n return text.length;\n }\n}\nfunction timeoutSignal(ms) {\n try {\n const ctor = globalThis.AbortSignal;\n if (ctor && typeof ctor.timeout === \"function\") return ctor.timeout(ms);\n } catch {\n }\n return void 0;\n}\nfunction httpStatusOf(response) {\n const raw = response?.status;\n return typeof raw === \"number\" && Number.isInteger(raw) && raw >= 100 && raw <= 599 ? raw : null;\n}\nfunction checkFailedWithStatus(response) {\n const err = updateError(\"check-failed\");\n const status = httpStatusOf(response);\n if (status !== null) err.httpStatus = status;\n return err;\n}\nasync function fetchNpmRelease(fetchImpl, timeoutMs = CHECK_TIMEOUT_MS, opts) {\n const targetName = opts?.targetPackageName ?? PACKAGE_NAME;\n const registry = opts?.registryUrl ?? NPM_REGISTRY;\n const channel = opts?.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n let response = null;\n try {\n response = await fetchImpl(`${registry}${encodeURIComponent(targetName)}/latest`, {\n headers: { accept: \"application/json\" },\n redirect: \"error\",\n signal: timeoutSignal(timeoutMs)\n });\n } catch {\n throw updateError(\"check-failed\");\n }\n try {\n if (!response.ok) throw checkFailedWithStatus(response);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const text = await response.text();\n if (byteLength(text) > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const value = JSON.parse(text);\n if (value.name !== targetName || !isVersionAllowedInChannel(value.version, channel)) throw updateError(\"invalid-release\");\n const version = value.version;\n const nodeRange = value.engines?.node;\n if (nodeRange !== void 0 && typeof nodeRange !== \"string\") throw updateError(\"invalid-release\");\n const tarballText = value.dist?.tarball;\n const integrity = value.dist?.integrity;\n let tarball;\n try {\n tarball = new URL(String(tarballText));\n } catch {\n throw updateError(\"invalid-release\");\n }\n const registryOrigin = new URL(registry).origin;\n const shapeOk = tarball.origin === registryOrigin && !tarball.username && !tarball.password && !tarball.search && !tarball.hash && tarball.pathname === `/${targetName}/-/${targetName}-${version}.tgz` && typeof integrity === \"string\" && new RegExp(INTEGRITY_PATTERN).test(integrity);\n if (!shapeOk) throw updateError(\"invalid-release\");\n return { version, nodeRange: typeof nodeRange === \"string\" ? nodeRange : \"*\", integrity, tarball: tarball.href };\n } catch (error) {\n if (error?.code === \"check-failed\") throw error;\n if (error?.code === \"invalid-release\") throw error;\n if (response !== null && response.ok) throw updateError(\"invalid-release\");\n throw checkFailedWithStatus(response);\n }\n}\nasync function fetchNpmVersionRelease(fetchImpl, timeoutMs = CHECK_TIMEOUT_MS, opts) {\n const targetName = opts?.targetPackageName ?? PACKAGE_NAME;\n const registry = opts?.registryUrl ?? NPM_REGISTRY;\n const channel = opts?.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n const version = opts?.version;\n if (typeof version !== \"string\" || !validReleaseVersion(version) || !isVersionAllowedInChannel(version, channel)) {\n throw updateError(\"invalid-release\");\n }\n let response = null;\n try {\n response = await fetchImpl(`${registry}${encodeURIComponent(targetName)}/${encodeURIComponent(version)}`, {\n headers: { accept: \"application/json\" },\n redirect: \"error\",\n signal: timeoutSignal(timeoutMs)\n });\n } catch {\n throw updateError(\"check-failed\");\n }\n try {\n if (!response.ok) throw checkFailedWithStatus(response);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const text = await response.text();\n if (byteLength(text) > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const value = JSON.parse(text);\n if (value.name !== targetName || value.version !== version || !isVersionAllowedInChannel(value.version, channel)) {\n throw updateError(\"invalid-release\");\n }\n const nodeRange = value.engines?.node;\n if (nodeRange !== void 0 && typeof nodeRange !== \"string\") throw updateError(\"invalid-release\");\n const tarballText = value.dist?.tarball;\n const integrity = value.dist?.integrity;\n let tarball;\n try {\n tarball = new URL(String(tarballText));\n } catch {\n throw updateError(\"invalid-release\");\n }\n const registryOrigin = new URL(registry).origin;\n const shapeOk = tarball.origin === registryOrigin && !tarball.username && !tarball.password && !tarball.search && !tarball.hash && tarball.pathname === `/${targetName}/-/${targetName}-${version}.tgz` && typeof integrity === \"string\" && new RegExp(INTEGRITY_PATTERN).test(integrity);\n if (!shapeOk) throw updateError(\"invalid-release\");\n return { version, nodeRange: typeof nodeRange === \"string\" ? nodeRange : \"*\", integrity, tarball: tarball.href };\n } catch (error) {\n if (error?.code === \"check-failed\") throw error;\n if (error?.code === \"invalid-release\") throw error;\n if (response !== null && response.ok) throw updateError(\"invalid-release\");\n throw checkFailedWithStatus(response);\n }\n}\nfunction createUpdateCore(ports) {\n const checkTimeoutMs = ports.checkTimeoutMs ?? CHECK_TIMEOUT_MS;\n const confirmationTtlMs = ports.confirmationTtlMs ?? CONFIRMATION_TTL_MS;\n const targetPackageName = ports.targetPackageName ?? PACKAGE_NAME;\n const registryUrl = ports.registryUrl ?? NPM_REGISTRY;\n const releaseChannel = ports.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n function fetchRelease() {\n return fetchNpmRelease(ports.fetchImpl, checkTimeoutMs, { targetPackageName, registryUrl, releaseChannel });\n }\n let checked = null;\n let checking = null;\n let lastCheckAt = -Infinity;\n let activeJobId = null;\n let memJob = null;\n let memLock = null;\n async function loadJob() {\n try {\n return ports.readJob ? await ports.readJob() ?? null : memJob;\n } catch {\n throw updateError(\"install-failed\");\n }\n }\n async function saveJob(job) {\n try {\n if (ports.writeJob) await ports.writeJob(job);\n else memJob = job;\n } catch {\n throw updateError(\"install-failed\");\n }\n }\n async function acquire(lockId) {\n try {\n if (ports.tryAcquireLock) return await ports.tryAcquireLock(lockId);\n } catch {\n return false;\n }\n if (memLock !== null) return false;\n memLock = lockId;\n return true;\n }\n async function release(lockId) {\n try {\n if (ports.releaseLock) await ports.releaseLock(lockId);\n else if (memLock === lockId) memLock = null;\n } catch {\n }\n }\n function healJob(job, env) {\n if (!job) return null;\n if (job.state === \"installing\" || job.state === \"verifying\") {\n return job.id === activeJobId ? job : { ...job, state: \"interrupted\", message: \"recovery-required\", requestId: job.requestId ?? null };\n }\n if (job.state === \"restart-required\" || job.state === \"completed\") {\n if (!job.targetVersion || env.installedVersion !== job.targetVersion) {\n return { ...job, state: \"interrupted\", message: \"installation-changed\", requestId: job.requestId ?? null };\n }\n const runningVersion = ports.readRunningVersion();\n return { ...job, state: runningVersion === job.targetVersion ? \"completed\" : \"restart-required\", requestId: job.requestId ?? null };\n }\n return job;\n }\n function buildSnapshot(env, job) {\n let blockedReason = env.blockedReason ?? checked?.blockedReason ?? null;\n if (job?.state === \"interrupted\" || job?.state === \"failed\") {\n if (env.installedVersion !== ports.readRunningVersion()) blockedReason = \"recovery-required\";\n } else if (job?.state === \"restart-required\" || env.installedVersion && env.installedVersion !== ports.readRunningVersion()) {\n blockedReason = \"pending-restart\";\n }\n const busy = job?.state === \"installing\" || job?.state === \"verifying\";\n const fresh = checked !== null && checked.checkId !== null && ports.now() < checked.expiresAt;\n const runningVersion = ports.readRunningVersion();\n let newer = false;\n try {\n newer = isVersionAllowedInChannel(runningVersion, releaseChannel) && !!checked?.release && isVersionAllowedInChannel(checked.release.version, releaseChannel) && compareReleaseVersions(checked.release.version, runningVersion) === 1;\n } catch {\n newer = false;\n }\n const canInstall = Boolean(\n env.eligible && !blockedReason && !busy && fresh && checked?.installationKey === env.installationKey && newer\n );\n return {\n runningVersion,\n installedVersion: env.installedVersion ?? null,\n latestVersion: checked?.release.version ?? null,\n canInstall,\n blockedReason,\n job\n };\n }\n async function status() {\n const env = await ports.readInstalled();\n const job = healJob(await loadJob(), env);\n return buildSnapshot(env, job);\n }\n async function check() {\n if (checking) return checking;\n if (checked?.checkId && ports.now() - lastCheckAt < RECHECK_WINDOW_MS) {\n const env = await ports.readInstalled();\n const snapshot = buildSnapshot(env, healJob(await loadJob(), env));\n return { snapshot, receipt: snapshot.canInstall ? toReceipt() : null };\n }\n lastCheckAt = ports.now();\n checking = (async () => {\n try {\n const env = await ports.readInstalled();\n const release2 = await fetchRelease();\n checked = {\n release: release2,\n checkId: ports.randomId(),\n checkedAt: ports.now(),\n expiresAt: ports.now() + confirmationTtlMs,\n installationKey: env.installationKey,\n blockedReason: satisfiesNodeRange(ports.nodeVersion, release2.nodeRange) ? null : \"incompatible-node\"\n };\n const snapshot = buildSnapshot(env, healJob(await loadJob(), env));\n return { snapshot, receipt: snapshot.canInstall ? toReceipt() : null };\n } catch (error) {\n if (checked) checked = { ...checked, checkId: null, expiresAt: 0 };\n throw error;\n } finally {\n checking = null;\n }\n })();\n return checking;\n }\n function toReceipt() {\n if (!checked?.checkId) return null;\n return { checkId: checked.checkId, checkedAt: checked.checkedAt, expiresAt: checked.expiresAt };\n }\n async function runBackground(job, envAtStart) {\n try {\n if (ports.runInstall) await ports.runInstall({ version: job.targetVersion, profileName: envAtStart.profileName, environmentKind: envAtStart.environmentKind });\n else throw updateError(\"unsupported\");\n const doing = { ...job, state: \"verifying\", message: null };\n activeJobId = doing.id;\n await saveJob(doing);\n const env = await ports.readInstalled();\n if (env.installedVersion !== job.targetVersion) {\n throw Object.assign(updateError(\"install-failed\"), {\n detail: `\\u88C5\\u5B8C\\u6821\\u9A8C\\u6CA1\\u8FC7\\uFF1A\\u78C1\\u76D8\\u4E0A\\u662F ${env.installedVersion ?? \"\\u672A\\u77E5\"}\\uFF0C\\u76EE\\u6807\\u662F ${job.targetVersion ?? \"\\u672A\\u77E5\"}`\n });\n }\n if (env.blockedReason && env.blockedReason !== \"pending-restart\") {\n throw Object.assign(updateError(\"install-failed\"), { detail: `\\u88C5\\u5B8C\\u6821\\u9A8C\\u6CA1\\u8FC7\\uFF1A\\u73AF\\u5883\\u62A5\\u544A ${env.blockedReason}` });\n }\n const done = { ...doing, state: \"restart-required\", message: null };\n await saveJob(done);\n } catch (error) {\n const code = error?.code;\n const base = code === \"installation-changed\" || code === \"registry-conflict\" || code === \"install-failed\" ? String(code) : \"install-failed\";\n const detail = error?.detail;\n const message = typeof detail === \"string\" && detail ? `${base}: ${detail}` : base;\n const failedJob = { ...job, state: \"failed\", message, requestId: job.requestId ?? null };\n try {\n await saveJob(failedJob);\n } catch {\n memJob = failedJob;\n }\n } finally {\n await release(job.id);\n if (activeJobId === job.id) activeJobId = null;\n }\n }\n async function install(args) {\n const checkId = typeof args?.checkId === \"string\" ? args.checkId : \"\";\n const requestId = typeof args?.requestId === \"string\" ? args.requestId : \"\";\n if (!checkId || !validRequestId(requestId)) throw updateError(\"check-expired\");\n let env = await ports.readInstalled();\n let previous = healJob(await loadJob(), env);\n if (previous?.requestId === requestId) return buildSnapshot(env, previous);\n if (previous?.state === \"installing\" || previous?.state === \"verifying\" || previous?.state === \"restart-required\") {\n throw updateError(\"update-busy\");\n }\n if (!checked || !checked.checkId || checked.checkId !== checkId || ports.now() >= checked.expiresAt) {\n throw updateError(\"check-expired\");\n }\n if (env.installationKey !== checked.installationKey) throw updateError(\"installation-changed\");\n if (!buildSnapshot(env, previous).canInstall) {\n throw updateError(env.blockedReason ?? checked.blockedReason ?? \"update-busy\");\n }\n const job = {\n id: ports.randomId(),\n state: \"installing\",\n targetVersion: checked.release.version,\n message: null,\n requestId\n };\n if (!await acquire(job.id)) throw updateError(\"update-busy\");\n try {\n const current = await fetchRelease();\n if (JSON.stringify(current) !== JSON.stringify(checked.release)) throw updateError(\"check-expired\");\n env = await ports.readInstalled();\n if (env.installationKey !== checked.installationKey) throw updateError(\"installation-changed\");\n if (!env.eligible || env.blockedReason) throw updateError(env.blockedReason ?? \"update-busy\");\n if (ports.backupJob) await ports.backupJob(job);\n await saveJob(job);\n activeJobId = job.id;\n } catch (error) {\n await release(job.id);\n throw error;\n }\n void runBackground(job, env).catch(() => {\n });\n return buildSnapshot(env, job);\n }\n return { status, check, install };\n}\nexport {\n CHECK_TIMEOUT_MS,\n CONFIRMATION_TTL_MS,\n INTEGRITY_PATTERN,\n MAX_METADATA_BYTES,\n NPM_REGISTRY,\n PACKAGE_NAME,\n RECHECK_WINDOW_MS,\n RELEASE_CHANNELS,\n compareReleaseVersions,\n compareVersions,\n createUpdateCore,\n fetchNpmRelease,\n fetchNpmVersionRelease,\n isPrereleaseVersion,\n isVersionAllowedInChannel,\n satisfiesNodeRange,\n updateError,\n validReleaseChannel,\n validReleaseVersion,\n validRequestId,\n validVersion\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/changelog.ts\nimport { compareReleaseVersions, validReleaseVersion } from \"./service.js\";\nconst CHANGELOG_FILENAME = \"CHANGELOG.md\";\nconst CHANGELOG_NEUTRAL_HINT = \"\\u4F5C\\u8005\\u672A\\u63D0\\u4F9B\\u66F4\\u65B0\\u8BF4\\u660E\";\nconst CHANGELOG_NEUTRAL_LINE = \"\\u4F5C\\u8005\\u672A\\u63D0\\u4F9B\\u66F4\\u65B0\\u8BF4\\u660E\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\u3002\";\nconst CHANGELOG_MAX_CHARS = 64 * 1024;\nconst CHANGELOG_MAX_ENTRIES = 100;\nconst CHANGELOG_MAX_BULLETS_PER_SECTION = 200;\nconst CHANGELOG_MAX_BULLET_CHARS = 500;\nconst CHANGELOG_ALL_CATEGORIES = [\n \"Added\",\n \"Fixed\",\n \"Changed\",\n \"Deprecated\",\n \"Removed\",\n \"Security\"\n];\nconst CHANGELOG_MUST_SHOW = [\"Added\", \"Fixed\", \"Changed\", \"Security\"];\nconst CHANGELOG_FOLDED = [\"Deprecated\", \"Removed\"];\nconst CHANGELOG_CATEGORY_ZH = {\n Added: \"\\u65B0\\u589E\",\n Fixed: \"\\u4FEE\\u590D\",\n Changed: \"\\u53D8\\u66F4\",\n Deprecated: \"\\u5F03\\u7528\\u9884\\u544A\",\n Removed: \"\\u79FB\\u9664\",\n Security: \"\\u5B89\\u5168\"\n};\nfunction emptySections() {\n return { Added: [], Fixed: [], Changed: [], Deprecated: [], Removed: [], Security: [] };\n}\nfunction emptyCounts() {\n return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n}\nfunction isCategoryName(v) {\n return CHANGELOG_ALL_CATEGORIES.includes(v);\n}\nfunction normalizeCategory(raw) {\n const t = String(raw || \"\").trim().toLowerCase();\n if (t === \"added\") return \"Added\";\n if (t === \"fixed\") return \"Fixed\";\n if (t === \"changed\") return \"Changed\";\n if (t === \"deprecated\") return \"Deprecated\";\n if (t === \"removed\") return \"Removed\";\n if (t === \"security\") return \"Security\";\n return null;\n}\nfunction isUnreleasedVersion(v) {\n return typeof v === \"string\" && v.trim().toLowerCase() === \"unreleased\";\n}\nfunction extractVersionDateAndYanked(title) {\n const t = String(title || \"\").trim();\n if (!t) return { version: null, date: null, yanked: false };\n const yanked = /\\[YANKED\\]/i.test(t);\n if (/unreleased/i.test(t)) return { version: \"Unreleased\", date: null, yanked };\n const vm = t.match(/(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)/);\n if (!vm) return { version: null, date: null, yanked: false };\n const dm = t.match(/(\\d{4}-\\d{2}-\\d{2})/);\n return { version: vm[1], date: dm ? dm[1] : null, yanked };\n}\nfunction truncateBullet(text) {\n const t = String(text || \"\").replace(/\\s+/g, \" \").trim();\n if (!t) return \"\";\n if (t.length <= CHANGELOG_MAX_BULLET_CHARS) return t;\n return `${t.slice(0, CHANGELOG_MAX_BULLET_CHARS - 1)}\\u2026`;\n}\nconst RE_VERSION_HEADING = /^##(?!#)\\s*(.+?)\\s*$/;\nconst RE_CATEGORY_HEADING = /^###\\s*(.+?)\\s*$/;\nconst RE_BULLET = /^\\s*[-*+]\\s+(.+?)\\s*$/;\nconst RE_FENCE_TOGGLE = new RegExp(\"^\\\\s*(\" + String.fromCharCode(96).repeat(3) + \"|~~~)\");\nconst RE_VERSION_NUM = /(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)/;\nconst RE_VERSION_DATE = /(\\d{4}-\\d{2}-\\d{2})/;\nconst RE_YANKED = /\\[YANKED\\]/i;\nconst RE_UNRELEASED = /unreleased/i;\nfunction matchVersionHeading(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_VERSION_HEADING);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\").trim();\n } catch {\n return null;\n }\n}\nfunction matchCategoryHeading(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_CATEGORY_HEADING);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\").trim();\n } catch {\n return null;\n }\n}\nfunction matchBulletBody(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_BULLET);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\");\n } catch {\n return null;\n }\n}\nfunction isFenceToggle(line) {\n try {\n return RE_FENCE_TOGGLE.test(String(line !== null && line !== void 0 ? line : \"\"));\n } catch {\n return false;\n }\n}\nconst BREAKING_BADGE_TEXT = \"\\u4E0D\\u517C\\u5BB9\";\nfunction stripBreakingDecorations(s) {\n try {\n let t = String(s !== null && s !== void 0 ? s : \"\").trim();\n let changed = true;\n while (changed) {\n changed = false;\n if (t.charAt(0) === \">\") {\n t = t.slice(1).trim();\n changed = true;\n continue;\n }\n if (t.slice(0, 2) === \"**\" || t.slice(0, 2) === \"__\") {\n t = t.slice(2).trim();\n changed = true;\n continue;\n }\n if (t.charAt(0) === \"*\" || t.charAt(0) === \"_\") {\n t = t.slice(1).trim();\n changed = true;\n continue;\n }\n }\n return t;\n } catch {\n return \"\";\n }\n}\nfunction isBreakingChangelogItem(item) {\n try {\n if (typeof item !== \"string\") return false;\n const t = stripBreakingDecorations(item);\n if (!t) return false;\n return /^(breaking|不兼容)\\s*[::]/i.test(t);\n } catch {\n return false;\n }\n}\nfunction splitBreakingPrefix(item) {\n try {\n const s = String(item !== null && item !== void 0 ? item : \"\");\n const m = s.match(/^(\\s*(?:>\\s*|\\*\\*\\s*|__\\s*|\\*\\s*|_\\s*)*)(breaking|不兼容)(\\s*[::])/i);\n if (!m) return null;\n const head = String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\");\n const core = String(m[2] !== void 0 && m[2] !== null ? m[2] : \"\");\n const colon = String(m[3] !== void 0 && m[3] !== null ? m[3] : \"\");\n const prefix = core + colon;\n const rest = s.slice(m[0].length);\n return { head, prefix, rest };\n } catch {\n return null;\n }\n}\nconst CHANGELOG_POLL_BACKOFF_MS = 30 * 1e3;\nfunction shouldFetchChangelog(opts) {\n try {\n const o = opts;\n if (o.hasCache === true) return false;\n if (o.isManual === true) return true;\n const now = typeof o.now === \"number\" && Number.isFinite(o.now) ? o.now : Date.now();\n const failedAt = typeof o.failedAt === \"number\" && Number.isFinite(o.failedAt) ? o.failedAt : null;\n if (failedAt === null) return true;\n const backoff = typeof o.backoffMs === \"number\" && Number.isFinite(o.backoffMs) && o.backoffMs > 0 ? Math.floor(o.backoffMs) : CHANGELOG_POLL_BACKOFF_MS;\n return now - failedAt >= backoff;\n } catch {\n return true;\n }\n}\nfunction parseChangelog(markdown) {\n try {\n if (typeof markdown !== \"string\" || !markdown.trim()) return [];\n let text = markdown.replace(/\\r\\n/g, \"\\n\");\n if (text.length > CHANGELOG_MAX_CHARS) text = text.slice(0, CHANGELOG_MAX_CHARS);\n const lines = text.split(\"\\n\");\n const entries = [];\n let cur = null;\n let curCat = null;\n let inFence = false;\n const pushCurrent = () => {\n if (!cur) return;\n const hasAny = CHANGELOG_ALL_CATEGORIES.some(\n (c) => cur.sections[c].length > 0\n );\n if (!hasAny) return;\n for (const c of CHANGELOG_ALL_CATEGORIES) {\n const list = cur.sections[c];\n if (list.length > CHANGELOG_MAX_BULLETS_PER_SECTION) {\n cur.sections[c] = list.slice(0, CHANGELOG_MAX_BULLETS_PER_SECTION);\n }\n }\n entries.push(cur);\n };\n for (const rawLine of lines) {\n const line = String(rawLine ?? \"\");\n if (isFenceToggle(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const versionTitle = matchVersionHeading(line);\n if (versionTitle !== null) {\n pushCurrent();\n cur = null;\n curCat = null;\n const title = versionTitle;\n const { version, date, yanked } = extractVersionDateAndYanked(title);\n if (!version) continue;\n cur = { version, date, yanked, sections: emptySections(), counts: emptyCounts() };\n continue;\n }\n const catTitle = matchCategoryHeading(line);\n if (catTitle !== null) {\n const cat = normalizeCategory(catTitle);\n curCat = cur && cat && isCategoryName(cat) ? cat : null;\n continue;\n }\n const bulletBody = matchBulletBody(line);\n if (bulletBody !== null && cur && curCat) {\n const item = truncateBullet(bulletBody);\n if (item) {\n cur.sections[curCat].push(item);\n try {\n if (cur.counts) cur.counts[curCat] += 1;\n } catch {\n }\n }\n continue;\n }\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"#\") && /^\\s/.test(line) && cur && curCat) {\n const list = cur.sections[curCat];\n if (list.length > 0) {\n const merged = truncateBullet(`${list[list.length - 1]} ${trimmed}`);\n if (merged) list[list.length - 1] = merged;\n }\n continue;\n }\n }\n pushCurrent();\n return entries.slice(0, CHANGELOG_MAX_ENTRIES);\n } catch {\n return [];\n }\n}\nfunction hasVisibleSections(entry) {\n if (!entry || typeof entry !== \"object\") return false;\n try {\n return CHANGELOG_ALL_CATEGORIES.some((c) => Array.isArray(entry.sections?.[c]) && entry.sections[c].length > 0);\n } catch {\n return false;\n }\n}\nfunction compareReleaseSafe(a, b) {\n try {\n return compareReleaseVersions(a, b);\n } catch {\n return null;\n }\n}\nfunction selectChangelogEntries(entries, fromExclusive, toInclusive) {\n try {\n if (!Array.isArray(entries) || entries.length === 0) return [];\n const to = typeof toInclusive === \"string\" ? toInclusive.trim() : \"\";\n if (!to || !validReleaseVersion(to)) return [];\n const from = typeof fromExclusive === \"string\" ? fromExclusive.trim() : \"\";\n const fromValid = from && validReleaseVersion(from) ? from : null;\n const out = [];\n for (const e of entries) {\n if (!e || typeof e !== \"object\") continue;\n const v = typeof e.version === \"string\" ? String(e.version) : \"\";\n if (!v || isUnreleasedVersion(v) || !validReleaseVersion(v)) continue;\n if (!hasVisibleSections(e)) continue;\n const leTo = compareReleaseSafe(v, to);\n if (leTo === null || leTo > 0) continue;\n if (fromValid) {\n const gtFrom = compareReleaseSafe(v, fromValid);\n if (gtFrom === null || gtFrom <= 0) continue;\n }\n out.push(e);\n }\n return out;\n } catch {\n return [];\n }\n}\nfunction changelogForUpdate(entries, runningVersion, latestVersion, installedVersion) {\n try {\n const to = typeof latestVersion === \"string\" ? latestVersion.trim() : \"\";\n if (!to || !validReleaseVersion(to)) return [];\n const run = typeof runningVersion === \"string\" ? runningVersion.trim() : \"\";\n const inst = typeof installedVersion === \"string\" ? installedVersion.trim() : \"\";\n const from = run && validReleaseVersion(run) ? run : inst && validReleaseVersion(inst) ? inst : \"\";\n if (!from) return [];\n const order = compareReleaseSafe(to, from);\n if (order === null || order <= 0) return [];\n return selectChangelogEntries(entries, from, to);\n } catch {\n return [];\n }\n}\nfunction escapeChangelogHtml(text) {\n return String(text ?? \"\").replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction countsOf(entry) {\n try {\n const out = { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n if (!entry || typeof entry !== \"object\") return out;\n for (const c of CHANGELOG_ALL_CATEGORIES) {\n const n = entry.counts;\n const v = n && typeof n[c] === \"number\" && Number.isFinite(n[c]) ? Math.floor(n[c]) : null;\n if (v !== null && v >= 0) {\n out[c] = v;\n continue;\n }\n const list = entry.sections;\n const arr = list ? list[c] : null;\n out[c] = Array.isArray(arr) ? arr.length : 0;\n }\n return out;\n } catch {\n return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n }\n}\nfunction truncatedNoteHTML(total, shown) {\n try {\n const m = Math.floor(total);\n const n = Math.floor(shown);\n return '<div class=\"dsh-upd-changelog-count\">\\u5171 ' + String(m) + \" \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D \" + String(n) + \" \\u6761</div>\";\n } catch {\n return \"\";\n }\n}\nfunction yankedBannerHTML(version) {\n try {\n const v = typeof version === \"string\" ? version.trim() : \"\";\n if (!v) return \"\";\n return '<div class=\"dsh-upd-changelog-yanked\" role=\"alert\">\\u76EE\\u6807\\u7248\\u672C ' + escapeChangelogHtml(v) + \" \\u5DF2\\u88AB\\u4F5C\\u8005\\u64A4\\u56DE\\uFF08yanked\\uFF09\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\uFF0C\\u7EE7\\u7EED\\u524D\\u8BF7\\u786E\\u8BA4\\u3002</div>\";\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogItem(item) {\n try {\n const s = String(item !== null && item !== void 0 ? item : \"\");\n if (!s) return \"\";\n const split = splitBreakingPrefix(s);\n if (!split) return \"<li>\" + escapeChangelogHtml(s) + \"</li>\";\n const head = escapeChangelogHtml(split.head);\n const prefix = escapeChangelogHtml(split.prefix);\n const rest = escapeChangelogHtml(split.rest);\n const badge = '<span class=\"dsh-upd-breaking-badge\" role=\"img\" aria-label=\"\\u7834\\u574F\\u6027\\u53D8\\u66F4\">' + BREAKING_BADGE_TEXT + \"</span> \";\n return \"<li>\" + head + badge + \"<strong>\" + prefix + \"</strong>\" + rest + \"</li>\";\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogSection(entry) {\n try {\n if (!entry || typeof entry !== \"object\") return \"\";\n const version = String(entry.version !== void 0 && entry.version !== null ? String(entry.version) : \"\").trim();\n if (!version || isUnreleasedVersion(version)) return \"\";\n if (!hasVisibleSections(entry)) return \"\";\n const dateRaw = entry.date;\n const date = typeof dateRaw === \"string\" ? dateRaw : \"\";\n const yankedFlag = entry.yanked === true;\n const yankedSuffix = yankedFlag ? \" \\xB7 \\u5DF2\\u64A4\\u56DE\" : \"\";\n const title = (date ? version + \" \\xB7 \" + date : version) + yankedSuffix;\n const counts = countsOf(entry);\n const parts = [];\n parts.push('<div class=\"dsh-upd-changelog-version\" data-version=\"' + escapeChangelogHtml(version) + '\">');\n parts.push('<div class=\"dsh-upd-changelog-title\">' + escapeChangelogHtml(title) + \"</div>\");\n for (const cat of CHANGELOG_MUST_SHOW) {\n const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];\n if (!items || items.length === 0) continue;\n const label = cat + \" \\xB7 \" + CHANGELOG_CATEGORY_ZH[cat];\n parts.push('<div class=\"dsh-upd-changelog-cat\" data-cat=\"' + cat + '\">');\n parts.push('<div class=\"dsh-upd-changelog-catname\">' + escapeChangelogHtml(label) + \"</div>\");\n const total = counts[cat];\n const shown = items.length;\n if (total > shown) {\n parts.push(truncatedNoteHTML(total, shown));\n }\n parts.push(\"<ul>\");\n for (const item of items) {\n if (!item) continue;\n const li = renderChangelogItem(item);\n if (li) parts.push(li);\n }\n parts.push(\"</ul>\");\n if (cat === \"Security\" && total > shown) {\n const restCount = total - shown;\n parts.push('<details class=\"dsh-upd-changelog-security-more\"><summary>\\u5176\\u4F59 ' + String(restCount) + ' \\u6761</summary><div class=\"dsh-upd-changelog-more-note\">\\u4E3A\\u4FDD\\u6301\\u9762\\u677F\\u6027\\u80FD\\uFF0C\\u5176\\u4F59\\u6761\\u76EE\\u5DF2\\u6298\\u53E0\\uFF0C\\u53EF\\u67E5\\u770B\\u539F\\u6587\\u3002</div></details>');\n }\n parts.push(\"</div>\");\n }\n for (const cat of CHANGELOG_FOLDED) {\n const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];\n if (!items || items.length === 0) continue;\n const label = cat + \" \\xB7 \" + CHANGELOG_CATEGORY_ZH[cat] + \"\\uFF08\" + String(items.length) + \"\\uFF09\";\n parts.push('<details class=\"dsh-upd-changelog-fold\" data-cat=\"' + cat + '\">');\n parts.push(\"<summary>\" + escapeChangelogHtml(label) + \"</summary>\");\n const total = counts[cat];\n const shown = items.length;\n if (total > shown) {\n parts.push(truncatedNoteHTML(total, shown));\n }\n parts.push(\"<ul>\");\n for (const item of items) {\n if (!item) continue;\n const li = renderChangelogItem(item);\n if (li) parts.push(li);\n }\n parts.push(\"</ul></details>\");\n }\n parts.push(\"</div>\");\n return parts.join(String.fromCharCode(10));\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogNeutral() {\n return `<div class=\"dsh-upd-changelog-neutral\">${escapeChangelogHtml(CHANGELOG_NEUTRAL_LINE)}</div>`;\n}\nfunction renderChangelogHTML(entries, opts) {\n try {\n if (!Array.isArray(entries) || entries.length === 0) return renderChangelogNeutral();\n const from = opts && typeof opts.from === \"string\" ? opts.from : null;\n const to = opts && typeof opts.to === \"string\" ? opts.to : null;\n let ranged;\n if (from !== null || to !== null) {\n ranged = selectChangelogEntries(entries, from, to);\n } else {\n ranged = entries.filter((e) => {\n try {\n const v = String(e.version ?? \"\");\n return !!v && !isUnreleasedVersion(v) && hasVisibleSections(e);\n } catch {\n return false;\n }\n });\n }\n if (ranged.length === 0) return renderChangelogNeutral();\n const blocks = ranged.map((e) => renderChangelogSection(e)).filter((s) => !!s);\n if (blocks.length === 0) return renderChangelogNeutral();\n return `<div class=\"dsh-upd-changelog\">\n${blocks.join(\"\\n\")}\n</div>`;\n } catch {\n return renderChangelogNeutral();\n }\n}\nconst CHANGELOG_VALIDATE_HINTS = {\n E_VERSION_TITLE: \"\\u8BE5\\u6807\\u9898\\u4E0D\\u662F\\u5408\\u6CD5\\u7248\\u672C\\u53F7\\uFF0C\\u5176\\u4E0B\\u6761\\u76EE\\u5DF2\\u88AB\\u5FFD\\u7565\\uFF0C\\u8BF7\\u6539\\u6210 ## [x.y.z] \\u5F62\\u6001\",\n E_CATEGORY: \"\\u672A\\u77E5\\u5206\\u7C7B\\uFF0C\\u5176\\u4E0B\\u6761\\u76EE\\u5DF2\\u88AB\\u5FFD\\u7565\\uFF0C\\u4EC5\\u6536 Added/Fixed/Changed/Deprecated/Removed/Security\",\n E_BULLET_ORPHAN: \"\\u8BE5\\u6761\\u76EE\\u4E0D\\u5728\\u7248\\u672C\\u8282\\u4E0E\\u5206\\u7C7B\\u4E0B\\uFF0C\\u5DF2\\u88AB\\u5FFD\\u7565\",\n W_BREAKING_MAYBE: \"\\u7591\\u4F3C\\u7834\\u574F\\u6807\\u8BB0\\u8BEF\\u5199\\uFF08\\u7F3A\\u5192\\u53F7/\\u62FC\\u5199\\u63A5\\u8FD1/\\u4F4D\\u7F6E\\u7591\\u4F3C\\u653E\\u9519\\uFF09\\uFF0C\\u8BF7\\u68C0\\u67E5\\u662F\\u5426\\u60F3\\u5199 BREAKING:/\\u4E0D\\u517C\\u5BB9:\",\n W_YANKED: \"\\u8BE5\\u7248\\u672C\\u5DF2\\u88AB\\u6807\\u8BB0\\u64A4\\u56DE\\uFF08yanked\\uFF09\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\uFF0C\\u7EE7\\u7EED\\u524D\\u8BF7\\u786E\\u8BA4\",\n W_TRUNCATED: \"\\u8BE5\\u7C7B\\u5171 M \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D N \\u6761\",\n W_TRUNCATED_FILE: \"\\u6587\\u4EF6\\u8D85 64K\\uFF0C\\u4EC5\\u6821\\u9A8C\\u524D 64K\",\n W_EMPTY: \"\\u672A\\u89E3\\u6790\\u5230\\u4EFB\\u4F55\\u7248\\u672C\\u8282\\uFF0C\\u9762\\u677F\\u5C06\\u663E\\u793A\\u4E2D\\u6027\\u63D0\\u793A\"\n};\nfunction isMaybeBreakingMisuse(bulletBody) {\n try {\n if (typeof bulletBody !== \"string\") return false;\n const t = stripBreakingDecorations(bulletBody);\n if (!t) return false;\n if (isBreakingChangelogItem(bulletBody)) return false;\n if (/^breaking\\s*$/i.test(t)) return true;\n if (/^breaking\\s+[^::]/i.test(t)) return true;\n if (/^breaking[^::\\s\\w]/i.test(t)) return true;\n if (/^不兼容\\s*$/i.test(t)) return true;\n if (/^不兼容\\s*[^::]/i.test(t)) return true;\n if (/^(braking|breking|breakign|brekaing|braeking|breakingg)\\b/i.test(t)) return true;\n if (/^break[^i::\\s]/i.test(t)) return true;\n return false;\n } catch {\n return false;\n }\n}\nfunction validateChangelog(markdown) {\n try {\n if (typeof markdown !== \"string\" || !markdown.trim()) {\n return { ok: true, diagnostics: [{ line: 1, code: \"W_EMPTY\", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY }] };\n }\n let raw = String(markdown).replace(/\\r\\n/g, String.fromCharCode(10));\n let fileTruncated = false;\n if (raw.length > CHANGELOG_MAX_CHARS) {\n fileTruncated = true;\n raw = raw.slice(0, CHANGELOG_MAX_CHARS);\n }\n const lines = raw.split(String.fromCharCode(10));\n const diags = [];\n let inFence = false;\n let curValid = false;\n let curCat = null;\n let catLineByCat = {};\n let countByCat = {};\n let hasVisibleVersion = false;\n const flushCounts = function() {\n try {\n for (const k of Object.keys(countByCat)) {\n const n = countByCat[k];\n if (n > CHANGELOG_MAX_BULLETS_PER_SECTION) {\n const lineNo = catLineByCat[k] ? catLineByCat[k] : lines.length;\n const hint = \"\\u5206\\u7C7B \" + k + \" \\u5171 \" + String(n) + \" \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D \" + String(CHANGELOG_MAX_BULLETS_PER_SECTION) + \" \\u6761\";\n diags.push({ line: lineNo, code: \"W_TRUNCATED\", hint });\n }\n }\n } catch {\n }\n };\n for (let i = 0; i < lines.length; i++) {\n const lineNo = i + 1;\n const line = String(lines[i] !== void 0 && lines[i] !== null ? lines[i] : \"\");\n if (isFenceToggle(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const vTitle = matchVersionHeading(line);\n if (vTitle !== null) {\n flushCounts();\n catLineByCat = {};\n countByCat = {};\n curValid = false;\n curCat = null;\n const info = extractVersionDateAndYanked(vTitle);\n if (!info.version) {\n diags.push({ line: lineNo, code: \"E_VERSION_TITLE\", hint: CHANGELOG_VALIDATE_HINTS.E_VERSION_TITLE });\n continue;\n }\n if (isUnreleasedVersion(info.version)) {\n curValid = false;\n continue;\n }\n curValid = true;\n hasVisibleVersion = true;\n if (info.yanked) {\n diags.push({ line: lineNo, code: \"W_YANKED\", hint: CHANGELOG_VALIDATE_HINTS.W_YANKED });\n }\n continue;\n }\n const cTitle = matchCategoryHeading(line);\n if (cTitle !== null) {\n const cat = normalizeCategory(cTitle);\n if (!cat) {\n curCat = null;\n diags.push({ line: lineNo, code: \"E_CATEGORY\", hint: CHANGELOG_VALIDATE_HINTS.E_CATEGORY });\n continue;\n }\n if (!curValid) {\n curCat = null;\n continue;\n }\n curCat = cat;\n catLineByCat[cat] = lineNo;\n if (countByCat[cat] === void 0) countByCat[cat] = 0;\n continue;\n }\n const body = matchBulletBody(line);\n if (body !== null) {\n if (!curValid || !curCat) {\n diags.push({ line: lineNo, code: \"E_BULLET_ORPHAN\", hint: CHANGELOG_VALIDATE_HINTS.E_BULLET_ORPHAN });\n continue;\n }\n countByCat[curCat] = (countByCat[curCat] ? countByCat[curCat] : 0) + 1;\n if (isMaybeBreakingMisuse(body)) {\n diags.push({ line: lineNo, code: \"W_BREAKING_MAYBE\", hint: CHANGELOG_VALIDATE_HINTS.W_BREAKING_MAYBE });\n }\n continue;\n }\n }\n flushCounts();\n if (fileTruncated) {\n diags.push({ line: lines.length, code: \"W_TRUNCATED_FILE\", hint: CHANGELOG_VALIDATE_HINTS.W_TRUNCATED_FILE });\n }\n if (!hasVisibleVersion) {\n let anyVisible = false;\n try {\n const parsed = parseChangelog(markdown);\n for (const e of parsed) {\n if (e && !isUnreleasedVersion(e.version) && hasVisibleSections(e)) {\n anyVisible = true;\n break;\n }\n }\n } catch {\n anyVisible = false;\n }\n if (!anyVisible) {\n diags.push({ line: lines.length, code: \"W_EMPTY\", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY });\n }\n }\n diags.sort(function(a, b) {\n return a.line - b.line;\n });\n let ok = true;\n for (const d of diags) {\n if (d.code.charAt(0) === \"E\") {\n ok = false;\n break;\n }\n }\n return { ok, diagnostics: diags };\n } catch {\n return { ok: false, diagnostics: [] };\n }\n}\nexport {\n BREAKING_BADGE_TEXT,\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_CATEGORY_ZH,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n CHANGELOG_POLL_BACKOFF_MS,\n CHANGELOG_VALIDATE_HINTS,\n RE_BULLET,\n RE_CATEGORY_HEADING,\n RE_FENCE_TOGGLE,\n RE_UNRELEASED,\n RE_VERSION_DATE,\n RE_VERSION_HEADING,\n RE_VERSION_NUM,\n RE_YANKED,\n changelogForUpdate,\n countsOf,\n hasVisibleSections,\n isBreakingChangelogItem,\n isFenceToggle,\n isMaybeBreakingMisuse,\n isUnreleasedVersion,\n matchBulletBody,\n matchCategoryHeading,\n matchVersionHeading,\n parseChangelog,\n renderChangelogHTML,\n renderChangelogItem,\n renderChangelogNeutral,\n renderChangelogSection,\n selectChangelogEntries,\n shouldFetchChangelog,\n splitBreakingPrefix,\n stripBreakingDecorations,\n truncatedNoteHTML,\n validateChangelog,\n yankedBannerHTML\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/batch.ts\nconst BATCH_SESSION_VERSION = 1;\nfunction isTerminalPhase(phase) {\n return phase === \"done\" || phase === \"failed\" || phase === \"skipped\" || phase === \"current\";\n}\nfunction asMillis(value, fallback) {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\nfunction isNonEmptyString(value) {\n return typeof value === \"string\" && value.length > 0;\n}\nconst PHASES = [\"pending\", \"checking\", \"ready\", \"installing\", \"current\", \"done\", \"failed\", \"skipped\"];\nfunction asPhase(value) {\n return typeof value === \"string\" && PHASES.includes(value) ? value : \"pending\";\n}\nfunction orderTargets(keys, selfKey) {\n const seen = /* @__PURE__ */ new Set();\n const others = [];\n let self = null;\n for (const key of keys) {\n if (!isNonEmptyString(key) || seen.has(key)) continue;\n seen.add(key);\n if (selfKey && key === selfKey) self = key;\n else others.push(key);\n }\n return self ? [...others, self] : others;\n}\nfunction batchRequestId(sessionId, key) {\n return \"batch:\" + sessionId + \":\" + key;\n}\nfunction createBatchSession(args) {\n const id = isNonEmptyString(args.id) ? args.id : \"batch\";\n const selfKey = isNonEmptyString(args.selfKey) ? args.selfKey : null;\n const order = orderTargets(args.keys, selfKey);\n const now = asMillis(args.now, 0);\n const entries = order.map((key) => ({\n key,\n phase: \"pending\",\n requestId: batchRequestId(id, key),\n targetVersion: null,\n restartRequired: false,\n error: null,\n updatedAt: now\n }));\n return {\n version: BATCH_SESSION_VERSION,\n id,\n selfKey,\n stopOnFailure: args.stopOnFailure === true,\n order,\n entries,\n createdAt: now,\n updatedAt: now\n };\n}\nfunction emptyBatchSession() {\n return {\n version: BATCH_SESSION_VERSION,\n id: \"\",\n selfKey: null,\n stopOnFailure: false,\n order: [],\n entries: [],\n createdAt: 0,\n updatedAt: 0\n };\n}\nfunction normalizeBatchSession(raw) {\n try {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return emptyBatchSession();\n const input = raw;\n if (input[\"version\"] !== BATCH_SESSION_VERSION) return emptyBatchSession();\n const id = isNonEmptyString(input[\"id\"]) ? input[\"id\"] : \"\";\n const selfKey = isNonEmptyString(input[\"selfKey\"]) ? input[\"selfKey\"] : null;\n const entries = [];\n const seen = /* @__PURE__ */ new Set();\n const rawEntries = input[\"entries\"];\n if (Array.isArray(rawEntries)) {\n for (const item of rawEntries) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) continue;\n const e = item;\n const key = e[\"key\"];\n if (!isNonEmptyString(key) || seen.has(key)) continue;\n seen.add(key);\n const requestId = isNonEmptyString(e[\"requestId\"]) ? e[\"requestId\"] : batchRequestId(id, key);\n entries.push({\n key,\n phase: asPhase(e[\"phase\"]),\n requestId,\n targetVersion: isNonEmptyString(e[\"targetVersion\"]) ? e[\"targetVersion\"] : null,\n restartRequired: e[\"restartRequired\"] === true,\n error: isNonEmptyString(e[\"error\"]) ? e[\"error\"] : null,\n updatedAt: asMillis(e[\"updatedAt\"], 0)\n });\n }\n }\n const order = entries.map((e) => e.key);\n return {\n version: BATCH_SESSION_VERSION,\n id,\n selfKey: selfKey && seen.has(selfKey) ? selfKey : null,\n stopOnFailure: input[\"stopOnFailure\"] === true,\n order,\n entries,\n createdAt: asMillis(input[\"createdAt\"], 0),\n updatedAt: asMillis(input[\"updatedAt\"], 0)\n };\n } catch {\n return emptyBatchSession();\n }\n}\nfunction batchEntryOf(session, key) {\n return session.entries.find((e) => e.key === key) ?? null;\n}\nfunction nextBatchKey(session) {\n if (session.stopOnFailure && session.entries.some((e) => e.phase === \"failed\")) return null;\n for (const key of session.order) {\n const entry = session.entries.find((e) => e.key === key);\n if (entry && !isTerminalPhase(entry.phase)) return key;\n }\n return null;\n}\nfunction markBatchEntry(session, key, patch, now) {\n const at = session.entries.findIndex((e) => e.key === key);\n if (at < 0) return { session, changed: false };\n const next = { ...session.entries[at], ...patch, key: session.entries[at].key, requestId: session.entries[at].requestId, updatedAt: asMillis(now, session.entries[at].updatedAt) };\n const entries = [...session.entries.slice(0, at), next, ...session.entries.slice(at + 1)];\n return { session: { ...session, entries, updatedAt: next.updatedAt }, changed: true };\n}\nfunction batchProgress(session) {\n const total = session.entries.length;\n let done = 0;\n let failed = 0;\n let skipped = 0;\n let current = 0;\n let pending = 0;\n for (const entry of session.entries) {\n if (entry.phase === \"done\" || entry.phase === \"current\") done += 1;\n else if (entry.phase === \"failed\") failed += 1;\n else if (entry.phase === \"skipped\") skipped += 1;\n else if (entry.phase === \"installing\" || entry.phase === \"checking\" || entry.phase === \"ready\") current += 1;\n else pending += 1;\n }\n return { total, done, failed, skipped, current, pending, finished: isBatchFinished(session) };\n}\nfunction isBatchFinished(session) {\n return session.entries.length > 0 && session.entries.every((e) => isTerminalPhase(e.phase));\n}\nfunction needsRestartKeys(session) {\n return session.entries.filter((e) => e.restartRequired).map((e) => e.key);\n}\nfunction failedKeys(session) {\n return session.entries.filter((e) => e.phase === \"failed\").map((e) => e.key);\n}\nfunction resumeBatchSession(session, now) {\n const at = asMillis(now, session.updatedAt);\n let changed = false;\n const entries = session.entries.map((entry) => {\n if (entry.phase === \"checking\" || entry.phase === \"installing\") {\n changed = true;\n return { ...entry, phase: \"pending\", error: null, updatedAt: at };\n }\n return entry;\n });\n return changed ? { ...session, entries, updatedAt: at } : session;\n}\nexport {\n BATCH_SESSION_VERSION,\n batchEntryOf,\n batchProgress,\n batchRequestId,\n createBatchSession,\n emptyBatchSession,\n failedKeys,\n isBatchFinished,\n isTerminalPhase,\n markBatchEntry,\n needsRestartKeys,\n nextBatchKey,\n normalizeBatchSession,\n orderTargets,\n resumeBatchSession\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/redaction.ts\nconst DETAIL_MAX_CHARS = 300;\nconst COPY_BUDGET_CHARS = 1500;\nconst DIAG_INLINE_BUDGET_BYTES = 1024;\nconst REDACTED_PATH = \"<\\u8DEF\\u5F84>\";\nconst REDACTED_SECRET = \"<\\u8131\\u654F>\";\nconst KNOWN_GAP_FILE_URL_PREFIX = \"file:///\";\nconst REDACTION_RULE_NAMES = [\n \"R_ABSOLUTE_PATH\",\n \"R_URL_USERINFO\",\n \"R_TOKEN\",\n \"R_CRED_PAIR\",\n \"R_EMAIL\"\n];\nconst ENDORSED_REGISTRY_HOSTS = [\"registry.npmjs.org\"];\nconst ABSOLUTE_PATH_RE = /[A-Za-z]:\\\\[^\\s\"']*|\\\\\\\\[^\\s\"'()\\[\\];]+|(^|[\\s\"'(\\[=,])\\/\\/[^\\s\"'()\\[\\];]+|(^|[\\s\"'(\\[=:,])\\/(?!\\/)[^\\s\"'()\\[\\];]+/g;\nconst URL_USERINFO_RE = /[A-Za-z][A-Za-z0-9+.-]*:\\/\\/[^\\s/]*@/;\nconst NPM_TOKEN_RE = /\\bnpm_[A-Za-z0-9_-]{8,}/g;\nconst GITHUB_TOKEN_RE = /\\b(?:gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{10,})/g;\nconst SK_TOKEN_RE = /\\bsk-[A-Za-z0-9_-]{6,}/g;\nconst BEARER_TOKEN_RE = /(\\bbearer\\s+)[A-Za-z0-9._~+/-=]{6,}/gi;\nconst CRED_PAIR_RE = /(\\b(?:token|password|passwd|pwd|api[_-]?key|access[_-]?key|auth[_-]?token|secret|cookie)\\s*[:=]\\s*)(\"[^\"]+\"|'[^']+'|[^\\s'\";,)\\]]+)/gi;\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\nfunction flattenWhitespace(text) {\n return String(text ?? \"\").replace(/\\s+/g, \" \").trim();\n}\nfunction applyPathRule(text) {\n ABSOLUTE_PATH_RE.lastIndex = 0;\n return text.replace(ABSOLUTE_PATH_RE, (_whole, g1, g2) => {\n const lead = g1 ?? g2 ?? \"\";\n return `${lead}${REDACTED_PATH}`;\n });\n}\nfunction applyTokenRule(text) {\n NPM_TOKEN_RE.lastIndex = 0;\n GITHUB_TOKEN_RE.lastIndex = 0;\n SK_TOKEN_RE.lastIndex = 0;\n BEARER_TOKEN_RE.lastIndex = 0;\n return text.replace(NPM_TOKEN_RE, REDACTED_SECRET).replace(GITHUB_TOKEN_RE, REDACTED_SECRET).replace(SK_TOKEN_RE, REDACTED_SECRET).replace(BEARER_TOKEN_RE, (_whole, prefix) => `${prefix}${REDACTED_SECRET}`);\n}\nfunction applyCredPairRule(text) {\n CRED_PAIR_RE.lastIndex = 0;\n return text.replace(CRED_PAIR_RE, (_whole, head) => `${head}${REDACTED_SECRET}`);\n}\nfunction applyEmailRule(text) {\n EMAIL_RE.lastIndex = 0;\n return text.replace(EMAIL_RE, REDACTED_SECRET);\n}\nfunction hasUserinfo(text) {\n URL_USERINFO_RE.lastIndex = 0;\n return URL_USERINFO_RE.test(text);\n}\nfunction truncateToWordBoundary(text, max) {\n const input = String(text ?? \"\");\n if (input.length <= max) return input;\n const slice = input.slice(0, max);\n const lastSpace = slice.lastIndexOf(\" \");\n let cut = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;\n const lastOpen = cut.lastIndexOf(\"<\");\n const lastClose = cut.lastIndexOf(\">\");\n if (lastOpen > lastClose) cut = cut.slice(0, lastOpen).trimEnd();\n if (!cut) cut = slice;\n return `${cut}\\u2026`;\n}\nfunction truncateDetail(text) {\n return truncateToWordBoundary(text, DETAIL_MAX_CHARS);\n}\nfunction truncateForCopy(text) {\n return truncateToWordBoundary(text, COPY_BUDGET_CHARS);\n}\nfunction scrubWithoutBudget(text) {\n return applyEmailRule(applyCredPairRule(applyTokenRule(applyPathRule(text))));\n}\nfunction sanitizeDetail(text) {\n const flat = flattenWhitespace(text);\n if (!flat) return \"\";\n if (hasUserinfo(flat)) return \"\";\n return truncateDetail(scrubWithoutBudget(flat));\n}\nfunction sanitizeForCopy(text) {\n const flat = flattenWhitespace(String(text ?? \"\"));\n if (!flat) return \"\";\n if (hasUserinfo(flat)) return \"\";\n return truncateForCopy(scrubWithoutBudget(flat));\n}\nfunction resolveRegistryHost(registryUrl) {\n try {\n const raw = String(registryUrl ?? \"\").trim();\n if (!raw) return null;\n const host = new URL(raw).hostname.toLowerCase();\n if (!host) return null;\n return ENDORSED_REGISTRY_HOSTS.includes(host) ? host : null;\n } catch {\n return null;\n }\n}\nfunction diagByteLength(value) {\n try {\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n const encoder = globalThis.TextEncoder;\n if (typeof encoder === \"function\") return new encoder().encode(text ?? \"\").length;\n return String(text ?? \"\").length;\n } catch {\n try {\n return String(JSON.stringify(value) ?? \"\").length;\n } catch {\n return 0;\n }\n }\n}\nconst DIAG_NEVER_DROP_KEYS = [\"v\", \"code\", \"error\", \"errorKind\", \"stage\", \"route\", \"method\", \"requestId\"];\nconst DIAG_DROP_ORDER = [\n \"registryHost\",\n \"action\",\n \"latestVersion\",\n \"runningVersion\",\n \"targetPackageName\",\n \"latencyMs\",\n \"exitCode\",\n \"environmentKind\",\n \"checkId\",\n \"httpStatus\"\n];\nfunction isRecord(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction enforceDiagBudget(input) {\n const base = isRecord(input) ? { ...input } : {};\n const sizeOf = (obj) => diagByteLength(obj);\n const convergeDetailTo300 = (obj) => {\n const current = obj[\"detail\"];\n if (typeof current !== \"string\" || !current) return false;\n const next = truncateToWordBoundary(current, DETAIL_MAX_CHARS);\n if (next !== current) {\n obj[\"detail\"] = next;\n return true;\n }\n return false;\n };\n const shrinkDetailToFit = (obj) => {\n const current = obj[\"detail\"];\n if (typeof current !== \"string\" || !current) return false;\n let next = current;\n let changed = false;\n while (next && sizeOf({ ...obj, detail: next, truncated: true }) > DIAG_INLINE_BUDGET_BYTES) {\n const shorter = next.length - 50;\n if (shorter <= 0) {\n next = \"\";\n break;\n }\n const cut = truncateToWordBoundary(next.slice(0, shorter), shorter) || next.slice(0, shorter) + \"\\u2026\";\n if (cut.length >= next.length) {\n next = next.slice(0, shorter) + \"\\u2026\";\n } else {\n next = cut;\n }\n changed = true;\n }\n if (next !== current) {\n obj[\"detail\"] = next;\n return true;\n }\n return changed;\n };\n if (sizeOf({ ...base, truncated: base[\"truncated\"] ?? false }) <= DIAG_INLINE_BUDGET_BYTES) {\n const already = base[\"truncated\"] === true;\n return { ...base, truncated: already };\n }\n const working = { ...base };\n convergeDetailTo300(working);\n for (const key of DIAG_DROP_ORDER) {\n if (sizeOf({ ...working, truncated: true }) <= DIAG_INLINE_BUDGET_BYTES) break;\n if (key in working) {\n delete working[key];\n }\n }\n if (sizeOf({ ...working, truncated: true }) > DIAG_INLINE_BUDGET_BYTES && typeof working[\"detail\"] === \"string\" && working[\"detail\"]) {\n shrinkDetailToFit(working);\n if (sizeOf({ ...working, truncated: true }) > DIAG_INLINE_BUDGET_BYTES && typeof working[\"detail\"] === \"string\" && working[\"detail\"]) {\n working[\"detail\"] = \"\";\n }\n }\n for (const key of DIAG_NEVER_DROP_KEYS) {\n if (key in base && !(key in working)) working[key] = base[key];\n }\n return { ...working, truncated: true };\n}\nexport {\n COPY_BUDGET_CHARS,\n DETAIL_MAX_CHARS,\n DIAG_DROP_ORDER,\n DIAG_INLINE_BUDGET_BYTES,\n DIAG_NEVER_DROP_KEYS,\n ENDORSED_REGISTRY_HOSTS,\n KNOWN_GAP_FILE_URL_PREFIX,\n REDACTED_PATH,\n REDACTED_SECRET,\n REDACTION_RULE_NAMES,\n diagByteLength,\n enforceDiagBudget,\n resolveRegistryHost,\n sanitizeDetail,\n sanitizeForCopy,\n truncateToWordBoundary\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel.ts\nimport { buildChangelogPhoneName, buildPhoneNames, DEFAULT_PANEL_POLL_MS, MIN_PANEL_POLL_MS } from \"./config.js\";\nimport { COPY_BUDGET_CHARS, sanitizeForCopy } from \"./redaction.js\";\nimport { validReleaseVersion } from \"./service.js\";\nimport {\n changelogForUpdate,\n parseChangelog,\n renderChangelogHTML,\n shouldFetchChangelog,\n yankedBannerHTML\n} from \"./changelog.js\";\nfunction normalizePanelTheme(value) {\n return value === \"d5-paper\" || value === \"archive\" ? \"d5-paper\" : \"default\";\n}\nconst BLOCKED_COPY = {\n \"unknown-profile\": {\n title: \"\\u4F7F\\u7528\\u8303\\u56F4\\u6216\\u63D2\\u4EF6\\u4F4D\\u7F6E\\u8BA4\\u4E0D\\u51FA\",\n action: \"\\u91CD\\u5F00\\u5BBF\\u4E3B\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u7248\\u672C\\u53F7\\u4E0E\\u65E5\\u5FD7\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u8FD9\\u79CD\\u60C5\\u5F62\\u4E0D\\u7ED9\\u624B\\u5DE5\\u547D\\u4EE4\"\n },\n \"source-install\": {\n title: \"\\u5F53\\u524D\\u662F\\u4ECE\\u6E90\\u7801\\u88C5\\u7684\\uFF0C\\u4E0D\\u662F\\u6309\\u7248\\u672C\\u53F7\\u88C5\\u7684\",\n action: \"\\u8FD9\\u79CD\\u60C5\\u5F62\\u4E0D\\u7ED9\\u624B\\u5DE5\\u547D\\u4EE4\\uFF1B\\u60F3\\u8D70\\u66F4\\u65B0\\u5148\\u6309\\u7248\\u672C\\u53F7\\u91CD\\u88C5\\u4E00\\u6B21\"\n },\n \"invalid-installation\": {\n title: \"\\u5DF2\\u88C5\\u7684\\u5305\\u4E0D\\u5B8C\\u6574\\uFF08\\u540D\\u5B57\\u5BF9\\u4E0D\\u4E0A\\u3001\\u7248\\u672C\\u975E\\u6CD5\\u3001\\u5165\\u53E3\\u6587\\u4EF6\\u7F3A\\u5931\\uFF09\",\n action: \"\\u91CD\\u88C5\\u5F53\\u524D\\u7248\\u672C\\uFF0C\\u4FEE\\u597D\\u5DF2\\u88C5\\u76EE\\u5F55\\u518D\\u67E5\\u66F4\\u65B0\"\n },\n \"installation-changed\": {\n title: \"\\u5B89\\u88C5\\u4F4D\\u7F6E\\u5728\\u4F7F\\u7528\\u4E2D\\u9014\\u53D8\\u4E86\\uFF08\\u6362\\u4E86\\u76EE\\u5F55\\u6216\\u6362\\u4E86\\u5305\\uFF09\",\n action: \"\\u91CD\\u65B0\\u6253\\u5F00\\u5BBF\\u4E3B\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u8FD8\\u51FA\\u73B0\\u5C31\\u91CD\\u88C5\"\n },\n \"pending-restart\": {\n title: \"\\u65B0\\u7248\\u5DF2\\u88C5\\u5230\\u78C1\\u76D8\\uFF0C\\u6B63\\u5728\\u8DD1\\u7684\\u8FD8\\u662F\\u65E7\\u7248\",\n action: \"\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u8BA9\\u65B0\\u7248\\u8DD1\\u8D77\\u6765\\uFF1B\\u8FD9\\u662F\\u6B63\\u5E38\\u7EC8\\u6001\\uFF0C\\u4E0D\\u662F\\u5931\\u8D25\"\n },\n \"registry-conflict\": {\n title: \"\\u672C\\u5730\\u58F0\\u660E\\u7684\\u7248\\u672C\\u4E0E\\u78C1\\u76D8\\u5B9E\\u9645\\u7248\\u672C\\u4E92\\u76F8\\u77DB\\u76FE\",\n action: \"\\u6253\\u5F00\\u4F7F\\u7528\\u8303\\u56F4\\u7684\\u6E05\\u5355\\u6587\\u4EF6\\uFF0C\\u628A\\u76EE\\u6807\\u5305\\u540D\\u90A3\\u4E00\\u884C\\u6539\\u6210\\u7248\\u672C\\u53F7\\u518D\\u8BD5\"\n },\n \"incompatible-node\": {\n title: \"\\u65B0\\u7248\\u8981\\u6C42\\u7684 Node \\u4E0E\\u5F53\\u524D\\u8FD0\\u884C\\u7684\\u5BF9\\u4E0D\\u4E0A\",\n action: \"\\u5148\\u5347\\u7EA7 Node \\u5230 22 \\u6216\\u66F4\\u9AD8\\uFF0C\\u518D\\u67E5\\u66F4\\u65B0\"\n },\n \"recovery-required\": {\n title: \"\\u4E0A\\u6B21\\u5B89\\u88C5\\u88AB\\u6253\\u65AD\\uFF0C\\u7559\\u4E0B\\u4E00\\u4E2A\\u534A\\u622A\\u4EFB\\u52A1\",\n action: \"\\u91CD\\u65B0\\u70B9\\u4E00\\u6B21\\u5B89\\u88C5\\uFF1B\\u4E00\\u76F4\\u51FA\\u73B0\\u5C31\\u6309\\u7B2C 6 \\u8282\\u6392\\u9519\"\n }\n};\nfunction blockedCopy(reason) {\n if (reason === null || reason === void 0) return null;\n return BLOCKED_COPY[reason] ?? null;\n}\nconst PHONE_FAILURE_COPY = {\n \"check-failed\": {\n zh: \"\\u67E5\\u65B0\\u7248\\u6CA1\\u6210\\u529F\\uFF08\\u8054\\u7F51\\u3001\\u6E90\\u3001\\u9650\\u6D41\\u90FD\\u53EF\\u80FD\\uFF09\",\n act: \"\\u8FC7\\u4E00\\u4F1A\\u513F\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u5931\\u8D25\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n },\n \"invalid-release\": {\n zh: \"\\u62FF\\u5230\\u7684\\u53D1\\u5E03\\u4FE1\\u606F\\u4E0D\\u5408\\u6CD5\\uFF08\\u7248\\u672C\\u53F7\\u975E\\u6CD5\\u6216\\u5185\\u5BB9\\u5BF9\\u4E0D\\u4E0A\\uFF09\",\n act: \"\\u68C0\\u67E5\\u6E05\\u5355\\u6587\\u4EF6\\u91CC\\u7684\\u5305\\u540D\\u4E0E\\u7248\\u672C\\u5199\\u6CD5\\uFF0C\\u518D\\u67E5\\u4E00\\u6B21\"\n },\n \"check-expired\": {\n zh: \"\\u51ED\\u8BC1\\u8FC7\\u671F\\u4E86\\uFF0C\\u5B89\\u88C5\\u8BF7\\u6C42\\u88AB\\u62D2\",\n act: \"\\u91CD\\u65B0\\u67E5\\u4E00\\u6B21\\u65B0\\u7248\\u518D\\u70B9\\u5B89\\u88C5\\uFF0C\\u4E0D\\u8981\\u91CD\\u8BD5\\u65E7\\u7F16\\u53F7\"\n },\n \"update-busy\": {\n zh: \"\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u6B63\\u5728\\u88C5\\u53E6\\u4E00\\u4E2A\",\n act: \"\\u7B49\\u5F53\\u524D\\u4EFB\\u52A1\\u79BB\\u5F00 installing/verifying \\u518D\\u70B9\\uFF1B\\u6392\\u961F\\u4E2D\\u53BB\\u67E5\\u72B6\\u6001\\u770B\\u4F4D\\u7F6E\"\n },\n \"install-failed\": {\n zh: \"\\u88C5\\u4E0D\\u4E0A\\uFF08\\u8BE6\\u89C1\\u8BCA\\u65AD\\u6458\\u8981\\uFF09\",\n act: \"\\u5148\\u770B\\u590D\\u5236\\u8BCA\\u65AD\\uFF1B\\u5B98\\u65B9\\u684C\\u9762\\u7248\\u628A\\u8FD9\\u6BB5\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n },\n internal: {\n zh: \"\\u51FA\\u4E86\\u70B9\\u95EE\\u9898\\uFF0C\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\",\n act: \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n }\n};\nconst UNKNOWN_FAILURE_COPY = {\n zh: \"\\u51FA\\u4E86\\u70B9\\u95EE\\u9898\\uFF0C\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\",\n act: \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF08\\u5E26\\u4E0A\\u4F60\\u770B\\u5230\\u7684\\u7801\\uFF09\"\n};\nfunction isKnownFailureCode(code) {\n if (typeof code !== \"string\" || !code) return false;\n const c = code.trim();\n if (!c) return false;\n return c in BLOCKED_COPY || c in PHONE_FAILURE_COPY;\n}\nfunction failureCopy(code) {\n if (typeof code !== \"string\") return null;\n const c = code.trim();\n if (!c) return null;\n const blocked = BLOCKED_COPY[c];\n if (blocked) return { zh: blocked.title, act: blocked.action };\n const phone = PHONE_FAILURE_COPY[c];\n if (phone) return phone;\n return UNKNOWN_FAILURE_COPY;\n}\nfunction failureCodeOf(reply, fallback) {\n const kind = reply && typeof reply.errorKind === \"string\" ? reply.errorKind.trim() : \"\";\n if (kind) return kind;\n const err = reply && typeof reply.error === \"string\" ? reply.error.trim() : \"\";\n if (err) return err;\n if (typeof fallback === \"string\" && fallback.trim()) return fallback.trim();\n return \"internal\";\n}\nconst PANEL_DIAG_MAX_CHARS = COPY_BUDGET_CHARS;\nfunction redactForCopy(text) {\n return sanitizeForCopy(text);\n}\nfunction buildDiagnosticText(input) {\n const code = String(input.code || \"check-failed\");\n const lines = [`[\\u66F4\\u65B0\\u8BCA\\u65AD] ${String(input.pluginId)} \\u7A33\\u5B9A\\u7801\\uFF1A${code}`];\n const detail = redactForCopy(input.detail ?? \"\");\n lines.push(`\\u4EBA\\u8BDD\\uFF1A${detail || failureCopy(code)?.act || code}`);\n const versions = [\n `\\u8FD0\\u884C\\u7248\\uFF1A${input.runningVersion ?? \"\\u672A\\u77E5\"}`,\n `\\u5DF2\\u88C5\\uFF1A${input.installedVersion ?? \"\\u672A\\u77E5\"}`,\n `\\u8FDC\\u7AEF\\uFF1A${input.latestVersion ?? \"\\u672A\\u67E5\\u8FC7\"}`\n ].join(\" / \");\n lines.push(versions);\n const queue = input.queuePosition === 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5\" : typeof input.queuePosition === \"number\" ? `\\u6392\\u961F\\u7B2C ${input.queuePosition} \\u4F4D` : \"\\u4E0D\\u5728\\u961F\\u5217\\u91CC\";\n const hostLine = [`\\u5BBF\\u4E3B\\uFF1A${input.hostKind ?? \"\\u672A\\u77E5\"}`];\n if (typeof input.profileName === \"string\" && input.profileName) hostLine.push(`\\u4F7F\\u7528\\u8303\\u56F4\\uFF1A${input.profileName}`);\n hostLine.push(`\\u961F\\u5217\\uFF1A${queue}`);\n lines.push(hostLine.join(\" / \") + (input.requestId ? ` / \\u8BF7\\u6C42\\u7F16\\u53F7\\uFF1A${input.requestId}` : \"\"));\n const manual = redactForCopy(input.manual ?? \"\");\n if (manual) lines.push(`\\u624B\\u5DE5\\u547D\\u4EE4\\uFF1A${manual}`);\n return redactForCopy(lines.join(\"\\n\"));\n}\nconst DIAG_KNOWN_TYPES = {\n v: \"number\",\n stage: \"string\",\n route: \"string\",\n method: \"string\",\n httpStatus: \"number\",\n exitCode: \"number\",\n latencyMs: \"number\",\n detail: \"string\",\n targetPackageName: \"string\",\n runningVersion: \"string\",\n latestVersion: \"string\",\n environmentKind: \"string\",\n requestId: \"string\",\n checkId: \"string\",\n registryHost: \"string\",\n action: \"string\"\n};\nfunction readDiagTolerant(diag) {\n const empty = {\n stage: null,\n route: null,\n method: null,\n httpStatus: null,\n exitCode: null,\n latencyMs: null,\n detail: null,\n targetPackageName: null,\n runningVersion: null,\n latestVersion: null,\n environmentKind: null,\n requestId: null,\n checkId: null,\n registryHost: null,\n action: null,\n unknownKeys: []\n };\n if (!diag || typeof diag !== \"object\" || Array.isArray(diag)) return empty;\n const raw = diag;\n const out = { ...empty, unknownKeys: [] };\n for (const key of Object.keys(DIAG_KNOWN_TYPES)) {\n if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;\n const v = raw[key];\n if (v === void 0 || v === null) continue;\n if (typeof v !== DIAG_KNOWN_TYPES[key]) continue;\n out[key] = v;\n }\n for (const key of Object.keys(raw)) {\n if (!Object.prototype.hasOwnProperty.call(DIAG_KNOWN_TYPES, key)) out.unknownKeys.push(key);\n }\n return out;\n}\nfunction pickText(value, fallback) {\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return fallback;\n}\nfunction queueTextOf(queuePosition, _diagQueuePos) {\n void _diagQueuePos;\n const pos = typeof queuePosition === \"number\" ? queuePosition : null;\n if (pos === 0) return \"\\u6B63\\u5728\\u5B89\\u88C5\";\n if (typeof pos === \"number\") return `\\u6392\\u961F\\u7B2C ${pos} \\u4F4D`;\n return \"\\u4E0D\\u5728\\u961F\\u5217\\u91CC\";\n}\nfunction buildUpdateDiagCopy(input) {\n const rawCode = String(input.code ?? \"\").trim() || \"internal\";\n const copy = failureCopy(rawCode) ?? UNKNOWN_FAILURE_COPY;\n const diag = readDiagTolerant(input.diag);\n const detailRaw = diag.detail ?? input.detail ?? \"\";\n const detail = redactForCopy(detailRaw) || \"\\uFF08\\u672C\\u56DE\\u5305\\u6CA1\\u6709\\u5E26\\u8BCA\\u65AD\\u6458\\u8981\\uFF0C\\u7B49\\u7535\\u8BDD\\u4FA7 diag \\u843D\\u5B9A\\u540E\\u8865\\u9F50\\uFF09\";\n const pluginName = pickText(diag.targetPackageName ?? input.pluginId, \"(\\u672A\\u77E5\\u5305)\");\n const runVer = pickText(diag.runningVersion ?? input.runningVersion, \"?\");\n const instVer = pickText(input.installedVersion ?? diag.latestVersion, \"?\");\n const host = pickText(diag.environmentKind ?? input.hostKind, \"\\u672A\\u77E5\");\n const routeRaw = diag.route ?? input.route;\n const requestRaw = diag.requestId ?? input.requestId;\n const checkRaw = diag.checkId ?? input.checkId;\n const queue = queueTextOf(input.queuePosition ?? null);\n const prov = [`\\u63D2\\u4EF6=${pluginName}`, `\\u7248\\u672C=${runVer}\\u2192${instVer}`, `\\u5BBF\\u4E3B=${host}`];\n if (typeof routeRaw === \"string\" && routeRaw.trim()) prov.push(`\\u8DEF\\u7531=${routeRaw.trim()}`);\n if (diag.stage) prov.push(`\\u9636\\u6BB5=${diag.stage}`);\n if (diag.method) prov.push(`\\u65B9\\u6CD5=${diag.method}`);\n if (typeof diag.httpStatus === \"number\") prov.push(`HTTP=${diag.httpStatus}`);\n if (typeof diag.exitCode === \"number\") prov.push(`exit=${diag.exitCode}`);\n if (typeof diag.latencyMs === \"number\") prov.push(`\\u8017\\u65F6=${diag.latencyMs}ms`);\n if (diag.registryHost) prov.push(`\\u6E90=${diag.registryHost}`);\n else prov.push(\"\\u6E90=\\u672A\\u77E5\\uFF08\\u975E\\u5B98\\u65B9\\u6E90\\u65F6\\u7701\\u7565\\u672C\\u8EAB\\u5373\\u4FE1\\u606F\\uFF09\");\n if (diag.action) prov.push(`\\u5EFA\\u8BAE=${diag.action}`);\n if (typeof requestRaw === \"string\" && requestRaw.trim()) prov.push(`\\u8BF7\\u6C42=${requestRaw.trim()}`);\n if (typeof checkRaw === \"string\" && checkRaw.trim()) prov.push(`\\u68C0\\u67E5=${checkRaw.trim()}`);\n prov.push(`\\u961F\\u5217=${queue}`);\n const format = input.format === \"line\" ? \"line\" : \"block\";\n const provLine = redactForCopy(prov.join(\" \\xB7 \"));\n if (format === \"line\") {\n const line = `[update-diag] code=${rawCode} \\xB7 ${copy.zh} \\xB7 \\u6458\\u8981=${detail} \\xB7 ${provLine} \\xB7 \\u600E\\u4E48\\u529E=${copy.act}`;\n return redactForCopy(line);\n }\n const block = [\n `[update-diag] ${rawCode} \\u2014 ${copy.zh}`,\n ` \\u6458\\u8981\\uFF1A${detail}`,\n ` \\u6765\\u6E90\\uFF1A${provLine}`,\n ` \\u600E\\u4E48\\u529E\\uFF1A${copy.act}`\n ].join(\"\\n\");\n return block;\n}\nconst PANEL_SKIPPED_MAX = 50;\nfunction panelSkipKey(pluginId) {\n return `dsh-upd-skipped/${String(pluginId)}`;\n}\nfunction normalizePanelSkipped(raw) {\n if (!raw || typeof raw !== \"object\") return [];\n const list = raw.skipped;\n if (!Array.isArray(list)) return [];\n const seen = /* @__PURE__ */ new Set();\n const out = [];\n for (const item of list) {\n if (!item || typeof item !== \"object\") continue;\n const version = item.version;\n if (!validReleaseVersion(version) || seen.has(version)) continue;\n seen.add(version);\n const at = item.skippedAt;\n out.push({ version, skippedAt: typeof at === \"number\" && Number.isFinite(at) ? at : 0 });\n }\n return out.slice(0, PANEL_SKIPPED_MAX);\n}\nfunction createMemorySkipStore(now = Date.now) {\n let skipped = [];\n function assertVersion(version) {\n if (!validReleaseVersion(version)) {\n throw new Error(`[dsh-plugin-update] \\u8DF3\\u8FC7\\u7248\\u672C\\u53F7\\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u53D1\\u884C\\u7248\\u672C\\u53F7\\uFF08\\u6536\\u5230 ${JSON.stringify(version)}\\uFF09`);\n }\n return version;\n }\n return {\n list: () => skipped.map((e) => e.version),\n has: (version) => validReleaseVersion(version) ? skipped.some((e) => e.version === version) : false,\n skip: (version) => {\n const v = assertVersion(version);\n const at = now();\n skipped = [{ version: v, skippedAt: typeof at === \"number\" && Number.isFinite(at) ? at : 0 }].concat(skipped.filter((e) => e.version !== v)).slice(0, PANEL_SKIPPED_MAX);\n },\n reset: (version) => {\n if (version === void 0) skipped = [];\n else skipped = skipped.filter((e) => e.version !== version);\n }\n };\n}\nfunction probeStorage(candidate) {\n try {\n if (!candidate || typeof candidate !== \"object\") return null;\n const s = candidate;\n if (typeof s.getItem !== \"function\" || typeof s.setItem !== \"function\" || typeof s.removeItem !== \"function\") return null;\n return s;\n } catch {\n return null;\n }\n}\nfunction createBrowserSkipStore(pluginId, storage, now = Date.now) {\n const memory = createMemorySkipStore(now);\n const store = probeStorage(storage) ?? probeStorage(globalThis[\"localStorage\"] ?? null) ?? null;\n if (!store) return memory;\n const active = store;\n const key = panelSkipKey(pluginId);\n function read() {\n try {\n const raw = active.getItem(key);\n if (!raw) return [];\n return normalizePanelSkipped(JSON.parse(raw));\n } catch {\n return [];\n }\n }\n function write(entries) {\n try {\n active.setItem(key, JSON.stringify({ skipped: entries }));\n } catch {\n }\n }\n try {\n for (const e of read()) memory.skip(e.version);\n } catch {\n }\n return {\n list: () => memory.list(),\n has: (version) => memory.has(version),\n skip: (version) => {\n memory.skip(version);\n write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));\n },\n reset: (version) => {\n memory.reset(version);\n if (version === void 0) {\n try {\n active.removeItem(key);\n } catch {\n }\n } else {\n write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));\n }\n }\n };\n}\nconst SEAL_BY_KIND = {\n loading: { text: \"\\u5F85\\u67E5\", mini: \"\\u67E5\", tone: \"ink\" },\n idle: { text: \"\\u5F85\\u67E5\", mini: \"\\u67E5\", tone: \"ink\" },\n update: { text: \"\\u53EF\\u88C5\", mini: \"\\u88C5\", tone: \"green\" },\n busy: { text: \"\\u5B89\\u88C5\\u4E2D\", mini: \"\\u88C5\", tone: \"yellow\" },\n restart: { text: \"\\u5F85\\u91CD\\u542F\", mini: \"\\u542F\", tone: \"yellow\" },\n blocked: { text: \"\\u53D7\\u963B\", mini: \"\\u963B\", tone: \"red\" },\n failed: { text: \"\\u53D7\\u963B\", mini: \"\\u963B\", tone: \"red\" },\n done: { text: \"\\u5DF2\\u6700\\u65B0\", mini: \"\\u5B9A\", tone: \"green\" }\n};\nfunction messageCodeOf(message) {\n if (typeof message !== \"string\" || !message) return \"\";\n const at = message.indexOf(\":\");\n return (at < 0 ? message : message.slice(0, at)).trim();\n}\nfunction panelViewModel(input) {\n const view = panelViewModelCore(input);\n return { ...view, seal: SEAL_BY_KIND[view.banner.kind] ?? SEAL_BY_KIND.idle };\n}\nfunction panelViewModelCore(input) {\n const { snapshot, manual, queue, skippedLatest, lastError } = input;\n const errorKind = input.errorKind;\n if (!snapshot) {\n const earlyCode = typeof errorKind === \"string\" && errorKind.trim() || lastError || \"\";\n if (earlyCode) {\n const copy = failureCopy(earlyCode);\n return {\n banner: {\n kind: \"failed\",\n title: `\\u66F4\\u65B0\\u5931\\u8D25\\uFF08${earlyCode}\\uFF09\\uFF1A${copy?.zh ?? earlyCode}\\u3002`,\n action: copy?.act || \"\\u590D\\u5236\\u8BCA\\u65AD\\u53D1\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\u901A\\u9053\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u91CD\\u8BD5\\u5B89\\u88C5\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote: null\n };\n }\n return {\n banner: { kind: \"loading\", title: \"\\u6B63\\u5728\\u8BFB\\u53D6\\u66F4\\u65B0\\u72B6\\u6001\\u2026\", action: \"\" },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote: null\n };\n }\n const job = snapshot.job ?? null;\n const jobState = job?.state ?? null;\n const queueNote = queue && queue.busy ? queue.position === 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5\\uFF08\\u672C\\u63D2\\u4EF6\\u5728\\u88C5\\uFF09\\u3002\" : typeof queue.position === \"number\" ? `\\u524D\\u65B9\\u6709\\u5B89\\u88C5\\u5728\\u8FDB\\u884C\\uFF0C\\u672C\\u63D2\\u4EF6\\u6392\\u7B2C ${queue.position} \\u4F4D\\uFF0C\\u5230\\u961F\\u9996\\u518D\\u70B9\\u5B89\\u88C5\\u3002` : \"\\u524D\\u65B9\\u6709\\u5176\\u4ED6\\u63D2\\u4EF6\\u5728\\u5B89\\u88C5\\uFF0C\\u7A0D\\u540E\\u91CD\\u8BD5\\u3002\" : null;\n if (skippedLatest && snapshot.latestVersion) {\n return {\n banner: {\n kind: \"idle\",\n title: `\\u5DF2\\u8DF3\\u8FC7 ${snapshot.latestVersion}`,\n action: \"\\u70B9\\u201C\\u6062\\u590D\\u201D\\u53EF\\u91CD\\u65B0\\u63D0\\u9192\\u8BE5\\u7248\\u672C\\uFF1B\\u6709\\u66F4\\u65B0\\u7684\\u65B0\\u7248\\u672C\\u4F1A\\u7167\\u5E38\\u63D0\\u9192\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: true,\n showManual: false,\n showReset: true,\n queueNote\n };\n }\n if (snapshot.blockedReason === \"pending-restart\") {\n const latest = snapshot.latestVersion ?? snapshot.installedVersion ?? \"\";\n return {\n banner: {\n kind: \"restart\",\n // 文案照原型(d5-paper.html:329):不带 emoji——警示由横幅左侧的手绘 SVG 标承担,\n // 印章在状态一侧,两者各司其职,不再三重标记。\n title: `\\u65B0\\u7248 ${latest} \\u5DF2\\u5B89\\u88C5\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002`,\n action: BLOCKED_COPY[\"pending-restart\"].action\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (jobState === \"installing\" || jobState === \"verifying\") {\n return {\n banner: {\n kind: \"busy\",\n title: `\\u6B63\\u5728\\u5B89\\u88C5${job?.targetVersion ? ` ${job.targetVersion}` : \"\"}\\u2026\\u5173\\u95ED\\u9762\\u677F\\u4E0D\\u4F1A\\u4E2D\\u65AD\\u3002`,\n action: \"\\u8FDB\\u5EA6\\u6309\\u8F6E\\u8BE2\\u81EA\\u52A8\\u5237\\u65B0\\uFF1B\\u91CD\\u5F00\\u9762\\u677F 1 \\u79D2\\u5185\\u6062\\u590D\\u663E\\u793A\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u4E2D\\u2026\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote\n };\n }\n const jobCode = jobState === \"failed\" ? messageCodeOf(job?.message) || \"install-failed\" : \"\";\n const failedCode = typeof errorKind === \"string\" && errorKind.trim() || lastError || jobCode || \"\";\n if (failedCode || jobState === \"failed\" || jobState === \"interrupted\") {\n const code = failedCode || \"install-failed\";\n const copy = failureCopy(code);\n return {\n banner: {\n kind: \"failed\",\n title: `\\u66F4\\u65B0\\u5931\\u8D25\\uFF08${code}\\uFF09\\uFF1A${copy?.zh ?? code}\\u3002`,\n action: copy?.act || \"\\u590D\\u5236\\u8BCA\\u65AD\\u53D1\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\u901A\\u9053\\u3002\"\n },\n installEnabled: snapshot.canInstall,\n installLabel: \"\\u91CD\\u8BD5\\u5B89\\u88C5\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (snapshot.blockedReason) {\n const copy = blockedCopy(snapshot.blockedReason);\n return {\n banner: {\n kind: \"blocked\",\n title: copy ? `${copy.title}\\u3002` : `${snapshot.blockedReason}\\u3002`,\n action: copy?.action || \"\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (snapshot.canInstall && snapshot.latestVersion) {\n return {\n banner: {\n kind: \"update\",\n title: `\\u6709\\u65B0\\u7248 ${snapshot.latestVersion} \\u53EF\\u88C5\\uFF08\\u5F53\\u524D ${snapshot.runningVersion}\\uFF09\\u3002`,\n action: \"\\u70B9\\u5B89\\u88C5\\u5373\\u8D70\\u7CBE\\u786E\\u7248\\u672C\\u5B89\\u88C5\\uFF1B\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u540C\\u65F6\\u53EA\\u88C5\\u4E00\\u4E2A\\u3002\"\n },\n installEnabled: true,\n installLabel: `\\u5B89\\u88C5 ${snapshot.latestVersion}`,\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n return {\n banner: { kind: \"done\", title: \"\\u5DF2\\u662F\\u6700\\u65B0\\uFF0C\\u65E0\\u9700\\u66F4\\u65B0\\u3002\", action: \"\" },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote\n };\n}\nconst UPDATE_PANEL_CSS = [\n '.dsh-upd{font:14px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937);',\n \"background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:8px;padding:12px 14px;max-width:560px;\",\n // 横幅配色走变量(浅色默认 + 深色覆盖,见下方 dark 媒体块):硬编码浅色会让深色下\n // 「浅底 + 浅字」读不出来(现场回归:默认主题深色模式更新横幅白底浅字)。\n \"--dsh-upd-ok-bg:#ecfdf5;--dsh-upd-ok-line:#059669;--dsh-upd-warn-bg:#fffbeb;--dsh-upd-warn-line:#d97706;\",\n \"--dsh-upd-bad-bg:#fef2f2;--dsh-upd-bad-line:#dc2626;--dsh-upd-busy-bg:#eff6ff;--dsh-upd-busy-line:#2563eb}\",\n \".dsh-upd *{box-sizing:border-box}\",\n \".dsh-upd button{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;background:var(--dsh-upd-btn,#f9fafb);\",\n \"color:inherit;padding:4px 12px;cursor:pointer;margin:2px 6px 2px 0}\",\n \".dsh-upd button:disabled{opacity:.45;cursor:not-allowed}\",\n \".dsh-upd button:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}\",\n '.dsh-upd button[data-primary=\"1\"]{background:var(--dsh-upd-primary,#2563eb);border-color:var(--dsh-upd-primary,#2563eb);color:#fff}',\n \".dsh-upd-banner{border-left:4px solid var(--dsh-upd-line,#9ca3af);padding:6px 10px;margin:0 0 8px;background:var(--dsh-upd-soft,#f3f4f6)}\",\n '.dsh-upd-banner[data-kind=\"restart\"]{border-color:var(--dsh-upd-warn-line);background:var(--dsh-upd-warn-bg)}',\n '.dsh-upd-banner[data-kind=\"failed\"],.dsh-upd-banner[data-kind=\"blocked\"]{border-color:var(--dsh-upd-bad-line);background:var(--dsh-upd-bad-bg)}',\n '.dsh-upd-banner[data-kind=\"update\"]{border-color:var(--dsh-upd-ok-line);background:var(--dsh-upd-ok-bg)}',\n '.dsh-upd-banner[data-kind=\"busy\"]{border-color:var(--dsh-upd-busy-line);background:var(--dsh-upd-busy-bg)}',\n \".dsh-upd code{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}\",\n \".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:8px 0;font-size:13px}\",\n \".dsh-upd-changelog-wrap{margin:8px 0;font-size:13px;border-top:1px solid var(--dsh-upd-line,#e5e7eb);padding-top:8px}\",\n \".dsh-upd-changelog-title{font-weight:700;margin:0 0 4px}\",\n \".dsh-upd-changelog-version{margin:6px 0}\",\n \".dsh-upd-changelog-catname{font-weight:600;margin:6px 0 2px}\",\n \".dsh-upd-changelog ul{margin:2px 0 6px 20px;padding:0}\",\n \".dsh-upd-changelog li{margin:2px 0}\",\n \".dsh-upd-changelog-fold{margin:4px 0}\",\n \".dsh-upd-changelog-fold>summary{cursor:pointer}\",\n \".dsh-upd-changelog-count{font-size:12px;opacity:.7;margin:2px 0 4px}\",\n \".dsh-upd-changelog-yanked{border-left:4px solid var(--dsh-upd-warn-line,#d97706);background:var(--dsh-upd-warn-bg,#fffbeb);padding:6px 10px;margin:6px 0;font-size:13px}\",\n \".dsh-upd-breaking-badge{display:inline-block;font-size:11px;font-weight:700;border:1px solid currentColor;border-radius:3px;padding:0 5px;margin-right:6px;vertical-align:baseline}\",\n \".dsh-upd-changelog-security-more{margin:4px 0 6px}\",\n \".dsh-upd-changelog-security-more>summary{cursor:pointer;font-size:12px;opacity:.8}\",\n \".dsh-upd-changelog-more-note{font-size:12px;opacity:.7;margin:2px 0 4px}\",\n \".dsh-upd-changelog-neutral{color:inherit;opacity:.8}\",\n \".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}\",\n \".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;overflow:auto}\",\n // —— 档案头 / 版本条 / 章节 / 进度条 / 跳过行(原型 :208-245 的新结构,默认主题给最小可用样式)——\n \".dsh-upd-head{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}\",\n // 卷宗抬头「插件更新 / 更新档案 卷」:D5 档案卷才画,默认(最小)主题不画。\n // 两个主题共用同一份内核 HTML(见 renderUpdatePanelHTML 的注释),画不画是皮肤决定的事。\n \".dsh-upd-masthead{display:none}\",\n \".dsh-upd-name{font-weight:700}\",\n \".dsh-upd-meta{font-size:12.5px;opacity:.75}\",\n \".dsh-upd-proftag{font-family:Consolas,Menlo,monospace;font-size:11px;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:3px;padding:0 5px;margin-left:6px;letter-spacing:.06em}\",\n \".dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;font-size:12.5px}\",\n \".dsh-upd-strip>div{flex:1 1 110px;padding:6px 10px;border-left:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-strip>div:first-child{border-left:0}\",\n \".dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.14em;opacity:.7}\",\n \".dsh-upd-strip-v{font-family:Consolas,Menlo,monospace;font-size:12.5px}\",\n \".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-chap-head{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}\",\n \".dsh-upd-chap-no{font-size:13px;font-style:italic;opacity:.6}\",\n \".dsh-upd-chap-title{font-size:14px;margin:0}\",\n \".dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-upd-line,#e5e7eb);transform:translateY(-3px)}\",\n // 03 章标题行右端的开关(问题 3 定案:按钮形态、挪到标题行)。\n \".dsh-upd-chap-note{flex:none;font-size:12px;opacity:.75}\",\n \".dsh-upd-chap-note button{margin:0}\",\n // 更新队列两行键值(用户定案的设计):结构两主题共用,皮肤各自收敛。\n \".dsh-upd-qrow{display:flex;align-items:baseline;gap:10px;padding:6px 0}\",\n \".dsh-upd-qrow+.dsh-upd-qrow{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-qdot{width:8px;height:8px;border-radius:50%;flex:none;align-self:center;background:currentColor;opacity:.5}\",\n '.dsh-upd-qdot[data-tone=\"busy\"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',\n '.dsh-upd-qdot[data-tone=\"you\"]{background:var(--dsh-upd-primary,#2563eb);opacity:1}',\n \".dsh-upd-qk{flex:none;width:5.5em;font-size:12px;opacity:.7}\",\n \".dsh-upd-qv{font-weight:600}\",\n \".dsh-upd-qn{margin-left:auto;font-size:12px;opacity:.7}\",\n \".dsh-upd-qseq{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}\",\n \".dsh-upd-prog{height:8px;background:var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;margin:10px 0 4px}\",\n \".dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-upd-primary,#2563eb);transition:width .3s}\",\n \".dsh-upd-progtxt{font-size:12.5px;opacity:.75}\",\n \".dsh-upd-skipline{font-size:13px;margin-top:8px}\",\n // 骨架微光:只在首帧 loading 出现;reduced-motion 下静止占位,不断语义。\n \".dsh-upd-skv{display:inline-block;min-width:64px;border-radius:3px;color:transparent !important;user-select:none;\",\n \"background:linear-gradient(90deg,var(--dsh-upd-line,#e5e7eb) 25%,var(--dsh-upd-soft,#f3f4f6) 50%,var(--dsh-upd-line,#e5e7eb) 75%);\",\n \"background-size:200% 100%;animation:dsh-upd-shimmer 1.2s linear infinite}\",\n \"@keyframes dsh-upd-shimmer{to{background-position:-200% 0}}\",\n \".dsh-upd-tag{display:inline-block;border:1px dashed currentColor;border-radius:3px;padding:1px 8px;margin-right:8px;font-family:Consolas,Menlo,monospace;font-size:12px}\",\n \".dsh-upd-err{font-size:13px;margin:0 0 6px}\",\n // —— 全按钮交互反馈(#36:悬停/按下/过渡/在途忙态;浅深双主题通用写法,不碰上面的既有串)——\n \".dsh-upd button{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}\",\n \".dsh-upd button:hover:not(:disabled){border-color:var(--dsh-upd-focus,#2563eb)}\",\n '.dsh-upd button[data-primary=\"1\"]:hover:not(:disabled){filter:brightness(.93)}',\n \".dsh-upd button:active:not(:disabled){transform:translateY(1px)}\",\n '.dsh-upd button[aria-busy=\"true\"]{cursor:wait;animation:dsh-upd-pulse 1s ease-in-out infinite}',\n \"@keyframes dsh-upd-pulse{0%,100%{opacity:1}50%{opacity:.55}}\",\n // 在途转圈:纯 CSS ::after,不加 DOM 节点(内核 DOM 冻结);转的是边框缺口,不是 emoji。\n '.dsh-upd button[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:11px;height:11px;margin-left:8px;vertical-align:-1px;',\n \"border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-spin .8s linear infinite}\",\n \"@keyframes dsh-upd-spin{to{transform:rotate(360deg)}}\",\n '@media (prefers-reduced-motion: reduce){.dsh-upd button{transition:none}.dsh-upd button:active:not(:disabled){transform:none}.dsh-upd button[aria-busy=\"true\"]{animation:none}.dsh-upd-skv{animation:none}}',\n \"@media (prefers-color-scheme: dark){.dsh-upd{--dsh-upd-fg:#e5e7eb;--dsh-upd-bg:#111827;--dsh-upd-line:#374151;\",\n \"--dsh-upd-btn:#1f2937;--dsh-upd-soft:#1f2937;--dsh-upd-primary:#3b82f6;--dsh-upd-focus:#93c5fd;\",\n // 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。\n \"--dsh-upd-ok-bg:rgba(16,185,129,.14);--dsh-upd-ok-line:#34d399;\",\n \"--dsh-upd-warn-bg:rgba(245,158,11,.16);--dsh-upd-warn-line:#fbbf24;\",\n \"--dsh-upd-bad-bg:rgba(239,68,68,.16);--dsh-upd-bad-line:#f87171;\",\n \"--dsh-upd-busy-bg:rgba(59,130,246,.16);--dsh-upd-busy-line:#60a5fa}}\"\n].join(\"\\n\");\nconst UPDATE_PANEL_D5_CSS = [\n \"/* D5 \\u6863\\u6848\\u5377\\u53EF\\u9009\\u4E3B\\u9898\\uFF1A\\u53EA\\u6362\\u989C\\u8272/\\u5B57\\u4F53/\\u95F4\\u8DDD\\uFF1B\\u5185\\u6838 DOM \\u987A\\u5E8F\\u4E00\\u5B57\\u4E0D\\u52A8\\uFF0C\\u4E0D\\u65AD\\u590D\\u5236\\u8BCA\\u65AD\\u3002 */\",\n '.dsh-upd[data-theme=\"d5-paper\"]{--d5-bg:#f7f3ea;--d5-card:#fffdf6;--d5-ink:#1a1a1a;--d5-muted:#6f675a;',\n \"--d5-line:#e3d9c4;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-accent-deep:#9c2e1d;\",\n \"--d5-ok:#1a7f37;--d5-ok-bg:#e9f4ea;--d5-warn:#8a5a00;--d5-warn-bg:#fbf0d0;\",\n \"--d5-bad:#b3261e;--d5-bad-bg:#fbe9e5;\",\n '--d5-serif:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",\"Noto Serif CJK SC\",\"Source Han Serif SC\",serif;',\n '--d5-sans:system-ui,\"PingFang SC\",\"Hiragino Sans GB\",\"Microsoft YaHei\",sans-serif;',\n '--d5-mono:ui-monospace,\"SF Mono\",SFMono-Regular,Consolas,\"Noto Sans Mono\",monospace;',\n \"--d5-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);\",\n \"font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);\",\n \"border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}\",\n // 按钮脸自己不透明(宿主底色未知时也读得出;卡片上渲染与 transparent 逐字同色)。\n '.dsh-upd[data-theme=\"d5-paper\"] button{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',\n '.dsh-upd[data-theme=\"d5-paper\"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"d5-paper\"] button[data-primary=\"1\"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',\n '.dsh-upd[data-theme=\"d5-paper\"] button[data-primary=\"1\"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',\n '.dsh-upd[data-theme=\"d5-paper\"] button:focus-visible{outline:2px solid var(--d5-accent);outline-offset:2px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner{background:var(--d5-bg);border-color:var(--d5-line-strong)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"update\"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"busy\"]{border-color:var(--d5-warn);background:var(--d5-warn-bg)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"restart\"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"failed\"],.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"blocked\"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"done\"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-changelog-yanked{border-color:var(--d5-warn);background:var(--d5-warn-bg);color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-breaking-badge{color:var(--d5-accent);border-color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-changelog-count{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-changelog-more-note{color:var(--d5-muted)}',\n // —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——\n '.dsh-upd[data-theme=\"d5-paper\"]{position:relative;padding:22px 26px 20px}',\n '.dsh-upd[data-theme=\"d5-paper\"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',\n \"display:flex;align-items:center;justify-content:center;text-align:center;letter-spacing:.18em;text-indent:.18em;line-height:1.35;\",\n \"border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--d5-serif);font-weight:700;font-size:21px;\",\n \"background:color-mix(in srgb,currentColor 8%,transparent);user-select:none;pointer-events:none;\",\n \"box-shadow:inset 0 0 0 5px var(--d5-card),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}\",\n '.dsh-upd[data-theme=\"d5-paper\"][data-seal-tone=\"ink\"]::before{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"][data-seal-tone=\"green\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"d5-paper\"][data-seal-tone=\"yellow\"]::before{color:var(--d5-warn)}',\n '.dsh-upd[data-theme=\"d5-paper\"][data-seal-tone=\"red\"]::before{color:var(--d5-bad)}',\n // 印章占位:首行(横幅/状态行)右侧留出 120px,文字不许压到印章上(原型 .filehead padding-right:120px)\n // —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 D5 档案卷才显示 ——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-masthead{display:block;padding:0 0 10px;margin:0 0 12px;border-bottom:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--d5-muted);margin-bottom:3px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-masthead-title{font-family:var(--d5-serif);font-size:26px;font-weight:700;line-height:1.2}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-masthead-title i{color:var(--d5-accent);font-style:normal}',\n // 横幅不再给大印章留 124px:实测(headless 量盒子)印章盒底边 y=109,横幅正文顶边 y=108、\n // 状态行那句在 y=159——印章只压到横幅顶部的留白带,压不到正文。留着反而把 27px 那句话挤成两行\n // (27px 单行需 428px,留白后只剩 366px)。档案头那 120px 保留:那里是真的重叠。\n // —— 更新队列(03 章)D5 皮肤 ——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-qrow{padding:8px 0}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-qk{width:66px;letter-spacing:.18em}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-qv{font-family:var(--d5-serif);font-size:16px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-qseq{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chap-note{color:var(--d5-muted)}',\n // —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——\n // 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"loading\"]::before,.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"idle\"]::before,',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"update\"]::before,.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"busy\"]::before,',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"blocked\"]::before,.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"failed\"]::before,',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"done\"]::before{content:attr(data-mini);display:inline-flex;align-items:center;justify-content:center;',\n \"width:30px;height:30px;margin-right:10px;vertical-align:middle;border:2px solid currentColor;border-radius:7px;\",\n \"font-family:var(--d5-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--d5-muted)}\",\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"update\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"busy\"]::before{color:var(--d5-warn)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"blocked\"]::before,.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"failed\"]::before{color:var(--d5-bad)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"done\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-log code{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;letter-spacing:.06em}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-manual code{display:block;background:var(--d5-ink);color:var(--d5-bg);font-family:var(--d5-mono);font-size:12.5px;padding:12px 14px;border-radius:4px;white-space:pre-wrap;word-break:break-all}',\n // 待重启标记:手绘 SVG 当**独立 flex 标记**放在文字块左侧(原型 :446 `.mark` 是独立节点),\n // 不能用行内背景——那样换行时三角会落在句子中间把话劈开(现场回归)。\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child{display:flex;gap:10px;align-items:flex-start}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{content:\"\";flex:none;width:20px;height:20px;margin-top:3px;',\n 'background:url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%238a5a00%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E\") no-repeat center/20px 20px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-actions{flex-wrap:wrap}',\n // —— 档案头(原型 :208-211 `.filehead`:serif 插件名 22px + 使用范围 + profile 牌;右侧留章位)——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-meta b{color:var(--d5-ink);font-weight:600}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-proftag{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;margin-left:8px;letter-spacing:.06em}',\n // —— 版本条(原型 :216 `.strip`:三格,格间一线,左上小写标签 + 等宽值)——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:10px 0 0;border:1px solid var(--d5-line);border-radius:4px;overflow:hidden;font-size:12.5px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-strip>div{flex:1 1 120px;padding:8px 12px;border-left:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-strip>div:first-child{border-left:0}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-strip-v{font-family:var(--d5-mono);font-size:13px}',\n // —— 章节(原型 :218-245:01–05 编号 + 衬线标题 + 细线)——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chapter{margin-top:26px;padding-top:16px;border-top:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chap-head{display:flex;align-items:baseline;gap:12px;margin-bottom:10px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chap-no{font-family:var(--d5-serif);font-style:italic;font-size:15px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chap-title{font-family:var(--d5-serif);font-size:17px;margin:0;letter-spacing:.1em}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--d5-line);transform:translateY(-4px)}',\n // —— 横幅即状态行 / 待重启横幅(原型 :81-85 `.restart-banner`:2px 边框、圆角 4、内边距 12/16、衬线;右侧留章位)——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner{border:2px solid var(--d5-line-strong);border-radius:4px;padding:12px 16px;font-size:14.5px;font-family:var(--d5-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner>div:first-child{flex:1 1 auto;min-width:0}',\n // 状态行字号照原型 .status-line=27px(实测去掉横幅右侧占位后可写 486px > 428px,一行放得下)\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner>div:first-child strong{font-family:var(--d5-serif);font-size:27px;font-weight:700;line-height:1.35}',\n // —— 进度条 / 跳过行(原型 :132-133 `.prog`、:129-131 `.skipline .tag`)——\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-prog{height:8px;background:var(--d5-line);border-radius:4px;overflow:hidden;margin:10px 0 4px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--d5-accent);transition:width .3s}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-progtxt{font-size:12.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-skipline{font-size:13px;margin-top:8px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-tag{display:inline-block;border:1px dashed var(--d5-line-strong);border-radius:3px;padding:1px 8px;margin-right:8px;font-family:var(--d5-mono);font-size:12px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--d5-muted)}',\n '@media (max-width:640px){.dsh-upd[data-theme=\"d5-paper\"]{padding:10px 12px}.dsh-upd[data-theme=\"d5-paper\"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--d5-card),inset 0 0 0 5px currentColor}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner>div:first-child{white-space:normal}}',\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"d5-paper\"]{--d5-bg:#141210;--d5-card:#1e1a15;--d5-ink:#ece5d3;--d5-muted:#a89c83;',\n \"--d5-line:#3a3226;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-accent-deep:#f0866b;\",\n \"--d5-ok:#8fd6a4;--d5-ok-bg:rgba(80,180,120,.12);--d5-warn:#e8c15a;--d5-warn-bg:rgba(232,193,90,.12);\",\n \"--d5-bad:#ef8a7d;--d5-bad-bg:rgba(239,138,125,.12);--d5-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.45)}}\",\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"d5-paper\"] button[data-primary=\"1\"]{color:#141210}.dsh-upd[data-theme=\"d5-paper\"] button:focus-visible{outline-color:var(--d5-accent-deep)}}',\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23e8c15a%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E\")}}',\n '@media (forced-colors: active){.dsh-upd[data-theme=\"d5-paper\"]{box-shadow:none}.dsh-upd[data-theme=\"d5-paper\"]::before{background:none;box-shadow:none;border-color:CanvasText;color:CanvasText}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner{border:1px solid CanvasText}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner::before{border-color:CanvasText;color:CanvasText;background:Canvas}.dsh-upd[data-theme=\"d5-paper\"] button{border:1px solid ButtonText}.dsh-upd[data-theme=\"d5-paper\"] button[data-primary=\"1\"]{background:ButtonFace;color:ButtonText;border-color:ButtonText}.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{background-image:none;content:\"\\u26A0\"}}',\n '@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme=\"d5-paper\"] *{transition:none !important;animation:none !important}}'\n].join(\"\\n\");\nfunction escapeHtml(text) {\n return String(text).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nconst CHAPTER_TITLES = [\"\\u68C0\\u67E5\\u4E0E\\u5B89\\u88C5\", \"\\u66F4\\u65B0\\u65E5\\u5FD7\", \"\\u66F4\\u65B0\\u961F\\u5217\", \"\\u9519\\u8BEF\\u4FE1\\u606F\", \"\\u624B\\u5DE5\\u547D\\u4EE4\"];\nfunction chapterOf(index, inner, note = \"\") {\n const no = String(index).padStart(2, \"0\");\n const title = CHAPTER_TITLES[index - 1];\n return `<section class=\"dsh-upd-chapter\" data-chapter=\"${no}\"><div class=\"dsh-upd-chap-head\"><span class=\"dsh-upd-chap-no\">${no}</span><h3 class=\"dsh-upd-chap-title\">${escapeHtml(title)}</h3><span class=\"dsh-upd-chap-rule\"></span>${note}</div>${inner}</section>`;\n}\nfunction skeletonStrip(loading) {\n if (!loading) return \"\";\n const cell = (k) => `<div><span class=\"dsh-upd-strip-k\">${escapeHtml(k)}</span><span class=\"dsh-upd-strip-v dsh-upd-skv\" aria-hidden=\"true\">\\u2026</span></div>`;\n return `<div class=\"dsh-upd-strip\" aria-hidden=\"true\">` + cell(\"\\u8FD0\\u884C\") + cell(\"\\u78C1\\u76D8\") + cell(\"\\u8FDC\\u7AEF\") + `</div>`;\n}\nfunction versionStrip(snapshot) {\n const cell = (k, v) => `<div><span class=\"dsh-upd-strip-k\">${escapeHtml(k)}</span><span class=\"dsh-upd-strip-v\">${escapeHtml(v ?? \"\\u672A\\u77E5\")}</span></div>`;\n if (!snapshot) return \"\";\n return `<div class=\"dsh-upd-strip\">` + cell(\"\\u8FD0\\u884C\", snapshot.runningVersion) + cell(\"\\u78C1\\u76D8\", snapshot.installedVersion) + cell(\"\\u8FDC\\u7AEF\", snapshot.latestVersion) + `</div>`;\n}\nfunction progressBar(snapshot) {\n const job = snapshot?.job;\n if (!job) return \"\";\n const state = String(job.state);\n if (state !== \"installing\" && state !== \"verifying\") return \"\";\n const width = state === \"installing\" ? 60 : 90;\n const text = state === \"installing\" ? \"\\u6B63\\u5728\\u5B89\\u88C5\\u65B0\\u7248\\u2026\" : \"\\u6B63\\u5728\\u6821\\u9A8C\\u5B89\\u88C5\\u7ED3\\u679C\\u2026\";\n return `<div class=\"dsh-upd-prog\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"${width}\"><i class=\"dsh-upd-prog-bar\" style=\"width:${width}%\"></i></div><div class=\"dsh-upd-progtxt\">${escapeHtml(text)}</div>`;\n}\nfunction renderUpdatePanelKernel(input, view) {\n const { snapshot, manual, queue, mode, showOthers, pluginId, copyNotice } = input;\n const changelogMarkdown = input.changelogMarkdown ?? null;\n const profileName = typeof input.profileName === \"string\" && input.profileName ? input.profileName : null;\n const b = view.banner;\n const seal = view.seal;\n const parts = [];\n parts.push(\n '<div class=\"dsh-upd-masthead\"><span class=\"dsh-upd-masthead-kicker\">\\u63D2\\u4EF6\\u66F4\\u65B0</span><span class=\"dsh-upd-masthead-title\">\\u66F4\\u65B0\\u6863\\u6848 <i>\\u5377</i></span></div>'\n );\n parts.push(\n `<div class=\"dsh-upd-head\"><span class=\"dsh-upd-name\">${escapeHtml(pluginId)}</span><span class=\"dsh-upd-meta\">\\u4F7F\\u7528\\u8303\\u56F4 <b>${escapeHtml(profileName ?? \"\\u672A\\u77E5\")}</b><span class=\"dsh-upd-proftag\">profile</span></span></div>`\n );\n parts.push(`<div class=\"dsh-upd-banner\" data-kind=\"${b.kind}\" data-mini=\"${escapeHtml(seal.mini)}\" role=\"status\" aria-live=\"polite\">`);\n parts.push(`<div><strong>${escapeHtml(b.title)}</strong></div>`);\n if (b.action) parts.push(`<div>${escapeHtml(b.action)}</div>`);\n parts.push(\"</div>\");\n parts.push(snapshot ? versionStrip(snapshot) : skeletonStrip(view.banner.kind === \"loading\"));\n const showActions = input.actions !== \"none\";\n const actions = [];\n if (showActions) {\n const busyAct = input.busyAct;\n const checkBusy = busyAct === \"check\";\n const installBusy = busyAct === \"install\";\n actions.push('<div class=\"dsh-upd-actions\">');\n actions.push(\n (checkBusy ? `<button type=\"button\" data-action=\"check\" disabled aria-busy=\"true\" title=\"\\u6B63\\u5728\\u5411\\u5B98\\u65B9\\u6E90\\u67E5\\u8BE2\\uFF0C\\u8BF7\\u7A0D\\u5019\">\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026</button>` : `<button type=\"button\" data-action=\"check\" title=\"\\u91CD\\u65B0\\u5411\\u5B98\\u65B9\\u6E90\\u67E5\\u4E00\\u6B21\\u65B0\\u7248\\uFF08\\u53EA\\u8BFB\\uFF0C\\u4E0D\\u5B89\\u88C5\\uFF09\">\\u67E5\\u65B0\\u7248</button>`) + (installBusy ? `<button type=\"button\" data-action=\"install\" data-primary=\"1\" disabled aria-busy=\"true\" title=\"\\u6B63\\u5728\\u5B89\\u88C5\\uFF0C\\u8BF7\\u7A0D\\u5019\">\\u6B63\\u5728\\u5B89\\u88C5\\u2026</button>` : `<button type=\"button\" data-action=\"install\" data-primary=\"1\" title=\"\\u7528\\u7CBE\\u786E\\u7248\\u672C\\u5B89\\u88C5\\uFF1B\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u540C\\u65F6\\u53EA\\u88C5\\u4E00\\u4E2A\"${view.installEnabled ? \"\" : \" disabled\"}>${escapeHtml(view.installLabel)}</button>`)\n );\n if (snapshot?.latestVersion && !view.skippedLatest && view.banner.kind === \"update\") {\n actions.push(`<button type=\"button\" data-action=\"skip\" title=\"\\u8BE5\\u7248\\u672C\\u4E0D\\u518D\\u63D0\\u9192\\uFF1B\\u6709\\u66F4\\u65B0\\u7684\\u65B0\\u7248\\u672C\\u7167\\u5E38\\u63D0\\u9192\">\\u8DF3\\u8FC7\\u8BE5\\u7248\\u672C</button>`);\n }\n if (view.showReset && snapshot?.latestVersion) {\n actions.push(`<button type=\"button\" data-action=\"reset-skip\" title=\"\\u64A4\\u9500\\u8DF3\\u8FC7\\uFF0C\\u8BE5\\u7248\\u672C\\u91CD\\u65B0\\u63D0\\u9192\">\\u6062\\u590D\\uFF08${escapeHtml(snapshot.latestVersion)}\\uFF09</button>`);\n }\n if (view.showManual && manual) {\n actions.push(`<button type=\"button\" data-action=\"copy-manual\" title=\"\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\">\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4</button>`);\n }\n if (b.kind === \"restart\") {\n actions.push(`<button type=\"button\" data-action=\"restart-hint\" data-primary=\"1\" title=\"\\u5BBF\\u4E3B\\u6CA1\\u6709\\u81EA\\u91CD\\u542F\\u7535\\u8BDD\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\">\\u91CD\\u542F\\u5BBF\\u4E3B</button>`);\n }\n if (snapshot) {\n actions.push(`<button type=\"button\" data-action=\"copy-diag\" title=\"\\u590D\\u5236\\u5DF2\\u8131\\u654F\\u8BCA\\u65AD\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\">\\u590D\\u5236\\u8BCA\\u65AD</button>`);\n }\n if (mode === \"dialog\") {\n actions.push(`<button type=\"button\" data-action=\"close-view\" title=\"\\u5173\\u95ED\\u9762\\u677F\\uFF08\\u5B89\\u88C5\\u5728\\u5BBF\\u4E3B\\u4FA7\\u7EE7\\u7EED\\u8DD1\\uFF0C\\u53EF\\u91CD\\u5F00\\u6062\\u590D\\u663E\\u793A\\uFF09\">\\u5173\\u95ED</button>`);\n }\n actions.push(\"</div>\");\n }\n actions.push(progressBar(snapshot));\n if (view.skippedLatest && snapshot?.latestVersion) {\n actions.push(\n `<div class=\"dsh-upd-skipline\"><span class=\"dsh-upd-tag\">\\u5DF2\\u8DF3\\u8FC7 ${escapeHtml(snapshot.latestVersion)}</span>\\u70B9\\u300C\\u6062\\u590D\\u300D\\u53EF\\u64A4\\u9500\\uFF0C\\u4E4B\\u540E\\u8FD9\\u4E00\\u7248\\u8FD8\\u4F1A\\u518D\\u63D0\\u9192\\u3002</div>`\n );\n }\n parts.push(chapterOf(1, actions.join(\"\")));\n {\n let inner = \"\";\n if (snapshot && snapshot.latestVersion) {\n try {\n const mdText = typeof changelogMarkdown === \"string\" ? changelogMarkdown : \"\";\n const entries = parseChangelog(mdText);\n const ranged = changelogForUpdate(\n entries,\n snapshot.runningVersion,\n snapshot.latestVersion,\n snapshot.installedVersion\n );\n const changelogHTML = renderChangelogHTML(ranged);\n const fromText = String(snapshot.runningVersion ?? \"\");\n const toText = String(snapshot.latestVersion ?? \"\");\n const rangeTitle = fromText && toText ? `\\u66F4\\u65B0\\u8BF4\\u660E\\uFF08${fromText} \\u2192 ${toText}\\uFF09\\uFF1A` : \"\\u66F4\\u65B0\\u8BF4\\u660E\\uFF1A\";\n let yankedBanner = \"\";\n try {\n const toEntry = Array.isArray(ranged) ? ranged.find(function(e) {\n try {\n return e && e.version === toText;\n } catch {\n return false;\n }\n }) : null;\n if (toEntry && toEntry.yanked === true && toText) {\n yankedBanner = yankedBannerHTML(toText);\n }\n } catch {\n yankedBanner = \"\";\n }\n inner = `<div class=\"dsh-upd-changelog-wrap\"><div>${escapeHtml(rangeTitle)}</div>${yankedBanner}\n${changelogHTML}\n</div>`;\n } catch {\n inner = \"\";\n }\n }\n if (!inner) {\n inner = `<div class=\"dsh-upd-changelog-wrap\"><div class=\"dsh-upd-changelog-neutral\">${escapeHtml(\n snapshot && snapshot.latestVersion ? \"\\u65E5\\u5FD7\\u8BFB\\u4E0D\\u51FA\\u6765\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\u3002\" : \"\\u8FD8\\u6CA1\\u67E5\\u5230\\u65B0\\u7248\\uFF1B\\u67E5\\u5230\\u540E\\u518D\\u663E\\u793A\\u65E5\\u5FD7\\u3002\"\n )}</div></div>`;\n }\n parts.push(chapterOf(2, inner));\n }\n {\n const queued = !!queue && (queue.busy || queue.waiting.length > 0);\n if (queued && queue) {\n const busy = queue.busy;\n const ownerRaw = queue.owner;\n const named = ownerRaw && \"pluginId\" in ownerRaw && ownerRaw.pluginId ? String(ownerRaw.pluginId) : null;\n const version = ownerRaw && \"targetVersion\" in ownerRaw && ownerRaw.targetVersion ? `@${String(ownerRaw.targetVersion)}` : \"\";\n const aboutSelf = named !== null && named === pluginId;\n const reveal = showOthers === true;\n const ownerShown = !busy ? \"\\u7A7A\\u95F2\" : named === null ? \"\\u5176\\u4ED6\\u63D2\\u4EF6\" : aboutSelf ? \"\\u672C\\u63D2\\u4EF6\" : reveal ? named : \"\\u5176\\u4ED6\\u63D2\\u4EF6\";\n const ownerVer = busy && named !== null && (aboutSelf || reveal) ? version : \"\";\n const pos = typeof queue.position === \"number\" ? queue.position : null;\n const posText = pos === null ? \"\\u672A\\u6392\\u961F\" : `\\u7B2C ${pos} \\u4F4D`;\n const posNote = pos === null ? \"\" : pos === 1 ? \"\\u4E0B\\u4E00\\u4E2A\\u5C31\\u662F\\u4F60\" : `\\u524D\\u65B9 ${pos - 1} \\u4E2A`;\n const rows = [\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\" data-tone=\"${busy ? \"busy\" : \"idle\"}\"></span><span class=\"dsh-upd-qk\">\\u6B63\\u5728\\u5B89\\u88C5</span><span class=\"dsh-upd-qv\">${escapeHtml(ownerShown + ownerVer)}</span><span class=\"dsh-upd-qn\">${busy ? \"\\u88C5\\u5B8C\\u81EA\\u52A8\\u8F6E\\u5230\\u4F60\" : \"\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u4E00\\u6B21\\u53EA\\u88C5\\u4E00\\u4E2A\"}</span></div>`,\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\" data-tone=\"you\"></span><span class=\"dsh-upd-qk\">\\u4F60\\u7684\\u987A\\u4F4D</span><span class=\"dsh-upd-qv\">${escapeHtml(posText)}</span><span class=\"dsh-upd-qn\">${escapeHtml(posNote)}</span></div>`\n ];\n if (reveal && queue.waiting.length > 0) {\n const seq = queue.waiting.map((e) => `${e.pluginId}${e.targetVersion ? `@${e.targetVersion}` : \"\"}`).join(\" \\u2192 \");\n rows.push(\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\"></span><span class=\"dsh-upd-qk\">\\u6392\\u961F\\u987A\\u5E8F</span><span class=\"dsh-upd-qseq\">${escapeHtml(seq)}</span></div>`\n );\n }\n const note = showActions ? `<span class=\"dsh-upd-chap-note\"><button type=\"button\" data-action=\"toggle-queue\">${reveal ? \"\\u9690\\u85CF\\u4ED6\\u4EBA\\u660E\\u7EC6\" : \"\\u663E\\u793A\\u5176\\u4ED6\\u63D2\\u4EF6\"}</button></span>` : \"\";\n parts.push(chapterOf(3, `<div class=\"dsh-upd-queue\">${rows.join(\"\")}</div>`, note));\n } else {\n parts.push(\n chapterOf(\n 3,\n `<div class=\"dsh-upd-queue\"><div class=\"dsh-upd-changelog-neutral\">${escapeHtml(\n view.queueNote ?? \"\\u5F53\\u524D\\u6CA1\\u6709\\u6392\\u961F\\u4EFB\\u52A1\\uFF0C\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u4E00\\u6B21\\u53EA\\u88C5\\u4E00\\u4E2A\\u3002\"\n )}</div></div>`\n )\n );\n }\n }\n {\n const errLines = [];\n if (b.kind === \"failed\" || b.kind === \"blocked\") {\n errLines.push(\n `<div class=\"dsh-upd-err\">\\u7A33\\u5B9A\\u7801 <code>${escapeHtml(String(b.kind === \"blocked\" ? snapshot?.blockedReason ?? b.kind : \"install-failed\"))}</code>\\uFF1A\\u4E0A\\u4E00\\u6761\\u4E2D\\u6587\\u8BF4\\u660E\\u5C31\\u662F\\u8981\\u7528\\u6237\\u505A\\u7684\\u4E8B\\uFF1B\\u8981\\u5F80\\u4E0A\\u6E38\\u62A5\\uFF0C\\u7528\\u300C\\u590D\\u5236\\u8BCA\\u65AD\\u300D\\u6574\\u6BB5\\u7C98\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002</div>`\n );\n }\n errLines.push(\n `<div class=\"dsh-upd-log\">\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\uFF1A\\u6309\\u63D2\\u4EF6\\u6807\\u8BC6 <code>${escapeHtml(pluginId)}</code> \\u8FC7\\u6EE4 <code>host.call</code>\\u3001<code>host.call.fail</code>\\u3001<code>update.install.exec</code> \\u4E09\\u4E2A\\u4E8B\\u4EF6\\u3002</div>`\n );\n if (copyNotice) errLines.push(`<div class=\"dsh-upd-copy\" role=\"status\">${escapeHtml(copyNotice)}</div>`);\n parts.push(chapterOf(4, errLines.join(\"\")));\n }\n parts.push(\n chapterOf(\n 5,\n view.showManual && manual ? `<div class=\"dsh-upd-manual\"><div>\\u624B\\u5DE5\\u515C\\u5E95\\u547D\\u4EE4\\uFF08\\u590D\\u5236\\u6574\\u884C\\u6267\\u884C\\uFF09\\uFF1A</div><code>${escapeHtml(manual)}</code></div>` : `<div class=\"dsh-upd-manual\"><div class=\"dsh-upd-changelog-neutral\">\\u5F53\\u524D\\u6CA1\\u6709\\u53EF\\u7528\\u7684\\u624B\\u5DE5\\u547D\\u4EE4\\uFF08\\u8BA4\\u4E0D\\u51FA\\u4F7F\\u7528\\u8303\\u56F4\\u6216\\u5C5E\\u6E90\\u7801\\u5B89\\u88C5\\u65F6\\u4E0D\\u7ED9\\uFF09\\u3002</div></div>`\n )\n );\n return parts.join(\"\\n\");\n}\nfunction renderUpdatePanelHTML(input) {\n const view = panelViewModel(input);\n const kernel = renderUpdatePanelKernel(input, view);\n const d5 = normalizePanelTheme(input.theme) === \"d5-paper\";\n const attr = d5 ? ' data-theme=\"d5-paper\"' : \"\";\n const sealAttr = ` data-seal=\"${escapeHtml(view.seal.text)}\" data-seal-tone=\"${view.seal.tone}\"`;\n const body = input.mode === \"dialog\" ? `<div class=\"dsh-upd-overlay\" data-mode=\"dialog\"><div class=\"dsh-upd\" data-mode=\"dialog\" data-plugin=\"${escapeHtml(input.pluginId)}\"${sealAttr}${attr}>\n${kernel}\n</div></div>` : `<div class=\"dsh-upd\" data-mode=\"embedded\" data-plugin=\"${escapeHtml(input.pluginId)}\"${sealAttr}${attr}>\n${kernel}\n</div>`;\n const css = d5 ? `${UPDATE_PANEL_CSS}\n${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS;\n return `<style>${css}</style>\n${body}`;\n}\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction asSnapshot(value) {\n if (!isObject(value)) return null;\n const s = value;\n if (typeof s[\"runningVersion\"] !== \"string\") return null;\n if (typeof s[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction asQueue(value) {\n if (!isObject(value) || typeof value[\"busy\"] !== \"boolean\") return null;\n return value;\n}\nfunction randomRequestId() {\n try {\n const rand = Math.floor(Math.random() * 16777215).toString(36);\n return `r${Date.now().toString(36)}${rand}`.slice(0, 64);\n } catch {\n return `r${Date.now()}`;\n }\n}\nfunction getTimer() {\n const g = globalThis;\n const set = g[\"setInterval\"];\n const clear = g[\"clearInterval\"];\n if (typeof set === \"function\" && typeof clear === \"function\") {\n return {\n set: (fn, ms) => set(fn, ms),\n clear: (h) => clear(h)\n };\n }\n const setT = g[\"setTimeout\"];\n const clearT = g[\"clearTimeout\"];\n return {\n set: (fn, ms) => setT(fn, ms),\n clear: (h) => clearT(h)\n };\n}\nasync function defaultCopyText(text) {\n try {\n const holder = globalThis[\"navigator\"];\n const clip = holder?.clipboard;\n const write = clip?.writeText;\n if (clip && typeof write === \"function\") {\n await write.call(clip, text);\n return;\n }\n } catch {\n }\n}\nfunction mountUpdatePanel(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = options.pluginId;\n if (typeof pluginId !== \"string\" || !pluginId) {\n throw new Error(`[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 ${JSON.stringify(pluginId)}\\uFF09`);\n }\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u9762\\u677F\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const prefix = options.prefix === void 0 ? \"wf\" : options.prefix;\n const phoneNames = buildPhoneNames(prefix);\n const changelogPhone = buildChangelogPhoneName(prefix);\n const autoChangelogEnabled = typeof options.changelogMarkdown !== \"string\" && options.autoChangelog !== false;\n let manualChangelogOverride = typeof options.changelogMarkdown === \"string\";\n const changelogCache = /* @__PURE__ */ new Map();\n const changelogInflight = /* @__PURE__ */ new Set();\n const changelogFailedAt = /* @__PURE__ */ new Map();\n let autoSeq = 0;\n const pollMs = options.pollMs === void 0 ? DEFAULT_PANEL_POLL_MS : options.pollMs;\n if (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS) {\n throw new Error(`[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 ${JSON.stringify(options.pollMs)}\\uFF09`);\n }\n let mode = options.mode ?? \"embedded\";\n if (mode !== \"embedded\" && mode !== \"dialog\") {\n throw new Error(`[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 ${JSON.stringify(options.mode)}\\uFF09`);\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(options.theme)}\\uFF09`);\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n let showOthers = options.showOthers === true;\n const call = options.call;\n const onRestartRequested = options.onRestartRequested;\n const onCloseRequested = typeof options.onCloseRequested === \"function\" ? options.onCloseRequested : null;\n let lastHTML = \"\";\n const copyText = options.copyText ?? defaultCopyText;\n const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);\n const hostKind = typeof options.hostKind === \"string\" && options.hostKind ? options.hostKind : null;\n const profileNameOption = typeof options.profileName === \"string\" && options.profileName ? options.profileName : null;\n const diagCopyFormat = options.diagCopyFormat === \"line\" ? \"line\" : \"block\";\n let changelogMarkdown = typeof options.changelogMarkdown === \"string\" ? options.changelogMarkdown : null;\n let snapshot = null;\n let manual = null;\n let queue = null;\n let receipt = null;\n let requestId = null;\n let lastError = null;\n let lastErrorKind = null;\n let lastDiag = null;\n let copyNotice = null;\n let noticeExpiresAt = 0;\n let busyAct = null;\n let mounted = true;\n let envProfileName = null;\n let envHostKind = null;\n function sayCopy(text) {\n copyNotice = text;\n try {\n noticeExpiresAt = Date.now() + 5e3;\n } catch {\n noticeExpiresAt = 0;\n }\n }\n function queueArgs() {\n return { includeQueue: true, includeEnv: true, showOthers, ...requestId ? { requestId } : {} };\n }\n function render() {\n if (!mounted) return;\n if (!copyNotice) noticeExpiresAt = 0;\n else if (noticeExpiresAt && Date.now() > noticeExpiresAt) {\n copyNotice = null;\n noticeExpiresAt = 0;\n }\n const latest = snapshot?.latestVersion ?? null;\n const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);\n const nextHTML = renderUpdatePanelHTML({\n snapshot,\n manual,\n queue,\n busyAct,\n skippedLatest,\n lastError,\n errorKind: lastErrorKind,\n changelogMarkdown,\n mode,\n showOthers,\n pluginId,\n copyNotice,\n theme,\n // 使用范围与宿主种类:调用方显式传的优先,否则用宿主回的真值。\n profileName: profileNameOption ?? envProfileName,\n hostKind: hostKind ?? envHostKind\n });\n if (nextHTML !== lastHTML) {\n lastHTML = nextHTML;\n container.innerHTML = nextHTML;\n }\n }\n function applyStatusReply(reply) {\n if (!isObject(reply)) return;\n if (reply[\"ok\"] === true) {\n const s = asSnapshot(reply[\"snapshot\"]);\n if (s) snapshot = s;\n manual = typeof reply[\"manual\"] === \"string\" ? reply[\"manual\"] : null;\n if (reply[\"receipt\"] && isObject(reply[\"receipt\"]) && typeof reply[\"receipt\"][\"checkId\"] === \"string\") {\n receipt = { checkId: reply[\"receipt\"][\"checkId\"] };\n }\n const q = asQueue(reply[\"queue\"]);\n if (q) queue = q;\n const env = reply[\"env\"];\n if (isObject(env)) {\n const pn = env[\"profileName\"];\n const hk = env[\"environmentKind\"];\n if (typeof pn === \"string\" && pn) envProfileName = pn;\n if (typeof hk === \"string\" && hk) envHostKind = hk;\n }\n lastError = null;\n lastErrorKind = null;\n lastDiag = Object.prototype.hasOwnProperty.call(reply, \"diag\") ? reply[\"diag\"] : null;\n } else {\n lastError = typeof reply[\"error\"] === \"string\" ? reply[\"error\"] : \"check-failed\";\n lastErrorKind = typeof reply[\"errorKind\"] === \"string\" && reply[\"errorKind\"].trim() ? reply[\"errorKind\"].trim() : null;\n lastDiag = Object.prototype.hasOwnProperty.call(reply, \"diag\") ? reply[\"diag\"] : null;\n }\n }\n function pendingAutoChangelogVersion() {\n if (!autoChangelogEnabled || manualChangelogOverride || !mounted) return null;\n const v = snapshot?.latestVersion ?? null;\n if (typeof v !== \"string\" || !validReleaseVersion(v)) return null;\n if (!snapshot || snapshot.canInstall !== true) return null;\n try {\n if (skipStore.has(v)) return null;\n } catch {\n }\n if (changelogInflight.has(v)) return null;\n const hasCache = changelogCache.has(v);\n const failedAt = changelogFailedAt.has(v) ? changelogFailedAt.get(v) : null;\n let nowMs = 0;\n try {\n nowMs = Date.now();\n } catch {\n nowMs = 0;\n }\n if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return null;\n return v;\n }\n function maybeAutoChangelog() {\n const v = pendingAutoChangelogVersion();\n if (v === null) return;\n changelogInflight.add(v);\n const seq = autoSeq;\n void Promise.resolve().then(() => call(changelogPhone, { version: v })).then(\n (reply) => {\n changelogInflight.delete(v);\n if (!mounted || seq !== autoSeq) return;\n if (!reply || typeof reply !== \"object\" || reply[\"ok\"] !== true) {\n try {\n changelogFailedAt.set(v, Date.now());\n } catch {\n try {\n changelogFailedAt.set(v, 0);\n } catch {\n }\n }\n return;\n }\n const md = reply[\"markdown\"];\n changelogCache.set(v, typeof md === \"string\" ? md : null);\n if (typeof md === \"string\") {\n changelogMarkdown = md;\n render();\n }\n },\n () => {\n changelogInflight.delete(v);\n if (mounted && seq === autoSeq) {\n try {\n changelogFailedAt.set(v, Date.now());\n } catch {\n try {\n changelogFailedAt.set(v, 0);\n } catch {\n }\n }\n }\n }\n );\n }\n async function refresh() {\n if (!mounted) return;\n try {\n const reply = await call(phoneNames.updateStatus, queueArgs());\n if (!mounted) return;\n applyStatusReply(reply);\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n lastErrorKind = null;\n lastDiag = null;\n }\n render();\n maybeAutoChangelog();\n }\n async function act(kind, arg) {\n if (!mounted) return;\n copyNotice = null;\n switch (kind) {\n case \"check\": {\n if (busyAct) return;\n busyAct = \"check\";\n copyNotice = \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\";\n render();\n try {\n const reply = await call(phoneNames.updateCheck, queueArgs());\n if (!mounted) return;\n applyStatusReply(reply);\n changelogFailedAt.clear();\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n lastErrorKind = null;\n lastDiag = null;\n } finally {\n busyAct = null;\n if (copyNotice === \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\") copyNotice = null;\n }\n render();\n maybeAutoChangelog();\n return;\n }\n case \"install\": {\n if (busyAct) return;\n busyAct = \"install\";\n copyNotice = \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\";\n render();\n try {\n if (!receipt) {\n const checked = await call(phoneNames.updateCheck, queueArgs());\n if (!mounted) return;\n applyStatusReply(checked);\n }\n if (!mounted) return;\n if (!receipt) {\n lastError = \"check-expired\";\n lastErrorKind = \"check-expired\";\n lastDiag = null;\n render();\n return;\n }\n requestId = randomRequestId();\n const reply = await call(phoneNames.updateInstall, {\n checkId: receipt.checkId,\n requestId,\n ...queueArgs()\n });\n if (!mounted) return;\n applyStatusReply(reply);\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n lastErrorKind = null;\n lastDiag = null;\n } finally {\n busyAct = null;\n if (copyNotice === \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\") copyNotice = null;\n }\n render();\n maybeAutoChangelog();\n return;\n }\n case \"skip\": {\n const v = snapshot?.latestVersion ?? null;\n if (v && validReleaseVersion(v)) {\n try {\n skipStore.skip(v);\n } catch {\n }\n }\n render();\n return;\n }\n case \"reset-skip\": {\n try {\n skipStore.reset(arg ?? snapshot?.latestVersion ?? void 0);\n } catch {\n }\n await refresh();\n return;\n }\n case \"copy-manual\": {\n if (manual) {\n try {\n await copyText(manual);\n sayCopy(\"\\u624B\\u5DE5\\u547D\\u4EE4\\u5DF2\\u590D\\u5236\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\\u5373\\u53EF\\u3002\");\n } catch {\n sayCopy(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u547D\\u4EE4\\u3002\");\n }\n }\n render();\n return;\n }\n case \"copy-diag\": {\n const jobCode = snapshot?.job?.state === \"failed\" ? messageCodeOf(snapshot.job.message) || \"install-failed\" : \"\";\n const code = failureCodeOf(\n { error: lastError, errorKind: lastErrorKind },\n jobCode || snapshot?.blockedReason || \"check-failed\"\n );\n const detail = snapshot?.job?.message && snapshot.job.message.includes(\":\") ? snapshot.job.message.slice(snapshot.job.message.indexOf(\":\") + 1) : snapshot?.job?.message;\n const text = buildUpdateDiagCopy({\n pluginId,\n code,\n detail,\n runningVersion: snapshot?.runningVersion ?? null,\n installedVersion: snapshot?.installedVersion ?? null,\n latestVersion: snapshot?.latestVersion ?? null,\n hostKind,\n queuePosition: queue?.position ?? null,\n requestId,\n checkId: receipt?.checkId ?? null,\n route: null,\n diag: lastDiag,\n manual,\n format: diagCopyFormat\n });\n try {\n await copyText(text);\n sayCopy(\"\\u8BCA\\u65AD\\u5DF2\\u590D\\u5236\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u5373\\u53EF\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002\");\n } catch {\n sayCopy(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u4FE1\\u606F\\u3002\");\n }\n render();\n return;\n }\n case \"toggle-queue\": {\n showOthers = !showOthers;\n await refresh();\n return;\n }\n // 「重启宿主」:宿主没有重启自己的电话,所以只做入口——\n // 调用方给了 onRestartRequested 就交给它;没给就如实说“请手动重启”,不假装。\n case \"restart-hint\": {\n try {\n if (typeof onRestartRequested === \"function\") {\n await onRestartRequested();\n sayCopy(\"\\u5DF2\\u6309\\u8C03\\u7528\\u65B9\\u7684\\u91CD\\u542F\\u6D41\\u7A0B\\u5904\\u7406\\uFF1B\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n } else {\n sayCopy(\"\\u672C\\u5BBF\\u4E3B\\u672A\\u63D0\\u4F9B\\u91CD\\u542F\\u5165\\u53E3\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n } catch {\n sayCopy(\"\\u91CD\\u542F\\u5165\\u53E3\\u8C03\\u7528\\u5931\\u8D25\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n render();\n return;\n }\n case \"close-view\": {\n await requestDialogClose();\n return;\n }\n }\n }\n async function setMode(next) {\n if (next !== \"embedded\" && next !== \"dialog\") {\n throw new Error(`[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n mode = next;\n render();\n }\n async function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n theme = normalizePanelTheme(next);\n render();\n }\n async function setShowOthers(show) {\n showOthers = show === true;\n await refresh();\n }\n function onClick(ev) {\n try {\n const t = ev;\n const btn = t?.target && typeof t.target.closest === \"function\" ? t.target.closest(\"[data-action]\") : null;\n const kind = btn?.getAttribute ? btn.getAttribute(\"data-action\") : null;\n if (!kind) return;\n void act(kind);\n } catch {\n }\n }\n async function requestDialogClose() {\n if (!mounted || mode !== \"dialog\") return;\n if (onCloseRequested) {\n try {\n await onCloseRequested();\n } catch {\n }\n }\n unmount();\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || mode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n void requestDialogClose();\n } catch {\n }\n }\n function setChangelogMarkdown(markdown) {\n changelogMarkdown = typeof markdown === \"string\" ? markdown : null;\n if (typeof markdown === \"string\") manualChangelogOverride = true;\n render();\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n autoSeq++;\n changelogInflight.clear();\n try {\n timer.clear(handle);\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n render();\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n const timer = getTimer();\n const handle = timer.set(() => {\n void refresh();\n }, pollMs);\n try {\n const h = handle;\n if (h && typeof h.unref === \"function\") h.unref();\n } catch {\n }\n void refresh();\n return { refresh, act, setMode, setTheme, setShowOthers, setChangelogMarkdown, unmount };\n}\nimport { buildPhoneNames as buildPhoneNames2 } from \"./config.js\";\nimport { buildChangelogPhoneName as buildChangelogPhoneName2 } from \"./config.js\";\nconst PANEL_POLL = {\n defaultMs: DEFAULT_PANEL_POLL_MS,\n minMs: MIN_PANEL_POLL_MS\n};\nimport {\n QUEUE_INTENT_TTL_MS,\n emptyQueueState,\n normalizeQueueState,\n queuePositionOf,\n isHeadOfQueue,\n isQueueBusy,\n visibleQueueFor as visibleQueueFor2\n} from \"./queue.js\";\nimport {\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n changelogForUpdate as changelogForUpdate2,\n hasVisibleSections,\n isUnreleasedVersion,\n parseChangelog as parseChangelog2,\n renderChangelogHTML as renderChangelogHTML2,\n renderChangelogNeutral,\n renderChangelogSection,\n selectChangelogEntries\n} from \"./changelog.js\";\nexport {\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n PANEL_DIAG_MAX_CHARS,\n PANEL_POLL,\n QUEUE_INTENT_TTL_MS,\n UPDATE_PANEL_CSS,\n UPDATE_PANEL_D5_CSS,\n blockedCopy,\n buildDiagnosticText,\n buildChangelogPhoneName2 as buildPanelChangelogPhoneName,\n buildPhoneNames2 as buildPanelPhoneNames,\n buildUpdateDiagCopy,\n changelogForUpdate2 as changelogForUpdate,\n createBrowserSkipStore,\n createMemorySkipStore,\n emptyQueueState,\n failureCodeOf,\n failureCopy,\n hasVisibleSections,\n isHeadOfQueue,\n isKnownFailureCode,\n isQueueBusy,\n isUnreleasedVersion,\n mountUpdatePanel,\n normalizePanelTheme,\n normalizeQueueState,\n panelViewModel,\n parseChangelog2 as parseChangelog,\n queuePositionOf,\n readDiagTolerant,\n redactForCopy,\n renderChangelogHTML2 as renderChangelogHTML,\n renderChangelogNeutral,\n renderChangelogSection,\n renderUpdatePanelHTML,\n renderUpdatePanelKernel,\n selectChangelogEntries,\n visibleQueueFor2 as visibleQueueFor\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel-batch.ts\nimport { assertPrefix, MIN_PANEL_POLL_MS } from \"./config.js\";\nimport { validReleaseVersion } from \"./service.js\";\nimport { shouldFetchChangelog } from \"./changelog.js\";\nimport {\n isTerminalPhase,\n normalizeBatchSession\n} from \"./batch.js\";\nimport {\n UPDATE_PANEL_CSS,\n UPDATE_PANEL_D5_CSS,\n buildDiagnosticText,\n createBrowserSkipStore,\n failureCopy,\n isKnownFailureCode,\n normalizePanelTheme,\n renderUpdatePanelHTML\n} from \"./panel.js\";\nconst BATCH_PANEL_POLL = {\n defaultMs: 1500,\n minMs: MIN_PANEL_POLL_MS\n};\nfunction buildBatchPhoneNames(prefix) {\n const p = assertPrefix(prefix, \"\\u6279\\u91CF\\u7535\\u8BDD\\u524D\\u7F00 prefix\");\n return {\n status: p + \".batchStatus\",\n check: p + \".batchCheck\",\n install: p + \".batchInstall\",\n resume: p + \".batchResume\",\n cancel: p + \".batchCancel\"\n };\n}\nfunction batchLedgerCounts(rows) {\n const counts = {\n updatable: 0,\n installing: 0,\n pending: 0,\n restart: 0,\n failed: 0,\n skipped: 0,\n settled: 0\n };\n for (const row of rows) {\n const phase = asBatchPhase(row.phase);\n if (isQueueBusyRow(row)) counts.updatable += 1;\n else if (phase === \"failed\") counts.failed += 1;\n else if (phase === \"installing\") counts.installing += 1;\n else if (phase === \"ready\") counts.updatable += 1;\n else if (phase === \"pending\" || phase === \"checking\") counts.pending += 1;\n else if (phase === \"skipped\") counts.skipped += 1;\n else if (row.restartRequired === true) counts.restart += 1;\n else counts.settled += 1;\n }\n return counts;\n}\nfunction batchLedgerText(counts) {\n const parts = [];\n if (counts.updatable > 0) parts.push(counts.updatable + \" \\u5BB6\\u53EF\\u66F4\\u65B0\");\n if (counts.installing > 0) parts.push(counts.installing + \" \\u5BB6\\u5B89\\u88C5\\u4E2D\");\n if (counts.pending > 0) parts.push(counts.pending + \" \\u5BB6\\u5F85\\u67E5\");\n if (counts.restart > 0) parts.push(counts.restart + \" \\u5BB6\\u5F85\\u91CD\\u542F\");\n if (counts.failed > 0) parts.push(counts.failed + \" \\u5BB6\\u5931\\u8D25\");\n if (counts.skipped > 0) parts.push(counts.skipped + \" \\u5BB6\\u5DF2\\u8DF3\\u8FC7\");\n if (counts.settled > 0) parts.push(counts.settled + \" \\u5BB6\\u5DF2\\u6700\\u65B0\");\n return parts.length > 0 ? parts.join(\" \\xB7 \") : \"\\u8FD8\\u6CA1\\u6709\\u76EE\\u6807\";\n}\nfunction batchQueuedStatus(position) {\n if (typeof position !== \"number\" || !Number.isFinite(position) || position < 1) return \"\\u5DF2\\u6392\\u961F \\xB7 \\u7B49\\u524D\\u9762\\u5B89\\u88C5\\u5B8C\";\n return \"\\u5DF2\\u6392\\u961F \\xB7 \\u524D\\u65B9 \" + position + \" \\u4E2A\";\n}\nfunction batchRowStatus(row, skippedVersion) {\n const skipped = typeof skippedVersion === \"string\" && skippedVersion ? skippedVersion : null;\n if (skipped !== null && asBatchPhase(row.phase) === \"ready\") return \"\\u5DF2\\u8DF3\\u8FC7 \" + skipped;\n switch (asBatchPhase(row.phase)) {\n case \"pending\":\n return \"\\u7B49\\u5B83\\uFF0C\\u8F6E\\u5230\\u5C31\\u81EA\\u52A8\\u67E5\\u65B0\\u7248\";\n case \"checking\":\n return \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\uFF0C\\u7A0D\\u7B49\";\n case \"ready\":\n return row.targetVersion ? \"\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u5B89\\u88C5 \" + row.targetVersion : \"\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u5B89\\u88C5\\u65B0\\u7248\";\n case \"installing\":\n return \"\\u6B63\\u5728\\u5B89\\u88C5\\uFF0C\\u522B\\u52A8\";\n case \"current\":\n return \"\\u5DF2\\u662F\\u6700\\u65B0\\uFF0C\\u4E0D\\u7528\\u52A8\";\n case \"done\":\n return row.restartRequired === true ? \"\\u5B89\\u88C5\\u597D\\u4E86\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u624D\\u751F\\u6548\" : \"\\u5B89\\u88C5\\u597D\\u4E86\\uFF0C\\u4E0D\\u7528\\u52A8\";\n case \"failed\":\n return \"\\u5B89\\u88C5\\u6CA1\\u6210\\u529F\\uFF0C\\u70B9\\u300C\\u91CD\\u8BD5\\u300D\\u518D\\u6765\\u4E00\\u6B21\";\n case \"skipped\":\n return \"\\u8FD9\\u4E00\\u7248\\u5DF2\\u8DF3\\u8FC7\\uFF0C\\u4E0D\\u7528\\u52A8\";\n default:\n return \"\\u72B6\\u6001\\u8BA4\\u4E0D\\u51FA\\uFF0C\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u91CD\\u67E5\\u4E00\\u6B21\";\n }\n}\nfunction renderBatchPanelHTML(input) {\n const rows = Array.isArray(input.rows) ? input.rows : [];\n const theme = normalizePanelTheme(input.theme);\n const mode = input.mode === \"dialog\" ? \"dialog\" : \"embedded\";\n const titles = input.titles && typeof input.titles === \"object\" ? input.titles : {};\n const skippedVersions = input.skippedVersions && typeof input.skippedVersions === \"object\" ? input.skippedVersions : {};\n const expandedKey = typeof input.expandedKey === \"string\" && input.expandedKey ? input.expandedKey : null;\n const lastError = typeof input.lastError === \"string\" && input.lastError ? input.lastError : null;\n const loaded = input.loaded === void 0 ? rows.length > 0 : input.loaded === true;\n const counts = batchLedgerCounts(rows);\n const installing = counts.installing > 0;\n const macroBusy = input.inFlight === true || installing;\n const total = rows.length;\n const terminal = total > 0 && rows.every((row) => isTerminalPhase(asBatchPhase(row.phase)));\n const stalled = total > 0 && !terminal && !rows.some((row) => {\n const phase = asBatchPhase(row.phase);\n return phase === \"installing\" || phase === \"checking\";\n });\n const disabled = macroBusy ? \" disabled\" : \"\";\n const notice = typeof input.notice === \"string\" && input.notice ? input.notice : null;\n const noticeKey = typeof input.noticeKey === \"string\" && input.noticeKey ? input.noticeKey : null;\n const detailNoticeKey = notice !== null && noticeKey !== null && noticeKey === expandedKey ? noticeKey : null;\n const changelogs = input.changelogs && typeof input.changelogs === \"object\" ? input.changelogs : {};\n const ctx = {\n expanded: false,\n busy: installing,\n inFlight: input.inFlight === true,\n theme,\n titles,\n skipped: null,\n notice: null,\n changelogMarkdown: null\n };\n const parts = [];\n parts.push('<div class=\"dsh-upd-batch-head\">');\n parts.push('<div class=\"dsh-upd-batch-title\">\\u66F4\\u65B0\\u6863\\u6848 \\xB7 <i>\\u603B\\u8D26</i></div>');\n parts.push('<div class=\"dsh-upd-batch-macros\">');\n parts.push('<button type=\"button\" data-act=\"check\"' + disabled + ' title=\"\\u91CD\\u65B0\\u8BFB\\u53D6\\u6279\\u91CF\\u72B6\\u6001\\uFF08\\u53EA\\u8BFB\\uFF09\">\\u68C0\\u67E5\\u66F4\\u65B0</button>');\n parts.push('<button type=\"button\" data-act=\"install\" data-primary=\"1\"' + disabled + ' title=\"\\u628A\\u6709\\u65B0\\u7248\\u7684\\u51E0\\u5BB6\\u4E00\\u6B21\\u63D0\\u4EA4\\uFF1B\\u540C\\u4E00\\u4F1A\\u8BDD\\u540C\\u4E00\\u5E42\\u7B49\\u7F16\\u53F7\">\\u5168\\u90E8\\u66F4\\u65B0</button>');\n if (mode === \"dialog\") parts.push('<button type=\"button\" data-act=\"close\" title=\"\\u5173\\u95ED\\u9762\\u677F\\uFF08\\u6279\\u91CF\\u63A8\\u8FDB\\u5728\\u5BBF\\u4E3B\\u4FA7\\u7EE7\\u7EED\\u8DD1\\uFF09\">\\u5173\\u95ED</button>');\n parts.push(\"</div>\");\n parts.push(\"</div>\");\n const summary = loaded ? batchSummaryText(counts, rows, total, installing, lastError !== null) : \"\\u6B63\\u5728\\u8BFB\\u53D6\\u6279\\u91CF\\u66F4\\u65B0\\u72B6\\u6001\\u2026\";\n parts.push('<div class=\"dsh-upd-batch-sum\" role=\"status\" aria-live=\"polite\">' + escapeHtml(summary) + \"</div>\");\n parts.push(bannersHTML(rows, titles, lastError));\n parts.push('<div class=\"dsh-upd-btable\">');\n for (const row of rows) {\n const raw = skippedVersions[row.key];\n const skipped = typeof raw === \"string\" && raw ? raw : null;\n const expanded = row.key === expandedKey;\n parts.push(\n rowSetHTML(row, {\n ...ctx,\n expanded,\n skipped,\n notice: expanded && row.key === detailNoticeKey ? notice : null,\n changelogMarkdown: expanded && typeof changelogs[row.key] === \"string\" ? changelogs[row.key] : null\n })\n );\n }\n parts.push(\"</div>\");\n if (total > 0) {\n parts.push('<div class=\"dsh-upd-batch-ledger\">' + escapeHtml(batchLedgerText(counts)) + \"</div>\");\n }\n if (total > 0 && !terminal) {\n const more = [];\n if (stalled) more.push('<button type=\"button\" data-act=\"resume\">\\u63A5\\u7740\\u4E0A\\u6B21</button>');\n const confirmCancel = input.confirmCancel === true;\n more.push(\n '<button type=\"button\" data-act=\"cancel\"' + disabled + (confirmCancel ? ' data-confirm=\"1\" title=\"\\u518D\\u70B9\\u4E00\\u6B21\\u786E\\u8BA4\\u53D6\\u6D88\"' : ' title=\"\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\uFF08\\u8981\\u70B9\\u4E24\\u6B21\\u786E\\u8BA4\\uFF0C\\u9632\\u8BEF\\u89E6\\uFF09\"') + \">\" + (confirmCancel ? \"\\u786E\\u8BA4\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\" : \"\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\") + \"</button>\"\n );\n parts.push('<div class=\"dsh-upd-batch-more\">' + more.join(\"\") + \"</div>\");\n }\n if (notice !== null && detailNoticeKey === null) {\n parts.push('<div class=\"dsh-upd-batch-notice\" role=\"status\">' + escapeHtml(notice) + \"</div>\");\n }\n const kernel = parts.join(String.fromCharCode(10));\n const d5 = normalizePanelTheme(theme) === \"d5-paper\";\n const attr = d5 ? ' data-theme=\"d5-paper\"' : \"\";\n const seal = batchSealOf(counts);\n const root = '<div class=\"dsh-upd dsh-upd-batch\" data-mode=\"' + mode + '\" data-seal=\"' + escapeHtml(seal.text) + '\" data-seal-tone=\"' + seal.tone + '\"' + attr + \">\\n\" + kernel + \"\\n</div>\";\n const body = mode === \"dialog\" ? '<div class=\"dsh-upd-overlay\" data-mode=\"dialog\">' + root + \"</div>\" : root;\n const css = d5 ? [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS, UPDATE_PANEL_D5_CSS, UPDATE_BATCH_PANEL_D5_CSS].join(String.fromCharCode(10)) : [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS].join(String.fromCharCode(10));\n return \"<style>\" + css + \"</style>\\n\" + body;\n}\nfunction batchSummaryText(counts, rows, total, installing, hasError) {\n if (hasError) return \"\\u521A\\u624D\\u90A3\\u6B21\\u6CA1\\u6210\\u529F\\uFF1A\\u770B\\u4E0B\\u9762\\u7684\\u7EA2\\u6761\\uFF0C\\u7167\\u5B83\\u8BF4\\u7684\\u505A\\u4E00\\u6B21\\u3002\";\n if (total === 0) return \"\\u8FD8\\u6CA1\\u6709\\u76EE\\u6807\\uFF1A\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u770B\\u770B\\u54EA\\u51E0\\u5BB6\\u6709\\u65B0\\u7248\\u3002\";\n if (installing) {\n const canQueue = rows.filter((row) => asBatchPhase(row.phase) === \"ready\" && !isQueuedRow(row)).length;\n return canQueue > 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5 \" + counts.installing + \" \\u5BB6\\uFF1B\\u8FD8\\u6709 \" + canQueue + \" \\u5BB6\\u53EF\\u4EE5\\u70B9\\u300C\\u52A0\\u5165\\u961F\\u5217\\u300D\\u6392\\u961F\\u7B49\\u3002\" : \"\\u6B63\\u5728\\u5B89\\u88C5 \" + counts.installing + \" \\u5BB6\\uFF0C\\u5B89\\u88C5\\u5B8C\\u81EA\\u52A8\\u4E0B\\u4E00\\u5BB6\\u3002\";\n }\n if (counts.failed > 0) return counts.failed + \" \\u5BB6\\u5B89\\u88C5\\u5931\\u8D25\\uFF1B\\u7167\\u4E0B\\u9762\\u7684\\u5931\\u8D25\\u63D0\\u793A\\u9010\\u5BB6\\u91CD\\u8BD5\\u3002\";\n if (counts.updatable > 0)\n return counts.updatable + \" \\u5BB6\\u53EF\\u66F4\\u65B0\\uFF1B\\u70B9\\u300C\\u5168\\u90E8\\u66F4\\u65B0\\u300D\\u4E00\\u6B21\\u5B89\\u88C5\\u5B8C\\uFF0C\\u4E5F\\u53EF\\u4EE5\\u9010\\u5BB6\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u3002\";\n if (counts.restart > 0) return counts.restart + \" \\u5BB6\\u5DF2\\u5B89\\u88C5\\u597D\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002\";\n if (counts.pending > 0) return counts.pending + \" \\u5BB6\\u8FD8\\u6CA1\\u67E5\\u8FC7\\uFF1B\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u67E5\\u4E00\\u8F6E\\u3002\";\n return \"\\u5168\\u90E8\\u5DF2\\u6700\\u65B0\\uFF0C\\u6CA1\\u6709\\u8981\\u505A\\u7684\\u3002\";\n}\nfunction batchSealOf(counts) {\n if (counts.failed > 0) return { text: \"\\u603B\\u8D26\", tone: \"red\" };\n if (counts.installing > 0 || counts.restart > 0) return { text: \"\\u603B\\u8D26\", tone: \"yellow\" };\n if (counts.updatable > 0) return { text: \"\\u603B\\u8D26\", tone: \"green\" };\n return { text: \"\\u603B\\u8D26\", tone: \"ink\" };\n}\nfunction bannersHTML(rows, titles, lastError) {\n const parts = [];\n if (lastError) {\n const copy = failureCopy(lastError);\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"failed\" data-mini=\"\\u963B\" role=\"status\"><div><strong>\\u8FD9\\u6B21\\u6CA1\\u6210\\u529F\\uFF08' + escapeHtml(lastError) + \"\\uFF09\\uFF1A\" + escapeHtml(copy ? copy.zh : \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"\\u3002</strong></div><div>\" + escapeHtml(copy ? copy.act : \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u3002\") + \"</div></div>\"\n );\n }\n const failed = rows.filter((row) => asBatchPhase(row.phase) === \"failed\" && !isQueueBusyRow(row));\n if (failed.length > 0) {\n const lines = failed.map((row) => {\n const code = row.error || \"install-failed\";\n const copy = failureCopy(code);\n return \"<div>\" + escapeHtml(titleOf(row, titles)) + \"\\uFF1A<code>\" + escapeHtml(code) + \"</code> \" + escapeHtml(copy ? copy.zh : \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"</div>\";\n }).join(\"\");\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"failed\" data-mini=\"\\u963B\" role=\"status\"><div><strong>' + failed.length + ' \\u5BB6\\u5B89\\u88C5\\u5931\\u8D25\\u3002</strong></div><div>\\u9010\\u5BB6\\u70B9\\u884C\\u5185\\u300C\\u91CD\\u8BD5\\u300D\\u518D\\u6765\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u5931\\u8D25\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u3002</div><div class=\"dsh-upd-blist\">' + lines + \"</div></div>\"\n );\n }\n const restart = rows.filter((row) => row.restartRequired === true);\n if (restart.length > 0) {\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"restart\" role=\"status\"><div><strong>' + restart.length + ' \\u5BB6\\u5DF2\\u5B89\\u88C5\\u597D\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002</strong></div><div>\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u8BA9\\u65B0\\u7248\\u8DD1\\u8D77\\u6765\\uFF1B\\u8FD9\\u662F\\u6B63\\u5E38\\u7EC8\\u6001\\uFF0C\\u4E0D\\u662F\\u5931\\u8D25\\u3002</div><div class=\"dsh-upd-blist\">' + restart.map((row) => escapeHtml(titleOf(row, titles))).join(\"\\u3001\") + '</div><div><button type=\"button\" data-act=\"restart\" data-primary=\"1\">\\u91CD\\u542F\\u5BBF\\u4E3B</button></div></div>'\n );\n }\n return parts.join(\"\");\n}\nfunction rowSetHTML(row, ctx) {\n const phase = asBatchPhase(row.phase);\n const keyAttr = escapeHtml(row.key);\n const skipped = ctx.skipped;\n const queued = isQueuedRow(row);\n const position = queued ? queuePositionOf(row) : null;\n const current = currentVersionOf(row);\n const version = row.targetVersion ? (current === null ? \"?\" : current) + \" \\u2192 \" + row.targetVersion : current === null ? \"\" : current;\n const actions = [];\n const off = ctx.inFlight ? \" disabled\" : \"\";\n if (phase === \"installing\") {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" disabled>\\u5B89\\u88C5\\u4E2D\\u2026</button>'\n );\n } else if (queued) {\n if (cancelQueuePhoneOf(row) !== null && queuedRequestIdOf(row) !== null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-cancel-queue\" data-key=\"' + keyAttr + '\"' + off + \">\\u53D6\\u6D88\\u6392\\u961F</button>\"\n );\n }\n } else if (phase === \"ready\" && skipped === null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" data-primary=\"1\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u5B89\\u88C5\\u8FD9\\u5BB6\") + \"</button>\"\n );\n } else if (phase === \"ready\" && skipped !== null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-resume-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u6062\\u590D\\uFF08\" + escapeHtml(skipped) + \"\\uFF09</button>\"\n );\n } else if (phase === \"failed\") {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u91CD\\u8BD5\") + \"</button>\"\n );\n }\n if (row.restartRequired === true) {\n actions.push('<button type=\"button\" data-act=\"restart\" data-key=\"' + keyAttr + '\">\\u91CD\\u542F\\u5BBF\\u4E3B</button>');\n }\n actions.push(\n '<button type=\"button\" data-act=\"toggle-details\" data-key=\"' + keyAttr + '\">' + (ctx.expanded ? \"\\u6536\\u8D77\" : \"\\u8BE6\\u60C5\") + \"</button>\"\n );\n const failLine = phase === \"failed\" && !queued ? '<span class=\"dsh-upd-bfail\">\\u5931\\u8D25 <code>' + escapeHtml(row.error || \"install-failed\") + \"</code>\\uFF1A\" + escapeHtml(failureCopy(row.error || \"install-failed\")?.zh ?? \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"</span>\" : \"\";\n const main = '<button type=\"button\" class=\"dsh-upd-brow-main\" data-act=\"toggle-details\" data-key=\"' + keyAttr + '\" aria-expanded=\"' + (ctx.expanded ? \"true\" : \"false\") + '\"><span class=\"dsh-upd-updot\" data-tone=\"' + dotToneOf(row, phase, skipped, queued) + '\"></span><span class=\"dsh-upd-bname\">' + escapeHtml(titleOf(row, ctx.titles)) + \"</span>\" + (version ? '<span class=\"dsh-upd-bver\">' + escapeHtml(version) + \"</span>\" : \"\") + '<span class=\"dsh-upd-bstat\">' + escapeHtml(queued ? batchQueuedStatus(position) : batchRowStatus(row, skipped)) + \"</span>\" + failLine + \"</button>\";\n const detail = ctx.expanded ? '<div class=\"dsh-upd-bdetail\" data-key=\"' + keyAttr + '\">' + detailActionsHTML(row, ctx) + (ctx.notice !== null ? '<div class=\"dsh-upd-bdetail-notice\" role=\"status\">' + escapeHtml(ctx.notice) + \"</div>\" : \"\") + detailHTML(row, ctx) + \"</div>\" : \"\";\n return '<div class=\"dsh-upd-brow-set\" data-key=\"' + keyAttr + '\"><div class=\"dsh-upd-brow\" data-phase=\"' + escapeHtml(phase) + '\" data-key=\"' + keyAttr + '\"' + (queued ? ' data-queued=\"1\"' : \"\") + \">\" + main + '<span class=\"dsh-upd-brow-actions\">' + actions.join(\"\") + \"</span></div>\" + detail + \"</div>\";\n}\nfunction detailHTML(row, ctx) {\n const html = renderUpdatePanelHTML({\n snapshot: asSnapshotLike(row.snapshot),\n manual: typeof row.manual === \"string\" ? row.manual : null,\n changelogMarkdown: typeof ctx.changelogMarkdown === \"string\" ? ctx.changelogMarkdown : null,\n queue: asQueueLike(row.queue),\n // 跳过是面板本地记录(与单插件面板同一套语义):内核按「已跳过」画 tag 与提示行,\n // 恢复按钮由上面的动作行提供,措辞与内核提示里的「恢复」对得上。\n skippedLatest: ctx.skipped !== null,\n lastError: row.error,\n errorKind: row.error,\n mode: \"embedded\",\n showOthers: false,\n pluginId: pluginIdOf(row),\n copyNotice: null,\n theme: ctx.theme,\n profileName: typeof row.profileName === \"string\" && row.profileName ? row.profileName : null,\n actions: \"none\"\n });\n return stripActionButtons(html);\n}\nfunction detailActionsHTML(row, ctx) {\n const phase = asBatchPhase(row.phase);\n const keyAttr = escapeHtml(row.key);\n const version = latestVersionOf(row);\n const queued = isQueuedRow(row);\n const off = ctx.inFlight ? \" disabled\" : \"\";\n const buttons = [];\n if (phase === \"installing\") {\n buttons.push('<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" disabled>\\u5B89\\u88C5\\u4E2D\\u2026</button>');\n } else if (queued) {\n if (cancelQueuePhoneOf(row) !== null && queuedRequestIdOf(row) !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-cancel-queue\" data-key=\"' + keyAttr + '\"' + off + \">\\u53D6\\u6D88\\u6392\\u961F</button>\"\n );\n }\n } else if (phase === \"failed\") {\n buttons.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u91CD\\u8BD5\") + \"</button>\"\n );\n } else if (phase === \"ready\" && ctx.skipped === null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" data-primary=\"1\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u5B89\\u88C5 \" + escapeHtml(version ?? \"\\u65B0\\u7248\")) + \"</button>\"\n );\n }\n if (!queued && phase === \"ready\" && ctx.skipped === null && version !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u8DF3\\u8FC7\\u8FD9\\u4E00\\u7248</button>\"\n );\n }\n if (ctx.skipped !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-resume-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u6062\\u590D\\uFF08\" + escapeHtml(ctx.skipped) + \"\\uFF09</button>\"\n );\n }\n if (typeof row.manual === \"string\" && row.manual) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-copy-manual\" data-key=\"' + keyAttr + '\"' + off + \">\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4</button>\"\n );\n }\n buttons.push('<button type=\"button\" data-act=\"row-copy-diag\" data-key=\"' + keyAttr + '\"' + off + \">\\u590D\\u5236\\u8BCA\\u65AD</button>\");\n return '<span class=\"dsh-upd-bdetail-actions\">' + buttons.join(\"\") + \"</span>\";\n}\nfunction stripActionButtons(html) {\n return html.replace(/<button\\b[^>]*data-action=[^>]*>[\\s\\S]*?<\\/button>/g, \"\");\n}\nfunction dotToneOf(row, phase, skipped, queued) {\n if (phase === \"failed\" && !queued) return \"bad\";\n if (phase === \"installing\" || phase === \"checking\") return \"busy\";\n if (queued) return \"warn\";\n if (row.restartRequired === true) return \"warn\";\n if (phase === \"ready\") return skipped === null ? \"todo\" : \"idle\";\n if (phase === \"current\" || phase === \"done\") return \"ok\";\n return \"idle\";\n}\nconst UPDATE_BATCH_PANEL_CSS = [\n \"/* \\u6279\\u91CF\\u9762\\u677F\\uFF08#25\\uFF09\\uFF1A\\u7C7B\\u540D\\u6CBF\\u7528 dsh-upd-* \\u524D\\u7F00\\uFF1B\\u9ED8\\u8BA4\\uFF08\\u6700\\u5C0F\\uFF09\\u4E3B\\u9898\\uFF0CD5 \\u76AE\\u80A4\\u89C1 UPDATE_BATCH_PANEL_D5_CSS\\u3002 */\",\n \".dsh-upd-batch-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;flex-wrap:wrap}\",\n \".dsh-upd-batch-title{font-weight:700;font-size:15px;letter-spacing:.06em}\",\n \".dsh-upd-batch-title i{font-style:normal;color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-batch-macros{flex:none}\",\n \".dsh-upd-batch-sum{margin:4px 0 8px;font-size:13.5px}\",\n \".dsh-upd-btable{margin-top:2px}\",\n \".dsh-upd-brow-set{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-brow-set:first-child{border-top:0}\",\n \".dsh-upd-brow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 0}\",\n \".dsh-upd .dsh-upd-brow-main{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:1 1 260px;min-width:0;\",\n \"text-align:left;cursor:pointer;color:inherit;font:inherit;background:transparent;border:0;padding:2px 0;margin:0}\",\n \".dsh-upd .dsh-upd-brow-main:hover{color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-brow-actions{flex:none;margin-left:auto}\",\n \".dsh-upd-brow-actions button{font-size:12.5px}\",\n \".dsh-upd-updot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--dsh-upd-line,#9ca3af);opacity:.55}\",\n '.dsh-upd-updot[data-tone=\"todo\"]{background:var(--dsh-upd-ok-line,#059669);opacity:1}',\n '.dsh-upd-updot[data-tone=\"ok\"]{background:var(--dsh-upd-ok-line,#059669);opacity:1}',\n '.dsh-upd-updot[data-tone=\"busy\"]{background:var(--dsh-upd-busy-line,#2563eb);opacity:1}',\n '.dsh-upd-updot[data-tone=\"warn\"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',\n '.dsh-upd-updot[data-tone=\"bad\"]{background:var(--dsh-upd-bad-line,#dc2626);opacity:1}',\n \".dsh-upd-bname{font-weight:700;flex:none}\",\n \".dsh-upd-bver{font-family:Consolas,Menlo,monospace;font-size:12px;opacity:.8;flex:none}\",\n \".dsh-upd-bstat{font-size:12.5px;opacity:.9}\",\n \".dsh-upd-bfail{flex:1 1 100%;display:block;font-size:12.5px;color:var(--dsh-upd-bad-line,#dc2626)}\",\n \".dsh-upd-blist{font-size:12.5px;margin-top:2px}\",\n \".dsh-upd-batch-ledger{margin:8px 0 0;font-size:13px;opacity:.85}\",\n \".dsh-upd-batch-more{margin-top:6px}\",\n \".dsh-upd-batch-more button{font-size:12px}\",\n // 确认态:红框红字,与「关闭」等中性按钮一眼区分;disabled 照旧置灰。\n '.dsh-upd-batch-more button[data-confirm=\"1\"]{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}',\n \".dsh-upd-batch-notice{margin-top:6px;font-size:12.5px;opacity:.85}\",\n \".dsh-upd-bdetail-actions{margin:0 0 6px}\",\n \".dsh-upd .dsh-upd-bdetail-actions button{font-size:12.5px}\",\n \".dsh-upd-bdetail-notice{margin:0 0 6px;font-size:12.5px;opacity:.85}\",\n \"/* \\u8BE6\\u60C5\\u590D\\u7528\\u5355\\u63D2\\u4EF6\\u5185\\u6838\\uFF1A\\u4E0D\\u8BB8\\u5D4C\\u5957\\u6EDA\\u52A8\\u2014\\u2014\\u4E2D\\u6027\\u5316 overlay \\u7684 max-height/overflow\\uFF0C\\u81EA\\u8EAB\\u4E5F\\u4E0D\\u7ED9 overflow\\u3002 */\",\n \".dsh-upd-bdetail{margin:2px 0 10px;overflow:visible}\",\n \".dsh-upd .dsh-upd-bdetail .dsh-upd{max-width:none}\",\n \".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd{max-height:none;overflow:visible}\",\n \"@media (prefers-color-scheme: dark){.dsh-upd-batch-title i{color:var(--dsh-upd-focus,#93c5fd)}}\",\n \"@media (prefers-reduced-motion: reduce){.dsh-upd-batch *{transition:none !important;animation:none !important}}\"\n].join(String.fromCharCode(10));\nconst UPDATE_BATCH_PANEL_D5_CSS = [\n \"/* \\u6279\\u91CF\\u9762\\u677F\\u7684 D5 \\u76AE\\u80A4\\uFF1A\\u53EA\\u6362\\u989C\\u8272/\\u5B57\\u4F53/\\u95F4\\u8DDD\\uFF1B\\u6839\\u4E0A\\u7684\\u5927\\u5370\\u7AE0\\u7531 UPDATE_PANEL_D5_CSS \\u8D1F\\u8D23\\u3002 */\",\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-batch-head{padding-right:120px}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-batch-title{font-family:var(--d5-serif);font-size:21px;letter-spacing:.04em}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-batch-title i{color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-brow-main{background:transparent;border:0;color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-brow-main:hover{color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-bname{font-family:var(--d5-serif)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-bver{font-family:var(--d5-mono);color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-batch-ledger{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-batch-sum{color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-brow-set{border-top-color:var(--d5-line)}'\n].join(String.fromCharCode(10));\nfunction escapeHtml(text) {\n return String(text).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nconst BATCH_PHASES = [\n \"pending\",\n \"checking\",\n \"ready\",\n \"installing\",\n \"current\",\n \"done\",\n \"failed\",\n \"skipped\"\n];\nfunction isQueueBusyRow(row) {\n return asBatchPhase(row.phase) === \"failed\" && row.error === \"update-busy\";\n}\nfunction queuePositionOf(row) {\n const queue = asQueueLike(row.queue);\n if (!queue || queue.busy !== true) return null;\n const position = queue.position;\n return typeof position === \"number\" && Number.isFinite(position) && position > 0 ? position : null;\n}\nfunction isQueuedRow(row) {\n const phase = asBatchPhase(row.phase);\n if (phase === \"installing\") return false;\n if (isQueueBusyRow(row)) return true;\n if (isTerminalPhase(phase)) return false;\n return queuePositionOf(row) !== null;\n}\nfunction queuedRequestIdOf(row) {\n const queue = asQueueLike(row.queue);\n if (!queue || !Array.isArray(queue.waiting)) return null;\n for (const entry of queue.waiting) {\n if (entry && typeof entry.requestId === \"string\" && entry.requestId) return entry.requestId;\n }\n return null;\n}\nfunction cancelQueuePhoneOf(row) {\n const phones = isObject(row.phoneNames) ? row.phoneNames : null;\n if (!phones) return null;\n const install = phones[\"updateInstall\"];\n return typeof install === \"string\" && install ? install : null;\n}\nfunction asBatchPhase(value) {\n return typeof value === \"string\" && BATCH_PHASES.includes(value) ? value : \"pending\";\n}\nfunction asSnapshotLike(value) {\n if (!isObject(value)) return null;\n if (typeof value[\"runningVersion\"] !== \"string\") return null;\n if (typeof value[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction asQueueLike(value) {\n if (!isObject(value) || typeof value[\"busy\"] !== \"boolean\") return null;\n return value;\n}\nfunction titleOf(row, titles) {\n const override = titles[row.key];\n if (typeof override === \"string\" && override) return override;\n return row.title || row.key;\n}\nfunction pluginIdOf(row) {\n return typeof row.pluginId === \"string\" && row.pluginId ? row.pluginId : row.key;\n}\nfunction latestVersionOf(row) {\n if (typeof row.targetVersion === \"string\" && row.targetVersion) return row.targetVersion;\n const snapshot = row.snapshot;\n if (!isObject(snapshot)) return null;\n const latest = snapshot[\"latestVersion\"];\n return typeof latest === \"string\" && latest ? latest : null;\n}\nasync function defaultCopyText(text) {\n try {\n const holder = globalThis[\"navigator\"];\n const clip = holder?.clipboard;\n const write = clip?.writeText;\n if (clip && typeof write === \"function\") {\n await write.call(clip, text);\n return;\n }\n } catch {\n }\n}\nfunction currentVersionOf(row) {\n const snapshot = row.snapshot;\n if (!isObject(snapshot)) return null;\n const running = snapshot[\"runningVersion\"];\n if (typeof running === \"string\" && running) return running;\n const installed = snapshot[\"installedVersion\"];\n if (typeof installed === \"string\" && installed) return installed;\n return null;\n}\nfunction jobFailureOf(row) {\n const snapshot = isObject(row.snapshot) ? row.snapshot : null;\n if (!snapshot) return null;\n const job = isObject(snapshot[\"job\"]) ? snapshot[\"job\"] : null;\n if (!job) return null;\n const state = typeof job[\"state\"] === \"string\" ? job[\"state\"] : \"\";\n if (state !== \"failed\" && state !== \"interrupted\") return null;\n const message = typeof job[\"message\"] === \"string\" ? job[\"message\"].trim() : \"\";\n if (!message) return null;\n const at = message.indexOf(\":\");\n if (at < 0) {\n return isKnownFailureCode(message) ? { code: message, detail: null } : { code: null, detail: message };\n }\n const code = message.slice(0, at).trim();\n const detail = message.slice(at + 1).trim();\n return { code: code || null, detail: detail || null };\n}\nfunction diagTextOf(row) {\n const snapshot = isObject(row.snapshot) ? row.snapshot : null;\n const queue = isObject(row.queue) ? row.queue : null;\n const diag = isObject(row.diag) ? row.diag : null;\n const text = (value) => typeof value === \"string\" && value ? value : null;\n const job = jobFailureOf(row);\n const diagDetail = diag ? text(diag[\"detail\"]) : null;\n const detail = diagDetail !== null ? diagDetail : job !== null && job.detail !== null ? job.detail + \"\\uFF08\\u6765\\u6E90\\uFF1A\\u540E\\u53F0\\u4EFB\\u52A1\\u6536\\u5C3E\\u8BB0\\u5F55\\uFF09\" : null;\n const input = {\n pluginId: pluginIdOf(row),\n code: job !== null && job.code !== null ? job.code : row.error || \"check-failed\",\n detail,\n runningVersion: text(snapshot ? snapshot[\"runningVersion\"] : null),\n installedVersion: text(snapshot ? snapshot[\"installedVersion\"] : null),\n latestVersion: text(snapshot ? snapshot[\"latestVersion\"] : null),\n hostKind: text(row.hostKind),\n queuePosition: queue && typeof queue[\"position\"] === \"number\" ? queue[\"position\"] : null,\n manual: text(row.manual),\n diag: row.diag ?? null,\n profileName: text(row.profileName)\n };\n return buildDiagnosticText(input);\n}\nfunction isBusyReply(reply) {\n if (!isObject(reply) || reply[\"ok\"] === true) return false;\n const kind = typeof reply[\"errorKind\"] === \"string\" ? reply[\"errorKind\"] : \"\";\n const err = typeof reply[\"error\"] === \"string\" ? reply[\"error\"] : \"\";\n return kind === \"update-busy\" || err === \"update-busy\";\n}\nfunction readRows(value) {\n if (!Array.isArray(value)) return [];\n const out = [];\n const seen = /* @__PURE__ */ new Set();\n for (const item of value) {\n if (!isObject(item)) continue;\n const key = typeof item[\"key\"] === \"string\" && item[\"key\"] ? item[\"key\"] : \"\";\n if (!key || seen.has(key)) continue;\n seen.add(key);\n out.push({\n key,\n title: typeof item[\"title\"] === \"string\" && item[\"title\"] ? item[\"title\"] : key,\n phase: asBatchPhase(item[\"phase\"]),\n targetVersion: typeof item[\"targetVersion\"] === \"string\" && item[\"targetVersion\"] ? item[\"targetVersion\"] : null,\n restartRequired: item[\"restartRequired\"] === true,\n error: typeof item[\"error\"] === \"string\" && item[\"error\"] ? item[\"error\"] : null,\n snapshot: item[\"snapshot\"] === void 0 ? null : item[\"snapshot\"],\n manual: typeof item[\"manual\"] === \"string\" ? item[\"manual\"] : null,\n queue: item[\"queue\"] === void 0 ? null : item[\"queue\"],\n profileName: typeof item[\"profileName\"] === \"string\" && item[\"profileName\"] ? item[\"profileName\"] : null,\n pluginId: typeof item[\"pluginId\"] === \"string\" && item[\"pluginId\"] ? item[\"pluginId\"] : null,\n diag: item[\"diag\"] === void 0 ? null : item[\"diag\"],\n hostKind: typeof item[\"hostKind\"] === \"string\" && item[\"hostKind\"] ? item[\"hostKind\"] : null,\n phoneNames: item[\"phoneNames\"] === void 0 ? null : item[\"phoneNames\"]\n });\n }\n return out;\n}\nfunction rowsFromSession(session) {\n return session.entries.map((entry) => ({\n key: entry.key,\n title: entry.key,\n phase: entry.phase,\n targetVersion: entry.targetVersion,\n restartRequired: entry.restartRequired,\n error: entry.error,\n snapshot: null,\n manual: null,\n queue: null,\n profileName: null\n }));\n}\nfunction getTimer() {\n const g = globalThis;\n const set = g[\"setInterval\"];\n const clear = g[\"clearInterval\"];\n if (typeof set === \"function\" && typeof clear === \"function\") {\n return {\n set: (fn, ms) => set(fn, ms),\n clear: (h) => clear(h)\n };\n }\n const setT = g[\"setTimeout\"];\n const clearT = g[\"clearTimeout\"];\n return {\n set: (fn, ms) => setT(fn, ms),\n clear: (h) => clearT(h)\n };\n}\nfunction mountUpdateBatchPanel(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u6279\\u91CF\\u7535\\u8BDD\\u524D\\u7F00 prefix \\u5FC5\\u586B\");\n }\n const phones = buildBatchPhoneNames(options.prefix);\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u9762\\u677F\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const pollMs = options.pollMs === void 0 ? BATCH_PANEL_POLL.defaultMs : options.pollMs;\n if (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < BATCH_PANEL_POLL.minMs) {\n throw new Error(\n \"[dsh-plugin-update] \\u6279\\u91CF\\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.pollMs) + \"\\uFF09\"\n );\n }\n let mode = options.mode === void 0 ? \"embedded\" : options.mode;\n if (mode !== \"embedded\" && mode !== \"dialog\") {\n throw new Error(\"[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.mode) + \"\\uFF09\");\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.theme) + \"\\uFF09\");\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n const call = options.call;\n const titles = options.titles && typeof options.titles === \"object\" ? options.titles : {};\n const onRestartRequested = options.onRestartRequested;\n const copyText = options.copyText ?? defaultCopyText;\n const skipStores = /* @__PURE__ */ new Map();\n const autoChangelogEnabled = options.autoChangelog !== false;\n const rowChangelog = /* @__PURE__ */ new Map();\n const rowInflight = /* @__PURE__ */ new Set();\n const rowFailedAt = /* @__PURE__ */ new Map();\n let rowAutoSeq = 0;\n let rows = [];\n let expandedKey = null;\n let lastError = null;\n let notice = null;\n let noticeKey = null;\n let inFlight = false;\n let loaded = false;\n let refreshing = false;\n let confirmCancelArmed = false;\n let mounted = true;\n function changelogView() {\n const out = {};\n for (const row of rows) {\n const cached = rowChangelog.get(row.key);\n if (!cached || typeof cached.markdown !== \"string\") continue;\n if (latestVersionOf(row) !== cached.version) continue;\n out[row.key] = cached.markdown;\n }\n return out;\n }\n function changelogPhoneOf(row) {\n const phones2 = isObject(row.phoneNames) ? row.phoneNames : null;\n if (!phones2) return null;\n const name = phones2[\"updateChangelog\"];\n return typeof name === \"string\" && name ? name : null;\n }\n function maybeAutoRowChangelog(key) {\n if (!autoChangelogEnabled || !mounted || !key) return;\n const row = rowOfKey(key);\n if (!row || asBatchPhase(row.phase) !== \"ready\") return;\n const version = latestVersionOf(row);\n if (!version || !validReleaseVersion(version)) return;\n try {\n if (skipStoreFor(pluginIdOf(row)).has(version)) return;\n } catch {\n }\n const cached = rowChangelog.get(key);\n if (cached && cached.version === version) return;\n if (rowInflight.has(key)) return;\n try {\n const failKey = key + \"\\0\" + version;\n const hasCache = false;\n const failedAt = rowFailedAt.has(failKey) ? rowFailedAt.get(failKey) : null;\n let nowMs = 0;\n try {\n nowMs = Date.now();\n } catch {\n nowMs = 0;\n }\n if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return;\n } catch {\n }\n const phone2 = changelogPhoneOf(row);\n if (!phone2) return;\n rowInflight.add(key);\n const seq = rowAutoSeq;\n void Promise.resolve().then(() => call(phone2, { version })).then(\n (reply) => {\n rowInflight.delete(key);\n if (!mounted || seq !== rowAutoSeq) return;\n if (!isObject(reply) || reply[\"ok\"] !== true) {\n try {\n rowFailedAt.set(key + \"\\0\" + version, Date.now());\n } catch {\n try {\n rowFailedAt.set(key + \"\\0\" + version, 0);\n } catch {\n }\n }\n return;\n }\n const md = reply[\"markdown\"];\n rowChangelog.set(key, { version, markdown: typeof md === \"string\" ? md : null });\n if (typeof md === \"string\") render();\n },\n () => {\n rowInflight.delete(key);\n if (mounted && seq === rowAutoSeq) {\n try {\n rowFailedAt.set(key + \"\\0\" + version, Date.now());\n } catch {\n try {\n rowFailedAt.set(key + \"\\0\" + version, 0);\n } catch {\n }\n }\n }\n }\n );\n }\n let lastHTML = \"\";\n function render() {\n if (!mounted) return;\n const nextHTML = renderBatchPanelHTML({\n rows,\n theme,\n mode,\n expandedKey,\n lastError,\n notice,\n noticeKey,\n skippedVersions: skippedVersionsOf(),\n inFlight,\n loaded,\n titles,\n confirmCancel: confirmCancelArmed,\n changelogs: changelogView()\n });\n if (nextHTML !== lastHTML) {\n lastHTML = nextHTML;\n container.innerHTML = nextHTML;\n }\n }\n function rowOfKey(key) {\n return rows.find((row) => row.key === key) ?? null;\n }\n function skipStoreFor(pluginId) {\n let store = skipStores.get(pluginId);\n if (!store) {\n store = createBrowserSkipStore(pluginId);\n skipStores.set(pluginId, store);\n }\n return store;\n }\n function skippedVersionOf(row) {\n if (asBatchPhase(row.phase) !== \"ready\") return null;\n const version = latestVersionOf(row);\n if (!version) return null;\n try {\n return skipStoreFor(pluginIdOf(row)).has(version) ? version : null;\n } catch {\n return null;\n }\n }\n function skippedVersionsOf() {\n const out = {};\n for (const row of rows) out[row.key] = skippedVersionOf(row);\n return out;\n }\n function say(text, key = null) {\n notice = text;\n noticeKey = key;\n }\n function clearNotice() {\n notice = null;\n noticeKey = null;\n }\n async function copyWith(text, okNotice, key) {\n try {\n await copyText(text);\n say(okNotice, key);\n } catch {\n say(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u4FE1\\u606F\\u3002\", key);\n }\n render();\n }\n function applyReply(reply) {\n if (!isObject(reply)) {\n lastError = \"internal\";\n return;\n }\n if (reply[\"ok\"] === true) {\n const session = normalizeBatchSession(reply[\"session\"]);\n const parsed = readRows(reply[\"rows\"]);\n rows = parsed.length > 0 ? parsed : rowsFromSession(session);\n loaded = true;\n lastError = null;\n if (expandedKey !== null && !rows.some((row) => row.key === expandedKey)) expandedKey = null;\n return;\n }\n const err = typeof reply[\"error\"] === \"string\" ? reply[\"error\"].trim() : \"\";\n const kind = typeof reply[\"errorKind\"] === \"string\" ? reply[\"errorKind\"].trim() : \"\";\n lastError = kind || err || \"internal\";\n }\n async function refresh() {\n if (!mounted || refreshing) return;\n refreshing = true;\n try {\n const reply = await call(phones.status, {});\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n } finally {\n refreshing = false;\n }\n render();\n maybeAutoRowChangelog(expandedKey);\n }\n async function phone(name, args, busyRowKey = null) {\n if (!mounted || inFlight) return;\n inFlight = true;\n clearNotice();\n render();\n try {\n const reply = await call(name, args);\n if (!mounted) return;\n if (busyRowKey !== null && isBusyReply(reply)) {\n say(\"\\u524D\\u9762\\u8FD8\\u5728\\u88C5\\uFF1A\\u8FD9\\u4E00\\u5BB6\\u8FD8\\u6CA1\\u6392\\u4E0A\\uFF0C\\u7B49\\u90A3\\u5BB6\\u88C5\\u5B8C\\u518D\\u70B9\\u4E00\\u6B21\\u3002\", busyRowKey);\n return;\n }\n applyReply(reply);\n rowFailedAt.clear();\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n } finally {\n inFlight = false;\n if (mounted) render();\n }\n maybeAutoRowChangelog(expandedKey);\n }\n async function act(kind, key) {\n if (!mounted) return;\n if (kind !== \"cancel\" && confirmCancelArmed) {\n confirmCancelArmed = false;\n render();\n }\n switch (kind) {\n case \"check\":\n return phone(phones.check, {});\n case \"install\":\n return phone(phones.install, {});\n case \"row-install\": {\n if (typeof key !== \"string\" || !key) return;\n return phone(phones.install, { keys: [key] }, key);\n }\n case \"resume\": {\n await phone(phones.resume, {});\n if (!mounted) return;\n say(\"\\u5DF2\\u63A5\\u7740\\u4E0A\\u6B21\\u7684\\u4F1A\\u8BDD\\u63A8\\u8FDB\\u4E00\\u6B65\\u3002\");\n render();\n return;\n }\n // 程序调用(controller.act)即执行:调它就是明确意图,不上膛。\n // 鼠标误触的防护在 onClick 那一层(第一次点击只上膛)。\n case \"cancel\": {\n confirmCancelArmed = false;\n await phone(phones.cancel, {});\n if (!mounted) return;\n say(\"\\u5DF2\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\uFF1A\\u5269\\u4E0B\\u7684\\u4E0D\\u518D\\u63A8\\u8FDB\\uFF1B\\u8981\\u91CD\\u6765\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u3002\");\n render();\n return;\n }\n // —— 详情里的动作行(与行内同一通道:都走 row-install / row-skip 这些 data-act)——\n case \"row-skip\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n const version = row ? latestVersionOf(row) : null;\n if (!row || !version) return;\n try {\n skipStoreFor(pluginIdOf(row)).skip(version);\n } catch {\n }\n say(\"\\u5DF2\\u8DF3\\u8FC7 \" + version + \"\\uFF1A\\u8FD9\\u4E00\\u7248\\u4E0D\\u518D\\u63D0\\u9192\\uFF1B\\u70B9\\u300C\\u6062\\u590D\\u300D\\u53EF\\u64A4\\u9500\\u3002\", row.key);\n render();\n return;\n }\n case \"row-resume-skip\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n const version = latestVersionOf(row);\n try {\n skipStoreFor(pluginIdOf(row)).reset(version ?? void 0);\n } catch {\n }\n say(version ? \"\\u5DF2\\u6062\\u590D \" + version + \"\\uFF1A\\u8FD9\\u4E00\\u7248\\u4F1A\\u7167\\u5E38\\u63D0\\u9192\\u3002\" : \"\\u5DF2\\u6062\\u590D\\u8DF3\\u8FC7\\u63D0\\u9192\\u3002\", row.key);\n render();\n return;\n }\n // 「取消排队」:走**该家自己的**单插件安装电话(宿主支持 cancelQueued:只撤自己那条 waiting,\n // 不碰别家、也不碰正在装的拥有者)。批量取消会清掉整轮会话,所以这里绝不用它顶。\n case \"row-cancel-queue\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n const phoneName = cancelQueuePhoneOf(row);\n const requestId = queuedRequestIdOf(row);\n if (phoneName === null || requestId === null) {\n say(\"\\u8FD9\\u4E00\\u884C\\u6CA1\\u5E26\\u53D6\\u6D88\\u6392\\u961F\\u8981\\u7528\\u7684\\u7535\\u8BDD\\u540D\\u6216\\u7F16\\u53F7\\uFF0C\\u6682\\u4E0D\\u80FD\\u53D6\\u6D88\\uFF1A\\u7B49\\u4E0B\\u4E00\\u6B21\\u5237\\u65B0\\u518D\\u770B\\u3002\", row.key);\n render();\n return;\n }\n if (inFlight) return;\n inFlight = true;\n clearNotice();\n render();\n try {\n await call(phoneName, { cancelQueued: true, requestId });\n if (mounted) say(\"\\u5DF2\\u53D6\\u6D88\\u6392\\u961F\\uFF1A\\u8FD9\\u4E00\\u5BB6\\u4E0D\\u7B49\\u4E86\\u3002\", row.key);\n } catch {\n if (mounted) say(\"\\u53D6\\u6D88\\u6392\\u961F\\u6CA1\\u6210\\u529F\\uFF08\\u53EF\\u80FD\\u5DF2\\u7ECF\\u5F00\\u59CB\\u88C5\\u4E86\\uFF09\\uFF1A\\u770B\\u4E0B\\u9762\\u6700\\u65B0\\u72B6\\u6001\\u3002\", row.key);\n } finally {\n inFlight = false;\n }\n await refresh();\n return;\n }\n case \"row-copy-manual\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n const manual = row && typeof row.manual === \"string\" ? row.manual : \"\";\n if (!row || !manual) return;\n await copyWith(manual, \"\\u624B\\u5DE5\\u547D\\u4EE4\\u5DF2\\u590D\\u5236\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\\u5373\\u53EF\\u3002\", row.key);\n return;\n }\n case \"row-copy-diag\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n await copyWith(diagTextOf(row), \"\\u8BCA\\u65AD\\u5DF2\\u590D\\u5236\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u5373\\u53EF\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002\", row.key);\n return;\n }\n case \"toggle-details\": {\n if (typeof key !== \"string\" || !key) return;\n expandedKey = expandedKey === key ? null : key;\n render();\n maybeAutoRowChangelog(expandedKey);\n return;\n }\n // 「重启宿主」:宿主没有重启自己的电话,只做入口——调用方给了 onRestartRequested 就交给它,\n // 没给就如实说「请手动重启」,不假装能重启(与单插件面板逐字同一口径)。\n case \"restart\": {\n try {\n if (typeof onRestartRequested === \"function\") {\n await onRestartRequested();\n say(\"\\u5DF2\\u6309\\u8C03\\u7528\\u65B9\\u7684\\u91CD\\u542F\\u6D41\\u7A0B\\u5904\\u7406\\uFF1B\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n } else {\n say(\"\\u672C\\u5BBF\\u4E3B\\u672A\\u63D0\\u4F9B\\u91CD\\u542F\\u5165\\u53E3\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n } catch {\n say(\"\\u91CD\\u542F\\u5165\\u53E3\\u8C03\\u7528\\u5931\\u8D25\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n render();\n return;\n }\n case \"close\": {\n if (mode === \"dialog\") unmount();\n return;\n }\n default:\n return;\n }\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || mode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n unmount();\n } catch {\n }\n }\n function setTheme(next) {\n if (next !== \"default\" && next !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 d5-paper\\uFF08\\u6536\\u5230 \" + JSON.stringify(next) + \"\\uFF09\");\n }\n theme = next;\n render();\n }\n function setMode(next) {\n if (next !== \"embedded\" && next !== \"dialog\") {\n throw new Error(\"[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 \" + JSON.stringify(next) + \"\\uFF09\");\n }\n mode = next;\n render();\n }\n function onClick(ev) {\n try {\n const t = ev;\n const btn = t?.target && typeof t.target.closest === \"function\" ? t.target.closest(\"[data-act]\") : null;\n const kind = btn?.getAttribute ? btn.getAttribute(\"data-act\") : null;\n if (!kind) return;\n if (kind === \"cancel\" && !confirmCancelArmed) {\n confirmCancelArmed = true;\n say(\"\\u518D\\u70B9\\u4E00\\u6B21\\u300C\\u786E\\u8BA4\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\u300D\\u624D\\u771F\\u7684\\u53D6\\u6D88\\uFF1B\\u70B9\\u522B\\u7684\\u6309\\u94AE\\u53EF\\u64A4\\u9500\\u8FD9\\u6B21\\u786E\\u8BA4\\u3002\");\n render();\n return;\n }\n if (kind === \"cancel\") confirmCancelArmed = false;\n const key = btn?.getAttribute ? btn.getAttribute(\"data-key\") : null;\n void act(kind, key === null ? void 0 : key);\n } catch {\n }\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n rowAutoSeq++;\n rowInflight.clear();\n try {\n timer.clear(handle);\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n render();\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n const timer = getTimer();\n const handle = timer.set(() => {\n void refresh();\n }, pollMs);\n try {\n const h = handle;\n if (h && typeof h.unref === \"function\") h.unref();\n } catch {\n }\n void refresh();\n return { refresh, act, setTheme, setMode, unmount };\n}\nexport {\n BATCH_PANEL_POLL,\n UPDATE_BATCH_PANEL_CSS,\n UPDATE_BATCH_PANEL_D5_CSS,\n batchLedgerCounts,\n batchLedgerText,\n batchQueuedStatus,\n batchRowStatus,\n buildBatchPhoneNames,\n mountUpdateBatchPanel,\n renderBatchPanelHTML\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/entry.ts\nimport { DEFAULT_PREFIX, MIN_PANEL_POLL_MS, buildPhoneNames } from \"./config.js\";\nimport {\n failureCodeOf,\n mountUpdatePanel,\n normalizePanelTheme\n} from \"./panel.js\";\nconst LABEL_IDLE = \"\\u68C0\\u67E5\\u66F4\\u65B0\";\nconst LABEL_FAILED = \"\\u66F4\\u65B0\\u5931\\u8D25\\uFF0C\\u70B9\\u6B64\\u67E5\\u770B\";\nconst LABEL_BUSY = \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\";\nconst LABEL_RESTART = \"\\u5F85\\u91CD\\u542F\";\nfunction hasUpdateOf(snapshot) {\n if (!snapshot) return false;\n const latest = snapshot.latestVersion;\n if (typeof latest !== \"string\" || !latest.trim()) return false;\n return latest.trim() !== snapshot.runningVersion;\n}\nfunction entryStateKind(state) {\n const snapshot = state?.snapshot ?? null;\n const job = snapshot?.job ?? null;\n if (job && (job.state === \"installing\" || job.state === \"verifying\")) return \"busy\";\n if (snapshot && (snapshot.blockedReason === \"pending-restart\" || job?.state === \"restart-required\")) {\n return \"restart\";\n }\n if (state && state.error || job && (job.state === \"failed\" || job.state === \"interrupted\")) {\n return \"failed\";\n }\n if (hasUpdateOf(snapshot)) return \"update\";\n return \"idle\";\n}\nfunction entryLabelFor(state) {\n switch (entryStateKind(state)) {\n case \"busy\":\n return LABEL_BUSY;\n case \"restart\":\n return LABEL_RESTART;\n case \"failed\":\n return LABEL_FAILED;\n case \"update\": {\n const latest = state?.snapshot?.latestVersion;\n return typeof latest === \"string\" && latest.trim() ? `\\u6709\\u65B0\\u7248 ${latest.trim()}` : LABEL_IDLE;\n }\n default:\n return LABEL_IDLE;\n }\n}\nconst UPDATE_ENTRY_CSS = [\n '.dsh-upd-entry{display:inline-flex;align-items:center;gap:8px;font:13px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937)}',\n \".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;\",\n \"background:var(--dsh-upd-btn,#f9fafb);color:inherit;padding:4px 12px;cursor:pointer}\",\n \".dsh-upd-entry-btn:hover{border-color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}\",\n \".dsh-upd-entry-btn:active:not(:disabled){transform:translateY(1px)}\",\n \".dsh-upd-entry-btn:disabled,.dsh-upd-entry-dot:disabled{opacity:.55;cursor:wait}\",\n '.dsh-upd-entry-btn[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:10px;height:10px;margin-left:7px;vertical-align:-1px;',\n \"border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-entry-spin .8s linear infinite}\",\n \"@keyframes dsh-upd-entry-spin{to{transform:rotate(360deg)}}\",\n '@media (prefers-reduced-motion: reduce){.dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:none}.dsh-upd-entry-btn[aria-busy=\"true\"]::after{animation:none}}',\n \".dsh-upd-entry-btn:focus-visible,.dsh-upd-entry-dot:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}\",\n '.dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-ok-line,#059669);color:var(--dsh-upd-ok-line,#059669)}',\n '.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-warn-line,#d97706);color:var(--dsh-upd-warn-line,#d97706)}',\n '.dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}',\n \".dsh-upd-entry-dot{width:10px;height:10px;padding:0;border:0;border-radius:50%;background:var(--dsh-upd-line,#9ca3af);cursor:pointer}\",\n '.dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-dot{background:var(--dsh-upd-ok-line,#059669)}',\n '.dsh-upd-entry[data-state=\"busy\"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-dot{background:var(--dsh-upd-warn-line,#d97706)}',\n '.dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}',\n // 小字自带底(深色宿主 + 浅色变量时也读得出;浅底宿主上只是多一圈细线,不抢戏)。\n \".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;padding:1px 8px}\",\n '.dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}',\n '.dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;',\n 'font-family:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",serif;color:var(--d5-ink)}',\n // 按钮脸自己不透明(深色宿主 + 浅色系统变量时也读得出;hover 红在深浅底上都可见)。\n '.dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}',\n '.dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}',\n '@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}',\n \"@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}\",\n \".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}\"\n].join(\"\\n\");\nconst ENTRY_ATTR = \"data-dsh-upd-entry\";\nconst ENTRY_SELECTOR = \"[data-dsh-upd-entry]\";\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction asSnapshot(value) {\n if (!isObject(value)) return null;\n if (typeof value[\"runningVersion\"] !== \"string\") return null;\n if (typeof value[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction escapeHtml(value) {\n return String(value).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction mountUpdateEntry(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = options.pluginId;\n if (typeof pluginId !== \"string\" || !pluginId) {\n throw new Error(`[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 ${JSON.stringify(pluginId)}\\uFF09`);\n }\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u5165\\u53E3\\u4EF6\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const rawPrefix = options.prefix;\n const prefix = rawPrefix === void 0 ? DEFAULT_PREFIX : rawPrefix;\n const phoneNames = buildPhoneNames(prefix);\n const variant = options.variant ?? \"button\";\n if (variant !== \"button\" && variant !== \"badge\" && variant !== \"inline\") {\n throw new Error(`[dsh-plugin-update] \\u5165\\u53E3\\u4EF6\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 button / badge / inline\\uFF08\\u6536\\u5230 ${JSON.stringify(options.variant)}\\uFF09`);\n }\n const autoCheck = options.autoCheck ?? \"mount\";\n if (autoCheck !== \"mount\" && autoCheck !== \"never\") {\n throw new Error(`[dsh-plugin-update] \\u81EA\\u52A8\\u68C0\\u67E5\\u65F6\\u673A\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 mount \\u6216 never\\uFF08\\u6536\\u5230 ${JSON.stringify(options.autoCheck)}\\uFF09`);\n }\n const openOn = options.openOn ?? \"has-update\";\n if (openOn !== \"has-update\" && openOn !== \"always\" && openOn !== \"manual\" && openOn !== \"direct\") {\n throw new Error(`[dsh-plugin-update] \\u70B9\\u51FB\\u53BB\\u5411\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 has-update / always / manual / direct\\uFF08\\u6536\\u5230 ${JSON.stringify(options.openOn)}\\uFF09`);\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(options.theme)}\\uFF09`);\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n const pollMs = options.pollMs;\n if (pollMs !== void 0 && (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS)) {\n throw new Error(`[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 ${JSON.stringify(options.pollMs)}\\uFF09`);\n }\n const labelOverride = typeof options.label === \"string\" && options.label ? options.label : null;\n const profileName = typeof options.profileName === \"string\" && options.profileName ? options.profileName : null;\n const onActivate = typeof options.onActivate === \"function\" ? options.onActivate : null;\n const call = options.call;\n let snapshot = null;\n let error = null;\n let note = null;\n let activating = false;\n let mounted = true;\n let panel = null;\n let panelMode = null;\n const panelHost = {\n get innerHTML() {\n return container.innerHTML;\n },\n set innerHTML(value) {\n container.innerHTML = value;\n },\n addEventListener(type, listener) {\n container.addEventListener?.(type, listener);\n },\n removeEventListener(type, listener) {\n container.removeEventListener?.(type, listener);\n }\n };\n async function panelCall(name, args) {\n const reply = await call(name, args);\n return isObject(reply) ? reply : {};\n }\n function stateOf() {\n return { snapshot, error };\n }\n function currentLabel() {\n return labelOverride ?? entryLabelFor(stateOf());\n }\n function hasUpdate() {\n return hasUpdateOf(snapshot);\n }\n function phoneArgs() {\n return {};\n }\n function entryHTML() {\n const kind = entryStateKind(stateOf());\n const text = activating ? \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\" : currentLabel();\n const busyAttr = activating ? ' disabled aria-busy=\"true\"' : \"\";\n const themeAttr = theme === \"d5-paper\" ? ' data-theme=\"d5-paper\"' : \"\";\n const control = variant === \"badge\" ? `<button type=\"button\" class=\"dsh-upd-entry-dot\" ${ENTRY_ATTR}=\"activate\" title=\"${escapeHtml(text)}\" aria-label=\"${escapeHtml(text)}\"${busyAttr}></button>` : `<button type=\"button\" class=\"dsh-upd-entry-btn\" ${ENTRY_ATTR}=\"activate\"${busyAttr}>${escapeHtml(text)}</button>`;\n const noteHTML = note ? `<span class=\"dsh-upd-entry-note\" data-dsh-upd-note=\"1\">${escapeHtml(note)}</span>` : \"\";\n return `<style>${UPDATE_ENTRY_CSS}</style>\n<span class=\"dsh-upd-entry\" data-variant=\"${variant}\" data-state=\"${kind}\"${themeAttr}>${control}${noteHTML}</span>`;\n }\n function render() {\n if (!mounted) return;\n if (panelMode !== null) return;\n container.innerHTML = entryHTML();\n }\n function applyReply(reply) {\n if (!isObject(reply) || reply[\"ok\"] !== true) {\n error = failureCodeOf(isObject(reply) ? reply : null, \"check-failed\");\n return;\n }\n const next = asSnapshot(reply[\"snapshot\"]);\n if (next) snapshot = next;\n error = null;\n }\n async function refresh() {\n if (!mounted) return;\n try {\n const reply = await call(phoneNames.updateStatus, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n render();\n }\n async function checkNow() {\n try {\n const reply = await call(phoneNames.updateCheck, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n }\n function mountPanel(mode) {\n if (!mounted || panelMode !== null) return;\n panel = mountUpdatePanel(panelHost, {\n pluginId,\n prefix,\n mode,\n theme,\n pollMs,\n profileName,\n autoChangelog: options.autoChangelog,\n changelogMarkdown: options.changelogMarkdown ?? null,\n // 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。\n onCloseRequested: () => close(),\n call: panelCall\n });\n panelMode = mode;\n }\n function openDialog() {\n mountPanel(\"dialog\");\n }\n function open() {\n if (!mounted || panelMode !== null) return;\n if (variant === \"inline\") return;\n openDialog();\n }\n function close() {\n if (!mounted || panelMode !== \"dialog\" || !panel) return;\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened.unmount();\n } catch {\n }\n render();\n void refresh();\n }\n async function activate() {\n if (!mounted || panelMode !== null || activating) return;\n if (openOn === \"direct\" && variant === \"button\") {\n openDialog();\n return;\n }\n activating = true;\n note = null;\n render();\n try {\n await checkNow();\n } finally {\n activating = false;\n }\n if (!mounted) return;\n if (variant === \"badge\" || openOn === \"manual\") {\n if (onActivate) onActivate({ hasUpdate: hasUpdate(), latestVersion: snapshot?.latestVersion ?? null });\n else openDialog();\n render();\n return;\n }\n if (openOn === \"always\" || hasUpdate() || !snapshot || error) {\n openDialog();\n return;\n }\n note = `\\u5DF2\\u662F\\u6700\\u65B0 ${snapshot.runningVersion}`;\n render();\n }\n function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n theme = normalizePanelTheme(next);\n if (panel) void panel.setTheme(theme);\n else render();\n }\n function onClick(ev) {\n if (!mounted) return;\n try {\n const target = ev?.target;\n const closest = target && typeof target.closest === \"function\" ? target.closest.bind(target) : null;\n if (!closest) return;\n if (closest(ENTRY_SELECTOR)) {\n void activate();\n return;\n }\n } catch {\n }\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || panelMode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n close();\n } catch {\n }\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened?.unmount();\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n if (variant === \"inline\") mountPanel(\"embedded\");\n else render();\n if (autoCheck === \"mount\") void refresh();\n return { refresh, open, close, label: currentLabel, setTheme, unmount };\n}\nexport {\n UPDATE_ENTRY_CSS,\n entryLabelFor,\n entryStateKind,\n mountUpdateEntry\n};\n","/** dsh-life-pack 安装口径(P10 脚手架,P1 #2 定案单命令双包)。\n *\n * 真相(#2 真机结论):dsh plugin 是 pnpm 薄转发,add 单品等价 pnpm add,\n * 传递依赖落盘是;激活否:reconcile 只扫直接 dependencies,传递的 manager\n * 不进 bundles;pnpm 隔离布局下从 profile 解析不到它。\n *\n * 口径:任何单品落地时总管必须已在位导航。实现为单命令双包:\n * dsh plugin add dsh-life-pack dsh-calorie\n * 两者皆直接依赖,由 reconcile 按序激活。单品 package 内保留对总管的\n * dependencies 只作本地开发兜底,不作为单 add 即激活的依据。\n * 不许单卸总管:缺席时总管对应 tab 显示推荐安装并提示补装(见 nav.ts)。\n * 安装验收以 bundles 双含为准,另加单 add 单品后 bundles 无总管的负向断言。\n */\n\nexport const MANAGER_PACKAGE = 'dsh-life-pack' as const;\n\nexport const SINGLE_PLUGINS: readonly string[] = [\n 'dsh-memo-ilife',\n 'dsh-calorie',\n 'dsh-schedule-ilife',\n 'dsh-home-ilife',\n 'dsh-chef',\n 'dsh-bill-ilife',\n];\n\n/** reconcile 语义仿真:只扫直接 dependencies,按序进 bundles(P1 定案)。 */\nexport function reconcileBundles(directDeps: readonly string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const d of directDeps) {\n if (!seen.has(d)) {\n seen.add(d);\n out.push(d);\n }\n }\n return out;\n}\n\nexport function assertDualBundles(bundles: readonly string[], single: string): void {\n if (!bundles.includes(MANAGER_PACKAGE)) {\n throw new Error('bundles 缺总管[' + MANAGER_PACKAGE + ']:请用单命令双包补装:dsh plugin add dsh-life-pack ' + single);\n }\n if (!bundles.includes(single)) {\n throw new Error('bundles 缺单品[' + single + ']');\n }\n}\n\nexport function assertNegativeSingleOnly(bundles: readonly string[]): void {\n if (bundles.includes(MANAGER_PACKAGE)) {\n throw new Error('负向断言失败:单 add 单品时 bundles 不应含总管(传递依赖不激活,P1 #2)');\n }\n}\n","/** 七个更新目标(总管自己 + 六个单品),薄接线唯一定义地(票 1170)。\n *\n * 包名不新开第二源:六单品取自 nav.ts 的 MANAGER_TABS,总管自己取自 install.ts。\n * 七个 phonePrefix 必须互不相同,否则串台。\n */\nimport { MANAGER_PACKAGE } from './install.js';\nimport { MANAGER_TABS } from './nav.js';\n\nexport interface UpdateTarget {\n readonly key: string;\n readonly title: string;\n readonly packageName: string;\n readonly phonePrefix: string;\n}\n\nexport const MANAGER_TARGET_KEY = 'life-pack' as const;\n\n/** 批量电话前缀:宿主与面板共用,唯一定义地(面板 mount 参数与宿主登记同源)。 */\nexport const BATCH_PREFIX = 'life' as const;\n\nexport const UPDATE_TARGETS: readonly UpdateTarget[] = [\n { key: MANAGER_TARGET_KEY, title: '爱生活', packageName: MANAGER_PACKAGE, phonePrefix: 'ilife-life-pack' },\n ...MANAGER_TABS.map((tab) => ({\n key: tab.skill,\n title: tab.title,\n packageName: tab.plugin,\n phonePrefix: 'ilife-' + tab.skill,\n })),\n];\n\nexport function targetFor(packageName: string): UpdateTarget | undefined {\n return UPDATE_TARGETS.find((target) => target.packageName === packageName);\n}\n","/** 面板薄包装(票 1170,浏览器侧:无 node 内建,可进 client 束)。\n *\n * 只定死三组参数,不自建文案与样式:入口形态 button、打开策略 manual(点击交由 onOpen 打开批量弹窗)、\n * 皮肤 archive;批量面板内嵌形态(弹窗壳由本包自家 overlay 提供,关闭由自家按钮收)+ 皮肤 archive;\n * 轮询与更新日志沿上游缺省,中文标题沿目标表(不传 titles 覆盖)。\n */\nimport { mountUpdateBatchPanel } from 'dsh-plugin-update/panel-batch';\nimport type { BatchPanelController, BatchPanelContainer } from 'dsh-plugin-update/panel-batch';\nimport { mountUpdateEntry } from 'dsh-plugin-update/entry';\nimport type { UpdateEntryController, UpdateEntryOptions } from 'dsh-plugin-update/entry';\nimport { MANAGER_RPC } from './update-contract.js';\nimport { BATCH_PREFIX, MANAGER_TARGET_KEY, UPDATE_TARGETS } from './update-targets.js';\nimport type { RpcCallFace } from './dsh-ctx.js';\n\n/** 面板调宿主: upstream 面板唯一的宿主接触面(电话名 + 参数)。 */\nexport type LifePanelCall = (name: string, args: Record<string, unknown>) => Promise<unknown>;\n\n/** 总管自己那一行的电话名前缀(入口件状态源,取自目标表,不手写字面量)。 */\nfunction managerPrefix(): string {\n const row = UPDATE_TARGETS.find((target) => target.key === MANAGER_TARGET_KEY);\n return row !== undefined ? row.phonePrefix : 'ilife-life-pack';\n}\n\n/** 总管自己在批量内的插件标识(与宿主侧上游缺省 batchPrefix-key 同口径)。 */\nfunction managerPluginId(): string {\n return BATCH_PREFIX + '-' + MANAGER_TARGET_KEY;\n}\n\n/** 把 RpcCallFace 收成面板要的一参形状:上游回包原样透传(含失败回包的 errorKind 与 diag)。\n *\n * 宿主电话表把上游回包整体装进回执 value(成功失败皆如此,只有宿主级故障才走外层失败信封),\n * 故这里只拆信封、不重组回包:value 里有 ok 布尔即上游原文,原样交面板。\n */\nexport function managerCallAdapter(call: RpcCallFace | null): LifePanelCall {\n return async (name: string, args: Record<string, unknown>): Promise<unknown> => {\n if (typeof call !== 'function') return { ok: false, error: 'manager-unreachable', errorKind: 'manager-unreachable' };\n let result: unknown;\n try {\n result = await call(MANAGER_RPC.base, MANAGER_RPC.endpoint, { method: name, payload: args });\n } catch {\n return { ok: false, error: 'manager-unreachable', errorKind: 'manager-unreachable' };\n }\n if (typeof result !== 'object' || result === null || (result as { ok?: unknown }).ok !== true) {\n const problem = (result as { error?: { code?: unknown; details?: unknown } } | null)?.error;\n const code = typeof problem?.code === 'string' && problem.code.length > 0 ? problem.code : 'manager-unreachable';\n const details = (problem?.details as Record<string, unknown> | undefined) ?? {};\n const kind = typeof details.errorKind === 'string' && (details.errorKind as string).length > 0\n ? (details.errorKind as string)\n : code;\n const raw: Record<string, unknown> = { ok: false, error: code, errorKind: kind };\n if (details.diag !== undefined) raw.diag = details.diag;\n return raw;\n }\n const value = (result as { value?: unknown }).value;\n if (typeof value === 'object' && value !== null && typeof (value as { ok?: unknown }).ok === 'boolean') return value;\n return { ok: false, error: 'internal', errorKind: 'internal' };\n };\n}\n\n/** 挂入口按钮(点击打开批量弹窗,状态取总管自己一行的只读快照)。 */\nexport function mountLifeUpdateEntry(\n container: BatchPanelContainer,\n call: LifePanelCall,\n onOpen: () => void,\n overrides?: Partial<Pick<UpdateEntryOptions, 'autoCheck' | 'label'>> | undefined,\n): UpdateEntryController {\n return mountUpdateEntry(container, {\n pluginId: managerPluginId(),\n prefix: managerPrefix(),\n call,\n variant: 'button',\n openOn: 'manual',\n theme: 'archive',\n autoCheck: overrides?.autoCheck ?? 'mount',\n label: overrides?.label,\n onActivate: () => { onOpen(); },\n });\n}\n\n/** 挂批量面板(内嵌形态进自家弹窗壳,离开时 unmount 只停轮询)。 */\nexport function mountLifeBatchPanel(container: BatchPanelContainer, call: LifePanelCall): BatchPanelController {\n return mountUpdateBatchPanel(container, { prefix: BATCH_PREFIX, call, theme: 'archive', mode: 'embedded' });\n}\n","/** 共用配置面板 · 契约:行表形状、配置面回执、几条两侧共用的常量。\n *\n * 为什么住这里:六个单品插件的设置页各有一份逐字相同的那套代码(卡片骨架/行渲染/样式表/\n * 状态机/配置面三通电话约 3105 行)。本件是抽件之后的**唯一一处定义地**。\n *\n * 本件不认识任何一家能力:行表由调用方给,分支只能由行表数据与配置面回执决定,\n * 里面不出现任何一个能力的名字(结构纪律「依赖方向」)。\n *\n * 对外只经 `config-panel-api.ts` 那一道门;本件自己不出门。\n */\n\n/** 分级:常用项直接画在页面上,其余进默认收起的「高级」组。 */\nexport type ConfigTier = 'common' | 'advanced';\n\n/** 控件种类:只有这四种(目录是**字符串那一档的页面形态**——取值仍是串,只是多一个选目录的入口)。 */\nexport type ConfigControl = 'text' | 'number' | 'switch' | 'directory';\n\n/** 设置页的一行:一个可配置项一行,写清它在配置文件里的键、页面上那一行的中文标题、分级、\n * 控件种类、只读与否与人话指引。它是页面渲染的唯一依据(各家自己的那份住各自的 `settings.ts`)。 */\nexport interface ConfigItem {\n /** 配置文件里的键路径,一层嵌套用 `.` 连接,例 `backup.dir`。 */\n readonly key: string;\n /** 页面上那一行的中文标题。 */\n readonly title: string;\n readonly tier: ConfigTier;\n readonly control: ConfigControl;\n /** 一行人话:这一项管什么、留空会怎样(一到两句,不写开发期口径)。 */\n readonly hint: string;\n /** **只读行**:页面上不给改,值只经技能侧解析后显示;改它要编辑配置文件。\n * 只读行**不参与保存**(填值时跳过它,免得把显示用的绝对路径当成配置值写回去)。 */\n readonly readonly?: boolean;\n /** 只读行的**显示值来源**:回执 `resolved` 组里的哪一格。标了它 ⇒ 显示技能算好的绝对路径;\n * 不标 ⇒ 显示配置文件里那个值本身(数字类只读项走这一档)。 */\n readonly resolveFrom?: string;\n /** 可改行的**落点来源**:这一行取值空着时,页面上拿回执里的哪一格当它显示的绝对路径。\n * 取法先看回执 `resolved` 组同名格,再看回执顶层同名格(如 `dataDir`)——两种写法收成同一格。 */\n readonly prefillFrom?: string;\n}\n\n/** 配置面整面回执:形状与各技能侧 `<技能>.config.read` 的 data 逐字段同形。\n *\n * 技能侧多带的格(各家自己的那几格)不写在这里:各家在附加块里自己认(回执形状的唯一定义地\n * 在各家的 `contract.ts`)。本件只认下面这几格,因此里面不出现任何一个能力的名字。 */\nexport interface ConfigSurfaceReply {\n /** 配置文件绝对路径。 */\n readonly path: string;\n /** 数据目录绝对路径。 */\n readonly dataDir: string;\n /** 本次是不是「文件不存在、按默认值落了一份」。 */\n readonly created: boolean;\n /** 当前取值(文件里的缺项按默认值补)。 */\n readonly values: Record<string, unknown>;\n /** 一组解析后的绝对路径,给只读行显示用;**可选**:装的是旧技能时这一格缺席。 */\n readonly resolved?: Readonly<Record<string, string | undefined>>;\n /** 按行键给的告警(#915 第二步):**只有技能侧真判出「这一格用不了」时才有这一格**;\n * 缺席=这一行没问题,面板不亮任何东西。面板只原样画 `message`,不比较、不合成新句子。 */\n readonly alerts?: Readonly<Record<string, ConfigRowAlert>> | undefined;\n}\n\n/** 技能侧报出的「这一格真用不了」:一句人话 + 故障码(面板只画 `message`,`code` 给判据与回执用)。 */\nexport interface ConfigRowAlert {\n readonly code: string;\n readonly message: string;\n}\n\n/** 面板对配置面的三通电话:读整面/保存一份取值/重置为默认。三个名字各家逐字相同,收在这里一处。 */\nexport const RPC_ENDPOINT_CONFIG_GET = 'config.get' as const;\nexport const RPC_ENDPOINT_CONFIG_SAVE = 'config.save' as const;\nexport const RPC_ENDPOINT_CONFIG_RESET = 'config.reset' as const;\n\n/** 高级组标题(六家逐字相同,收在这里一处)。\n *\n * #934:原先还有一条副文案 `ADVANCED_GROUP_NOTE`(「不常改。留空=用默认值。」),\n * 维护者裁定**整条删除、删后不另找地方说**——理由:三家高级项全是只读行、不参与保存、根本没有\"留空\",\n * 而\"留空会怎样\"这条口径本就逐行住在每条的 `hint` 里(见本件 `ConfigItem.hint` 的契约)。 */\nexport const ADVANCED_GROUP_TITLE = '高级' as const;\n\n/** 请求超时毫秒:与宿主侧 SPAWN_TIMEOUT_MS 同级,界面永不无限转圈。 */\nexport const READ_TIMEOUT_MS = 20_000 as const;\n\n/** 快路径首帧有界重试表(#1142,照 #982 更新目标表同值:瞬时未就绪不再整面失败)。\n *\n * 有界、调用方负责卸载清理(见 `config-panel.ts` 的取数),不是无界轮询;仅读整面用,保存/重置永不自动重试。 */\nexport const CONFIG_READ_RETRY_MS: readonly number[] = [1000, 3000, 8000];\n\n/** 复制那一枚按钮就地变「已复制」之后,多久变回「复制」(定稿 v3 第三条:终值 900ms,不弹提示)。\n *\n * #1162 差值终结:由现状 1500 切到终值 900(真相源 #1159 参数表+原型 `},900)`);老期望变红属预期,见 #1162。 */\nexport const COPY_FEEDBACK_MS = 900 as const;\n\n/** T1 交互 tokens(#1160,唯一定义地,真相源 #1159 参数表)。\n *\n * 按压 .97、焦点隔离 2px+主题色 5px、过渡 .15s——取值逐字照原型 V1\n *(`.scratch/1155-real-panel-prototype.html`:`btn:active{transform:scale(.97)}`、\n * `btn:focus-visible{box-shadow:0 0 0 2px #0b0b0d,0 0 0 5px var(--focus)}`、\n * 主题橙 #f6ad55/蓝 #0a84ff/绿 #30d158、过渡 background/transform/box-shadow/border-color 各 .15s)。\n *\n * 颜色全走主题变量、永不写死蓝:隔离环取卡面底(深浅自适应),焦点环取\n * `--ilife-focus`(缺席时退到宿主品牌色,宿主品牌色本身跟主题走)。\n * 悬停/长按/折叠/复制变勾/提示/液态归后票,本件只定按压与焦点四格。 */\nexport const PRESS_SCALE = 0.97 as const;\nexport const FOCUS_RING_GAP_PX = 2 as const;\nexport const FOCUS_RING_WIDTH_PX = 5 as const;\nexport const INTERACTION_TRANSITION = 'background .15s ease,transform .15s ease,box-shadow .15s ease,border-color .15s ease' as const;\n\n/** T2 指针与按住 tokens(#1161,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 悬停:聚光半径 120、淡洗 13%、辉光内圈 55%+外发光 25%、上浮 1px——逐字照原型 V1\n *(`.scratch/1155-real-panel-prototype.html`:`.btn:hover{background:color-mix(…13%…);…box-shadow:0 0 0 1px …55%…,0 4px 16px …25%…;transform:translateY(-1px)}`、\n * `::before{background:radial-gradient(120px circle at var(--mx) var(--my),…22%…,transparent 65%)}`)。\n * 页签 12%/高级行 10%不归本票(页签见 #1165,高级见 #1162),这里只定按钮 13% 一档。\n *\n * 禁用:摇头 ±2px/.3s、禁止指针、不透明度 .4——照原型(`shakeV1`+`.btn:disabled{cursor:not-allowed;opacity:.4}`)。\n *\n * 长按:500ms 纯视觉环(底边 3px 主题色条,`:active` 驱动 0→100%,永不触发动作、不进状态机分支)+\n * 回弹(沿用 T1 按压释放 .15s 回弹,不另起回弹曲线)。原型环色 `#30d158` 在生产侧换成主题变量。\n *\n * 颜色全走主题变量、永不写死蓝:悬停/辉光/聚光/环一律取 `--ilife-focus` 退品牌色。\n * 触屏降级与减少动态无静态常量(媒体查询即定义,见视图 `interactionCss`)。 */\nexport const HOVER_SPOTLIGHT_RADIUS_PX = 120 as const;\nexport const HOVER_WASH_PERCENT = 13 as const;\nexport const HOVER_GLOW_EDGE_PERCENT = 55 as const;\nexport const HOVER_GLOW_SOFT_PERCENT = 25 as const;\nexport const HOVER_LIFT_PX = 1 as const;\nexport const SPOTLIGHT_PEAK_PERCENT = 22 as const;\nexport const SPOTLIGHT_FADE_MS = 180 as const;\nexport const DISABLED_OPACITY = 0.4 as const;\nexport const DISABLED_SHAKE_MS = 300 as const;\nexport const LONGPRESS_MS = 500 as const;\nexport const LONGPRESS_RING_HEIGHT_PX = 3 as const;\n\n/** T3 展开与结果 tokens(#1162,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 高级组:220ms 分级展开(`.advbody{transition:max-height .22s ease,opacity .22s ease}`,\n * 开态 cap 300px 即 `.open{max-height:300px;opacity:1}`;原生 details 开关离散、过渡跑不起来,\n * 故生产侧取等价形态 `ilifeFoldIn` 关键帧 0→300+0→1 同 .22s)+箭头旋转\n * (`.chev{transition:transform .2s}`、开态 `rotate(90deg)`)+悬停箭头右移 2px 并跟焦点色\n * (`:hover .chev{translateX(2px)}`)。页签 12%/按钮 13% 淡洗各归其票,这里只定高级头。\n *\n * 轻提示:渐显 .18s(`.toast{transition:opacity .18s ease,transform .18s ease}`,\n * 入场位移 8px 即 `translateY(8px)→0`;生产侧 `notice` 条件挂载,进场即跑 `ilifeToastIn`)。\n *\n * 复制变勾只切时长(字面「已复制」由 #920/#908 锁死不改,见 #1162 进展)。\n * 液态拉伸影子消散与时长公式归 #1165(本工作区已落地 LIQUID_*,本票不另起第二套);\n * 低端机模糊降级无静态常量(`@media (update: slow)` 即定义,见两处 CSS 函数尾部)。\n * 颜色全走主题变量、永不写死蓝。 */\nexport const ADVANCED_OPEN_MS = 220 as const;\nexport const ADVANCED_CHEV_MS = 200 as const;\nexport const ADVANCED_CHEV_SHIFT_PX = 2 as const;\nexport const ADVANCED_OPEN_MAX_PX = 300 as const;\nexport const NOTICE_FADE_MS = 180 as const;\nexport const NOTICE_RISE_PX = 8 as const;\n\n/** T6 页签与液态 tokens(#1165,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 页签悬停淡洗 12%(原型 V1 `.tab:hover{background:color-mix(…12%…)}`,按钮 13% 不串用);\n * 圆点呼吸 .8s(`dotPulseV1`:scale 1→1.5、opacity 1→.6,悬停驱动);\n * 液态彗星式:拉伸 1.28/.86(45% 处)+影子 180ms 消散(ghost opacity .18s),\n * 滑移时长 200+距离×0.35、夹 220–380ms、缓动 cubic-bezier(.3,1.1,.4,1)(原型 `liquidTo` 原式);\n * 页签过渡只 background+border-color 各 .15s(不带 transform:页签不收缩,滑移由 glider 走)。\n * 颜色全走主题变量(`--ilife-focus` 退品牌色、缺席退橙,永不写死蓝);触屏/减少动态无静态常量(媒体查询即定义,见视图 `tabInteractionCss`)。\n * 共用位第二个用法:配置面板按钮(T1/T2)+爱生活页签条(本票)共用同一契约。 */\nexport const TAB_HOVER_WASH_PERCENT = 12 as const;\nexport const TAB_DOT_PULSE_MS = 800 as const;\nexport const TAB_TRANSITION = 'background .15s ease,border-color .15s ease' as const;\nexport const LIQUID_STRETCH_X = 1.28 as const;\nexport const LIQUID_STRETCH_Y = 0.86 as const;\nexport const LIQUID_GHOST_MS = 180 as const;\nexport const LIQUID_MIN_MS = 220 as const;\nexport const LIQUID_MAX_MS = 380 as const;\nexport const LIQUID_BASE_MS = 200 as const;\nexport const LIQUID_DIST_FACTOR = 0.35 as const;\nexport const LIQUID_EASE = 'cubic-bezier(.3,1.1,.4,1)' as const;\n\n/** 宿主给「这是哪一种目录选择能力」时用的那两格原语的名字(软依赖:按名字现取,缺席即没有入口)。 */\nexport const REMOTE_DIRECTORY_PICKER = 'remote.directoryPicker' as const;\n\n/** 宿主在「这条路不给」时回的那个码(`directory-browser-state` 的 `openRowBrowser` 只照比不认名)。 */\nexport const DIRECTORY_PICKER_REFUSED = 'directory-picker/unavailable' as const;\n\n/** 一次配置电话的失败格。 */\nexport interface RpcErrorFace {\n readonly code?: string;\n readonly message?: string;\n}\n\n/** 配置电话的信封(收或拒,永不抛)。 */\nexport type RpcReply =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error?: RpcErrorFace };\n\n/** 信封守卫:拆包前校验(信封形状的定义地在这里)。 */\nexport function isRpcResult(raw: unknown): raw is RpcReply {\n if (typeof raw !== 'object' || raw === null) return false;\n return (raw as { ok?: unknown }).ok === true || (raw as { ok?: unknown }).ok === false;\n}\n\n/** 印章角色:skill=技能横条/help=HELP 旧版/plugin=插件双铆(定义只此一处)。 */\nexport type SealRole = 'skill' | 'help' | 'plugin';\n\n/** 印章档位:copper=铜/silver=银/gold=金(定义只此一处)。 */\nexport type SealTier = 'copper' | 'silver' | 'gold';\n\n/** 面板上的一枚签:角色×档位+印文+卷轴三段文案(各家交自家三枚,六家以外不许有第四枚)。 */\nexport interface SealInstance {\n readonly role: SealRole;\n readonly tier: SealTier;\n /** 印文那一行(技能/某 HELP/插件)。 */\n readonly label: string;\n /** 卷轴进展。 */\n readonly progress: string;\n /** 卷轴状态。 */\n readonly status: string;\n /** 卷轴计划。 */\n readonly plan: string;\n}\n","/** 目录浏览器的纯逻辑半(**不认识宿主**:只认两格原语)。\n *\n * 为什么在这里:六个单品插件的设置页各有一批「值是一个目录」的行,它们要的界面是同一个东西。\n * 界面的**取数**来自宿主给的两个原语(`list`/`createDirectory`),而这两个原语的形状是**调用方喂进来的**\n * ——本件不 import 宿主的任何实现,也不认识 `remote.directoryPicker` 这个名字(那两件住在各家的\n * `dsh-ctx.ts` 镜像里)。于是本件在 Windows 桌面、远程浏览器、SSH 三种部署下是同一份代码。\n *\n * 装什么:本次要浏览的那一层怎么走(进子目录/回上一级/跳面包屑)、路径怎么切出「目录部分」与\n * 「筛选词」、回执怎么认。**不装**任何 IO、不装任何持久化——`list`/`createDirectory` 由调用方执行。\n *\n * 与平台自带浏览器件的关系:`@deepseek-ai/dsh-client-ui-directory-picker-browse` 的 `DirectoryBrowser`\n * 是包内私有件(只经 `ui-workspace` 的流程空位暴露),插件侧取不到,所以这里是一份独立实现,\n * 只对齐它的**交互结果**(进/选/新建),不对齐它的 DOM。\n */\n\n/** 一层目录里的一个子目录。`hidden` 由宿主判,本件只照传。 */\nexport interface DirectoryEntry {\n readonly name: string;\n readonly path: string;\n readonly hidden: boolean;\n}\n\n/** 面包屑的一格:从文件系统根到当前层,每一格都是一个可跳转目标。 */\nexport interface DirectoryCrumb {\n readonly name: string;\n readonly path: string;\n readonly hidden: boolean;\n}\n\n/** 一次列举的回执(形状照宿主 browse 原语:`path`/`home`/`crumbs`/`entries`/`truncated`)。 */\nexport interface DirectoryListing {\n readonly path: string;\n readonly home: string;\n readonly crumbs: readonly DirectoryCrumb[];\n readonly entries: readonly DirectoryEntry[];\n readonly truncated: boolean;\n}\n\n/** 取数面:由调用方注入。本件只调这两个方法,别的什么都不调。 */\nexport interface DirectoryBrowseFace {\n list(path?: string, signal?: AbortSignal): Promise<DirectoryListing>;\n createDirectory(path: string, name: string): Promise<string>;\n}\n\n/** 浏览失败的人话(`code` 保留给用例断言,`message` 直接上图)。 */\nexport interface BrowseFailure {\n readonly code: string;\n readonly message: string;\n}\n\n/** 宿主拒了一条路的说法随宿主变化,**本件不认识**:调用方把它的回执码传进 `openRowBrowser`。\n *\n * 它为什么是唯一信号:一次组合里只服务一种能力(native 或 browse),另一条路上的动词一律被拒\n * (`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability`);\n * 而三条动词在客户端命名空间上都在(都由 `TYPERT_REMOTE.descriptors` 生成),\n * 所以「有没有 `pick`/有没有 `list`」问不出组合里服务哪种能力,只有真调一次、看它拒没拒才知道。 */\n\n/** 目录行入口的三态判定结果:**按命名空间上有什么动词**看一眼,不是问组合里服务哪种能力。 */\nexport type PickerMode = 'native' | 'browse' | 'none';\n\n/** 一个「根」是什么类型:界面只拿它写悬停说明,不拿它做任何判断。\n *\n * 这五个值由**取数方**(宿主半)归一:Windows 上是 `DriveInfo.DriveType` 映过来的;\n * 别的根(挂载点等)落 `other`。本件不认识任何宿主名词,只认识这五个值。 */\nexport type RootKind = 'fixed' | 'network' | 'removable' | 'optical' | 'other';\n\n/** 一个可跳转的根:`path` 是绝对路径(Windows 上形如 `D:\\`),`kind` 只用于显示。 */\nexport interface RootRow {\n readonly path: string;\n readonly kind: RootKind;\n}\n\n/** 平台回执的信封(各家的 `DirectoryPickerAnswer` 与此同形;本件只认这三格,不认别家的类名)。 */\nexport interface PickEnvelope {\n readonly ok: boolean;\n readonly value?: string | null;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\n/** 一次「唤起选择器」的归一结果:选中/取消/供不了,永不抛。\n *\n * `code` 只在平台给了的时候带上(就是 `error.code`):调用方据它分辨「宿主没给这条路」与\n * 「别的失败」——前者该换一条路走,后者才该报出来。那个码由调用方从自己的宿主镜像里传进来\n * (见 `openRowBrowser` 的 `refusalCode`),本件不认它的名字。 */\nexport type PickOutcome =\n | { readonly kind: 'picked'; readonly path: string }\n | { readonly kind: 'cancelled' }\n | { readonly kind: 'unavailable'; readonly code?: string; readonly message: string };\n\n/** 认一认某个值是不是可用的目录取数面(软依赖守卫用:认不出就当没有,绝不把页面带下来)。 */\nexport function isBrowseFace(raw: unknown): raw is DirectoryBrowseFace {\n if (typeof raw !== 'object' || raw === null) return false;\n const face = raw as Partial<DirectoryBrowseFace>;\n return typeof face.list === 'function' && typeof face.createDirectory === 'function';\n}\n\n/** 认一认某个值有没有 `pick`(对应宿主 composition 里的 `native` 能力)。 */\nexport function hasPickFn(raw: unknown): boolean {\n if (typeof raw !== 'object' || raw === null) return false;\n return typeof (raw as { pick?: unknown }).pick === 'function';\n}\n\n/** 这次列举失败算不算「宿主没给这条路」(调用方据此换另一条路走)。\n *\n * 为什么码不够:旧宿主被拒回固定回执码(调用方经 `refusalCode` 传进来),新宿主\n * (`dsh 0.2.0-rc.2` 实测 #990)拒 `list` 时没带那个码——`code` 落到兜底值,\n * 只有人话里那句能力说明还在。所以码命中算被拒,人话命中能力句式也算被拒;\n * 人话对不上=这一层真读不出来,不换路。\n *\n * 人话只认两句能力句式(大小写不敏感):`needs the browse/native capability`\n * 与 `cannot serve pick/list/createDirectory`(含组合服务说明变体)。\n * 本件仍不认识宿主名:这里只出现能力词,不出现任何接缝名。 */\nexport function isCapabilityRefusal(code: unknown, message: unknown, refusalCode: string): boolean {\n if (typeof code === 'string' && code !== '' && code === refusalCode) return true;\n if (typeof message !== 'string' || message.trim() === '') return false;\n return (\n /needs the (browse|native) capability/i.test(message) ||\n /cannot serve (pick|list|createDirectory)/i.test(message)\n );\n}\n\n/** 三态判定:**先看两格浏览原语**(都在=应用内浏览器这条路在),再看单独的 `pick`,都没有=`none`。\n *\n * 为什么浏览优先:客户端命名空间上三条动词一定都在(由 `TYPERT_REMOTE.descriptors` 生成,\n * 与组合里服务哪种能力无关),所以两格原语在=这台宿主**可能**给浏览;真调一次才知道给不给,\n * 不给({@link CAPABILITY_REFUSED})再由调用方换系统对话框。反过来先认 `pick` 就会在\n * Desktop 这种「只服务 browse」的宿主上每次都去唤一次系统对话框、被拒一次(#744 实测)。 */\nexport function pickerModeOf(raw: unknown): PickerMode {\n if (isBrowseFace(raw)) return 'browse';\n return hasPickFn(raw) ? 'native' : 'none';\n}\n\n/** 平台回执 → 三态(**永不抛**):成功回的是信封里的 `value` 不是路径;`ok:false` 是「供不了」不是「取消」。\n *\n * 出处:`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`(失败不抛、成功也不回裸值),\n * 第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js:99-103` 就照这个信封拆。\n * 裸串照收(老形状兜底),认不出的形状当「供不了」报出来,不当取消吞掉(#743 的教训)。 */\nexport function readPickAnswer(raw: unknown): PickOutcome {\n if (typeof raw === 'string') return raw.trim() === '' ? { kind: 'cancelled' } : { kind: 'picked', path: raw };\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as PickEnvelope;\n if (answer.ok === true) {\n const value = answer.value;\n return typeof value === 'string' && value.trim() !== '' ? { kind: 'picked', path: value } : { kind: 'cancelled' };\n }\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n return {\n kind: 'unavailable',\n ...(typeof code === 'string' && code !== '' ? { code } : {}),\n message: '打不开系统文件夹对话框' + (detail === '' ? '' : '(' + detail + ')') + ':请直接在框里填绝对路径。',\n };\n}\n\n/** 取数原语失败:带上宿主给的回执码,好让调用方分辨「没这条路」与「这一层读不出来」。\n *\n * `code` 就是 `humanize`(状态那半)读的那一格,人话直接上图。 */\nexport class BrowseAnswerError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = 'BrowseAnswerError';\n this.code = code;\n }\n}\n\n/** 宿主 browse 原语的**信封** → 值,或按信封里的 `error` 抛。\n *\n * 为什么必须走这一道:`list`/`createDirectory` 与 `pick` 一样是 Remote 动词,客户端拿到的是\n * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`)。\n * 把信封当值用,图上就会是一层「读出来了但一行都没有」的空目录(#744 实测),\n * 而不是报错——所以宁可在这里抛,也不许把它当列举结果传下去。\n * 第一方消费方同口径:`@deepseek-ai/dsh-client-ui-workspace/lib/client.js:105-113` 也是这么拆的。 */\nexport function readBrowseAnswer<T>(raw: unknown, what: string): T {\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as {\n ok?: unknown;\n value?: unknown;\n error?: { code?: string; message?: string };\n };\n if (answer.ok === true && answer.value !== undefined && answer.value !== null) return answer.value as T;\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n throw new BrowseAnswerError(\n typeof code === 'string' && code !== '' ? code : 'browse-failed',\n detail === '' ? what + '没有回执' : detail,\n );\n}\n\n/** 这次失败算不算「客户端按参数个数拦的」(与能力被拒、读不出来都不是一回事)。\n *\n * 出处:新宿主客户端按 `values.length` 对描述符的业务参数个数(`signal` 另算,\n * `values.length === 业务参数个数 + 1` 才算带了 signal),对不上就地抛\n * `expected N …argument(s)…, got M`,还没走到服务端。家目录重试只认这一句,\n * 能力句式与读失败一律不碰(它们各有各的去处)。 */\nexport function isArgCountRejection(cause: unknown): boolean {\n const message =\n cause instanceof Error ? cause.message : typeof cause === 'string' ? cause : '';\n return /expected \\d+ .*argument/i.test(message);\n}\n\n/** 宿主命名空间 → 本件的取数面:把两条原语的信封拆开,成功给值、失败按码抛。\n *\n * 命名空间上两格原语不在就回 null(调用方据此不画入口)。**透传参数个数照宿主的规矩**:\n * 描述符声明了可选 `AbortSignal`(`cancellation: {parameter:'signal'}`),而客户端按\n * `values.length === 业务参数个数 + 1` 判有没有 signal——旧宿主的家目录要零参直调,\n * 多传一个 `undefined` 会被当成 signal 去 `AbortSignal.any([…, undefined])` 而炸。\n * 本件的界面不做取消,故**从不传 signal**。\n *\n * 家目录多一档兼容:新宿主的 `list` 描述符带一个业务参数(接受 `undefined`,\n * 服务端 `undefined` 即回家),零参会被客户端按个数直接拦下。于是家目录先零参试一次,\n * 是个数问题再补一个显式 `undefined` 重试;有参调用与旧语义逐字相同,不多试。 */\nexport function browseFaceOf(raw: unknown): DirectoryBrowseFace | null {\n if (!isBrowseFace(raw)) return null;\n const namespace = raw as DirectoryBrowseFace;\n return {\n list: async (path) => {\n if (path !== undefined) {\n return readBrowseAnswer<DirectoryListing>(await namespace.list(path), '列举目录');\n }\n try {\n return readBrowseAnswer<DirectoryListing>(await namespace.list(), '列举目录');\n } catch (first) {\n if (!isArgCountRejection(first)) throw first;\n return readBrowseAnswer<DirectoryListing>(await namespace.list(undefined), '列举目录');\n }\n },\n createDirectory: async (path, name) => readBrowseAnswer<string>(await namespace.createDirectory(path, name), '新建文件夹'),\n };\n}\n\n/** 一段路径里最后那个分隔符的位置(Windows 上 `\\` 与 `/` 都算;POSIX 上 `\\` 是合法文件名字符,不算)。 */\nfunction lastSeparator(path: string): number {\n const back = path.lastIndexOf('\\\\');\n const slash = path.lastIndexOf('/');\n // 只含 `\\` 的路径按 Windows 规则解;两者都有时取更靠后的那个。\n return back > slash ? back : slash;\n}\n\n/** 上一层:`'C:\\\\a\\\\b\\\\'` → `'C:\\\\a'`;已经是根则回自身;相对名(没有任何分隔符)也回自身。\n *\n * 尾分隔符先吃掉再切——否则「回上一级」会在原地打转。**盘根单独认**:`C:\\` 掐掉尾分隔符会剩成\n * `C:`,而 `C:` 是「当前盘」不是「上一层」,回它就会多跑一趟、还可能跨到别的盘上去。\n * UNC(`\\\\server\\share`)不特殊处理:切到头就是共享名本身,浏览停在共享根。 */\nexport function parentOf(path: string): string {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '') return path;\n const sep = path.includes('\\\\') ? '\\\\' : '/';\n if (/^[A-Za-z]:$/.test(trimmed)) return trimmed + sep;\n const cut = lastSeparator(trimmed);\n if (cut === -1) return trimmed;\n if (cut === 0) return path.slice(0, 1);\n if (cut === 2 && /^[A-Za-z]:/.test(trimmed)) return trimmed.slice(0, 2) + sep;\n return trimmed.slice(0, cut);\n}\n\n/** 把「当前路径 + 一段输入」拼成一个目标路径。 */\nexport function joinPath(directory: string, name: string): string {\n const sep = directory.includes('\\\\') ? '\\\\' : '/';\n const base = directory.replace(/[\\\\/]+$/, '');\n if (base === '') return sep + name;\n return base + sep + name;\n}\n\n/** 一层列举里,哪些行可进:隐藏目录按开关过滤,其余照宿主给的顺序。 */\nexport function visibleEntries(listing: DirectoryListing, showHidden: boolean): readonly DirectoryEntry[] {\n return showHidden ? listing.entries : listing.entries.filter((entry) => !entry.hidden);\n}\n\n/** 这一层有多少个被藏起来的目录(用来决定「显示隐藏目录」那个开关要不要出现在图上)。 */\nexport function hiddenCount(listing: DirectoryListing): number {\n return listing.entries.filter((entry) => entry.hidden).length;\n}\n\n/** 拿一段文本按「最后一个分隔符」切成「目录部分 + 筛选词」。\n *\n * 没打过任何分隔符时目录部分=null(那段文字还不指任何目录,只当筛选词用)。\n * Windows 上 `/` 也当分隔符(宿主那边两种都收),POSIX 上不。 */\nexport function splitDraft(draft: string): { readonly directory: string | null; readonly filter: string } {\n const cut = lastSeparator(draft);\n if (cut === -1) return { directory: null, filter: draft };\n return { directory: draft.slice(0, cut + 1), filter: draft.slice(cut + 1) };\n}\n\n/** 按筛选词过一层目录(大小写不敏感;空筛选词=全留)。 */\nexport function filterEntries(entries: readonly DirectoryEntry[], filter: string): readonly DirectoryEntry[] {\n const needle = filter.trim().toLowerCase();\n if (needle === '') return entries;\n return entries.filter((entry) => entry.name.toLowerCase().includes(needle));\n}\n\n/** 新建文件夹的名字合不合法(单个路径段:不许分隔符、不许空、不许 `.`/`..`)。\n *\n * 与宿主 browse 后端的校验同口径——这里先拦一道,是为了在图上给得出人话,不是替代宿主校验。 */\nexport function validateFolderName(name: string): { readonly ok: true } | { readonly ok: false; readonly reason: string } {\n const trimmed = name.trim();\n if (trimmed === '') return { ok: false, reason: '文件夹名不能为空。' };\n if (/[\\\\/]/.test(trimmed)) return { ok: false, reason: '文件夹名里不能带路径分隔符。' };\n if (trimmed === '.' || trimmed === '..') return { ok: false, reason: '「.」与「..」不是文件夹名。' };\n return { ok: true };\n}\n","/** 文件浏览器的操作半:把「进哪一层、这一层显示什么、选到了什么」算成状态,**IO 全部由调用方注入**。\n *\n * 分两层的原因:本件(操作)在 Node 里就能直测——喂一个假的取数面,就能把「进目录/回上一级/\n * 跳面包屑/筛选/新建文件夹/失败出人话」全跑一遍;视图那半(`directory-browser-ui.ts`)\n * 只负责把这份状态画成 DOM。两边都不认识宿主。\n *\n * 数据来源只有两格:`list(path?)` 与 `createDirectory(path, name)`(调用方注入的 `DirectoryBrowseFace`)。\n */\n\nimport type {\n BrowseFailure,\n DirectoryBrowseFace,\n DirectoryEntry,\n DirectoryListing,\n RootRow,\n} from './directory-browser-contract.js';\nimport {\n browseFaceOf,\n filterEntries,\n isCapabilityRefusal,\n joinPath,\n parentOf,\n splitDraft,\n validateFolderName,\n visibleEntries,\n} from './directory-browser-contract.js';\n\n/** 一层列举的三态:加载中/拿到了/这一层读不出来。 */\nexport type BrowsePhase = 'idle' | 'loading' | 'ready' | 'failed';\n\n/** 图上的全部状态。视图只读它,不改它。 */\nexport interface BrowseState {\n readonly phase: BrowsePhase;\n readonly listing: DirectoryListing | null;\n /** 这一层读失败时的人话;`phase==='failed'` 时一定有。 */\n readonly failure: BrowseFailure | null;\n /** 当前选中的子目录(点行选中;选中后再点「打开」就是它)。 */\n readonly selected: string | null;\n /** 面包屑上那一层的编号,只用于画图。 */\n readonly showHidden: boolean;\n /** 路径框里的草稿(用户可能正在编辑)。 */\n readonly draft: string;\n /** 草稿指向的那一层已经列举过之后,用来按末段过滤的词。 */\n readonly filter: string;\n /** 正在建的文件夹名;null=没在建。 */\n readonly creating: string | null;\n /** 上一次操作的失败人话(新建失败等);看一眼就好,下一次操作清掉。 */\n readonly notice: string | null;\n /** 可跳转的根(Windows 上的盘符等):取数方给什么就是什么,空数组=不画那一行。 */\n readonly roots: readonly RootRow[];\n}\n\n/** 订阅者拿到的状态(不可变,每次变更是新对象)。 */\nexport type BrowseListener = (state: BrowseState) => void;\n\n/** 操作面:视图与用例都只用这些方法。 */\nexport interface BrowseController {\n getState(): BrowseState;\n subscribe(listener: BrowseListener): () => void;\n /** 开图:先试配置里那个目录(空则直接家目录),读不出来(不存在、没权限)退回宿主家目录。\n *\n * 回**第一份落定的状态**:调用方据此分辨「这条路宿主没给」与「这一层读不出来」。 */\n open(): Promise<BrowseState>;\n /** 进某一层(绝对路径,或 `list()` 的默认家目录当入参为 null)。 */\n enter(path: string | null): Promise<void>;\n /** 回上一级(当前层就是根时原地不动)。 */\n up(): Promise<void>;\n /** 采纳当前选择(选中行,没有选中就用当前层)并回调 `onPicked`。 */\n pick(): void;\n /** 取消:关图,什么都不回调。 */\n cancel(): void;\n /** 选中一个子目录(再点一次同一条=取消选中)。 */\n select(path: string): void;\n /** 切换隐藏目录。 */\n toggleHidden(): void;\n /** 改路径框草稿(不列举,只更新草稿与筛选词)。 */\n setDraft(draft: string): void;\n /** 提交路径框草稿:指到哪一层就去哪一层。 */\n commitDraft(): Promise<void>;\n /** 开「新建文件夹」;传 null 收起。 */\n setCreating(name: string | null): void;\n /** 在当前层(或选中的那一层)下建一个新文件夹,成功后进它并选中它。 */\n createFolder(name: string): Promise<void>;\n /** 关图(与 `cancel` 同义,供宿主侧统一收口)。 */\n close(): void;\n}\n\n/** 建一个浏览器控制器。取数、回调、初值全部由调用方给。 */\nexport function createBrowseController(deps: {\n readonly face: DirectoryBrowseFace;\n readonly initialPath: string;\n readonly onPicked: (path: string) => void;\n readonly onClose: () => void;\n /** 失败人话的前缀,默认「浏览失败」。 */\n readonly failureLabel?: string;\n /** 可跳转的根从哪来;不给就是没有这一行(都是空清单,一样不画)。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): BrowseController {\n const label = deps.failureLabel ?? '浏览失败';\n const listeners = new Set<BrowseListener>();\n let state: BrowseState = {\n phase: 'idle',\n listing: null,\n failure: null,\n selected: null,\n showHidden: false,\n draft: deps.initialPath,\n filter: '',\n creating: null,\n notice: null,\n roots: [],\n };\n\n /** 每次列举带一个序号:晚回来的旧回执直接丢掉,不许覆盖新一层。 */\n let generation = 0;\n\n const emit = (next: Partial<BrowseState>): void => {\n state = { ...state, ...next };\n for (const listener of [...listeners]) listener(state);\n };\n\n /** 根清单只问一次:取不到就是空(不报错、不重试——它只是图上的一行入口)。\n *\n * 「问一次」钉在这里而不是图里:视图那半每次重画都会重新求值,问在这里才谈得上一次。 */\n let rootsAsked = false;\n const loadRoots = async (): Promise<void> => {\n if (rootsAsked || deps.rootsSource === undefined) return;\n rootsAsked = true;\n try {\n const roots = await deps.rootsSource();\n if (roots.length > 0) emit({ roots });\n } catch {\n /* 取不到就当没有这一行:浏览本身照常。 */\n }\n };\n\n const humanize = (cause: unknown): BrowseFailure => {\n const raw = (typeof cause === 'object' && cause !== null ? cause : {}) as {\n code?: unknown;\n message?: unknown;\n name?: unknown;\n };\n const message = typeof raw.message === 'string' && raw.message.trim() !== '' ? raw.message.trim() : String(cause);\n const code = typeof raw.code === 'string' && raw.code !== '' ? raw.code : 'browse-failed';\n return { code, message: `${label}:${message}` };\n };\n\n const load = async (path: string | null): Promise<void> => {\n const mine = ++generation;\n emit({ phase: 'loading', failure: null, notice: null });\n try {\n const listing = path === null ? await deps.face.list() : await deps.face.list(path);\n if (mine !== generation) return;\n emit({\n phase: 'ready',\n listing,\n failure: null,\n selected: null,\n draft: listing.path,\n filter: '',\n });\n } catch (cause) {\n if (mine !== generation) return;\n emit({ phase: 'failed', listing: null, failure: humanize(cause), selected: null });\n }\n };\n\n /** 草稿指到的那一层已经列举过时,按末段筛当前这一层的行;否则不过滤。\n *\n * 两边都先掐掉尾分隔符再比:草稿那边切开后天然带尾分隔符(`C:\\a\\` + `b`),\n * 而这一层的 `path` 由宿主给、不带尾分隔符。 */\n const applyDraft = (draft: string): void => {\n const { directory, filter } = splitDraft(draft);\n const listing = state.listing;\n const sameLevel = listing !== null && directory !== null && normalize(directory) === normalize(listing.path);\n emit({ draft, filter: sameLevel ? filter : '' });\n };\n\n const normalize = (path: string): string => path.replace(/[\\\\/]+$/, '');\n\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n async open(): Promise<BrowseState> {\n // 根清单一并去问,但**不等它**:它只是图上的一行入口,落定了自会推一次重画;\n // 等它就等于让「宿主那台机器列盘符快不快」决定对话框什么时候开。\n void loadRoots();\n const wanted = deps.initialPath.trim();\n if (wanted !== '') {\n await load(wanted);\n if (state.phase === 'ready') return state;\n }\n await load(null);\n return state;\n },\n enter: (path) => load(path),\n async up() {\n const listing = state.listing;\n if (listing === null) return;\n const parent = parentOf(listing.path);\n if (parent === listing.path) return;\n await load(parent);\n },\n pick() {\n const target = state.selected ?? state.listing?.path ?? null;\n if (target === null) return;\n deps.onPicked(target);\n },\n cancel() {\n deps.onClose();\n },\n close() {\n deps.onClose();\n },\n select(path) {\n emit({ selected: state.selected === path ? null : path, notice: null });\n },\n toggleHidden() {\n emit({ showHidden: !state.showHidden });\n },\n setDraft(draft) {\n applyDraft(draft);\n },\n async commitDraft() {\n const trimmed = state.draft.trim();\n if (trimmed === '') return;\n await load(trimmed);\n },\n setCreating(name) {\n emit({ creating: name, notice: null });\n },\n async createFolder(name) {\n const checked = validateFolderName(name);\n if (!checked.ok) {\n emit({ notice: checked.reason });\n return;\n }\n const base = state.selected ?? state.listing?.path ?? null;\n if (base === null) {\n emit({ notice: '还没有打开任何目录,无法新建文件夹。' });\n return;\n }\n try {\n const created = await deps.face.createDirectory(base, name.trim());\n emit({ creating: null, notice: null });\n await load(created);\n emit({ selected: created });\n } catch (cause) {\n const failure = humanize(cause);\n emit({ notice: failure.message, creating: null });\n }\n },\n };\n}\n\n/** 图上这一层要画的行(按隐藏开关与草稿末段过滤之后的)。 */\nexport function rowsOf(state: BrowseState): readonly DirectoryEntry[] {\n if (state.listing === null) return [];\n return filterEntries(visibleEntries(state.listing, state.showHidden), state.filter);\n}\n\n/** 图上这一层能不能回上一级(根目录不能)。 */\nexport function canGoUp(state: BrowseState): boolean {\n if (state.listing === null) return false;\n return parentOf(state.listing.path) !== state.listing.path;\n}\n\n/** 「打开」这颗按钮的目标:选中行优先,没选中就是当前层。 */\nexport function targetOf(state: BrowseState): string | null {\n return state.selected ?? state.listing?.path ?? null;\n}\n\n/** 新建文件夹那一行要展示的位置(\"在 <这里> 下新建\")。 */\nexport function createBaseOf(state: BrowseState): string | null {\n return state.selected ?? state.listing?.path ?? null;\n}\n\n/** 一段路径拼成可读的一句话(给用例与调试用;图上不直接显示)。 */\nexport function describe(state: BrowseState): string {\n if (state.phase === 'failed') return `${state.failure?.code ?? 'browse-failed'} @ ${state.draft}`;\n if (state.listing === null) return `${state.phase}`;\n return `${state.listing.path}(${rowsOf(state).length} 行)`;\n}\n\n/** 一行子目录的完整路径(图上点行时用)。 */\nexport function entryPath(directory: string, entry: DirectoryEntry): string {\n return entry.path === '' ? joinPath(directory, entry.name) : entry.path;\n}\n\n/** 目录行接线的返回:**不碰 React**——`actions` 原样喂给 `directory-browser-ui` 的组件 props,\n * `isOpen`/`setOpen` 由调用方自己接进它的状态(宿主怎么重画是它的事,本件不管)。 */\nexport interface DirectoryRowBrowser {\n readonly actions: {\n readonly onPick: () => void;\n readonly onClose: () => void;\n readonly onEnter: (path: string) => void;\n readonly onUp: () => void;\n readonly onSelect: (path: string) => void;\n readonly onToggleHidden: () => void;\n readonly onDraft: (draft: string) => void;\n readonly onCommitDraft: () => void;\n readonly onCreate: (name: string) => void;\n readonly onCreatingChange: (name: string | null) => void;\n };\n /** 开图(先试配置里那个目录,读不出来退回家目录),回第一份落定的状态。 */\n open(): Promise<BrowseState>;\n /** 状态变更订阅:视图那半靠 `useSyncExternalStore` 用它重画。\n *\n * **没有它图上只有第一帧**——列举是异步落定的,React 不订阅就永远停在开图那一刻的状态。 */\n subscribe(listener: BrowseListener): () => void;\n state(): BrowseState;\n /** 一段可读的当前状态(用例与调试用)。 */\n summary(): string;\n}\n\n/** 建一份「目录行接线」:给一个取数面与初值,回一组动作。\n *\n * 只做接线:把控制器算出来的状态与回调打包好,不取数、不画图、不认宿主。 */\nexport function createDirectoryRowBrowser(deps: {\n readonly face: DirectoryBrowseFace;\n readonly initialPath: string;\n readonly onPicked: (path: string) => void;\n readonly onClosed?: () => void;\n readonly failureLabel?: string;\n /** 可跳转的根从哪来(各家给的是同一份「总管电话」的取数器);不给就没有那一行。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): DirectoryRowBrowser {\n const controller = createBrowseController({\n face: deps.face,\n initialPath: deps.initialPath,\n onPicked: deps.onPicked,\n onClose: () => deps.onClosed?.(),\n ...(deps.failureLabel === undefined ? {} : { failureLabel: deps.failureLabel }),\n ...(deps.rootsSource === undefined ? {} : { rootsSource: deps.rootsSource }),\n });\n return {\n open: () => controller.open(),\n subscribe: (listener) => controller.subscribe(listener),\n state: () => controller.getState(),\n summary: () => describe(controller.getState()),\n actions: {\n onPick: () => controller.pick(),\n onClose: () => controller.cancel(),\n onEnter: (path) => void controller.enter(path),\n onUp: () => void controller.up(),\n onSelect: (path) => controller.select(path),\n onToggleHidden: () => controller.toggleHidden(),\n onDraft: (draft) => controller.setDraft(draft),\n onCommitDraft: () => void controller.commitDraft(),\n onCreate: (name) => void controller.createFolder(name),\n onCreatingChange: (name) => controller.setCreating(name),\n },\n };\n}\n\n/** 一条目录行点下去该走哪条路(**六家共用这一处策略**,单独写在各家就会各错各的)。\n *\n * 顺序是「先应用内浏览,被拒再换系统对话框」,因为客户端命名空间上三条动词都在、问不出组合里服务哪种\n * 能力(见 `pickerModeOf`)。回执:\n * - `undefined`=连两格浏览原语都没有(这条路没有,调用方自己想办法);\n * - `'refused'`=宿主回了 `refusalCode`(组合里是系统对话框),图已收起;\n * - `'open'`=图开着,后面的事都在图里。\n *\n * **状态由调用方持有**:`onRow` 收到刚建好的那条(或 null=收起),React 那边据此决定画不画图。\n *\n * `refusalCode` 是**宿主的回执码**(各家自己从宿主镜像里拿,本件只照比不认名):\n * 它换名字本件照样跑,六家一起换。 */\nexport function openRowBrowser(input: {\n readonly picker: unknown;\n readonly initialPath: string;\n readonly onChange: (next: string) => void;\n readonly onRow: (row: DirectoryRowBrowser | null) => void;\n readonly refusalCode: string;\n /** 可跳转的根从哪来(可选:不给就是图上没有那一行)。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): Promise<'open' | 'refused'> | undefined {\n const face = browseFaceOf(input.picker);\n if (face === null) return undefined;\n const row = createDirectoryRowBrowser({\n face,\n initialPath: input.initialPath,\n onPicked: (picked) => {\n input.onChange(picked);\n input.onRow(null);\n },\n onClosed: () => input.onRow(null),\n ...(input.rootsSource === undefined ? {} : { rootsSource: input.rootsSource }),\n });\n input.onRow(row);\n return row.open().then((settled) => {\n if (\n settled.phase === 'failed' &&\n isCapabilityRefusal(settled.failure?.code, settled.failure?.message, input.refusalCode)\n ) {\n input.onRow(null);\n return 'refused' as const;\n }\n return 'open' as const;\n });\n}\n","/** 文件浏览器视图半的**零件**:样式表与四行控件(面包屑/根/一行子目录/新建行)。\n *\n * 为什么拆出来:整张对话框的组版与这些零件的变化频率不同——零件跟着「图上多一行/少一行」动,\n * 组版跟着「对话框长什么样」动。拆开之后两边都在 350 行告警线内,而对外面不变\n * (`directory-browser-ui.ts` 照旧转出组件与两个接线函数)。\n *\n * 本件与视图半同样**不认识宿主**:只认状态与文案。\n */\n\nimport * as React from 'react';\nimport type { BrowseState } from './directory-browser-state.js';\nimport type { DirectoryEntry, RootKind } from './directory-browser-contract.js';\n\n/** 图上出现的每一句文案;调用方给(本件不自造口径)。 */\nexport interface DirectoryBrowserLabels {\n /** 对话框标题,例:「选择数据目录」。 */\n readonly title: string;\n readonly close: string;\n readonly up: string;\n readonly pathPlaceholder: string;\n readonly go: string;\n readonly showHidden: (count: number) => string;\n readonly empty: string;\n readonly loading: string;\n readonly newFolder: string;\n readonly createConfirm: string;\n readonly createCancel: string;\n readonly select: string;\n readonly selected: string;\n readonly open: string;\n readonly cancel: string;\n /** 选中预览那一行的前缀,例:「将选定:」。 */\n readonly willPick: string;\n}\n\n/** 根的类型说明(悬停可见):**本件自己的词汇**,与取数方无关。 */\nconst ROOT_KIND_TEXT: Record<RootKind, string> = {\n fixed: '固定磁盘',\n network: '网络驱动器',\n removable: '可移动磁盘',\n optical: '光驱',\n other: '磁盘',\n};\n\nexport const S = {\n scrim: {\n position: 'fixed',\n inset: 0,\n background: 'rgba(0,0,0,0.35)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n } as React.CSSProperties,\n dialog: {\n width: 680,\n maxWidth: '92vw',\n height: 500,\n maxHeight: '86vh',\n display: 'flex',\n flexDirection: 'column',\n background: 'var(--dsw-alias-bg-layer-1, #1f1f23)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 10,\n boxShadow: '0 12px 32px rgba(0,0,0,0.35)',\n overflow: 'hidden',\n } as React.CSSProperties,\n head: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n } as React.CSSProperties,\n title: { fontSize: 14, fontWeight: 700 } as React.CSSProperties,\n crumbs: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2, padding: '8px 12px 0', fontSize: 12 } as React.CSSProperties,\n chromeButton: {\n background: 'transparent',\n border: 'none',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n padding: '2px 4px',\n fontSize: 12,\n opacity: 0.85,\n } as React.CSSProperties,\n crumbSep: { opacity: 0.45, fontSize: 12 } as React.CSSProperties,\n roots: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6, padding: '0 12px 8px', fontSize: 12 } as React.CSSProperties,\n rootButton: {\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n padding: '3px 8px',\n opacity: 1,\n } as React.CSSProperties,\n pathRow: { display: 'flex', gap: 6, padding: '8px 12px' } as React.CSSProperties,\n input: {\n flex: 1,\n minWidth: 0,\n fontSize: 12,\n padding: '5px 8px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n } as React.CSSProperties,\n button: {\n fontSize: 12,\n padding: '5px 10px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n } as React.CSSProperties,\n buttonOff: { opacity: 0.45, cursor: 'default' } as React.CSSProperties,\n list: {\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n margin: '0 12px',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n borderRadius: 6,\n } as React.CSSProperties,\n row: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n padding: '6px 10px',\n textAlign: 'left',\n background: 'transparent',\n color: 'inherit',\n border: 'none',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))',\n cursor: 'pointer',\n fontSize: 13,\n } as React.CSSProperties,\n rowSelected: { background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.18))' } as React.CSSProperties,\n empty: { padding: '14px 12px', fontSize: 12, opacity: 0.7 } as React.CSSProperties,\n footNote: { padding: '6px 12px 0', fontSize: 12, minHeight: 20 } as React.CSSProperties,\n foot: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n } as React.CSSProperties,\n target: { marginRight: 'auto', fontSize: 12, opacity: 0.8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } as React.CSSProperties,\n error: { color: 'var(--dsw-alias-state-error-primary, #e66)' } as React.CSSProperties,\n} as const;\n\n/** 面包屑:从文件系统根到当前层,每一格都是跳转目标(`listing.crumbs` 由取数方给)。 */\nexport function crumbRow(state: BrowseState, onEnter: (path: string) => void): React.ReactElement | null {\n const listing = state.listing;\n if (listing === null || listing.crumbs.length === 0) return null;\n const parts: React.ReactNode[] = [];\n listing.crumbs.forEach((crumb, index) => {\n if (index > 0) parts.push(React.createElement('span', { key: 'sep-' + crumb.path, style: S.crumbSep }, '›'));\n const isCurrent = crumb.path === listing.path;\n parts.push(\n React.createElement(\n 'button',\n {\n key: crumb.path,\n type: 'button',\n style: { ...S.chromeButton, fontWeight: isCurrent ? 700 : 400 },\n title: crumb.path,\n // #1164 T5:对话框组 V1 全量(复用共享 interactionCss,不另起 tokens/链路)。\n // 面包屑与普通按钮同口径:悬停聚光+淡洗+辉光+上浮、按下收缩、焦点双环、\n // 长按 500ms 纯视觉环(只做视觉标记,不注册动作回调、不进状态机分支)。\n 'data-ilife-press': 'dialog-crumb',\n 'data-ilife-longpress': 'dialog-crumb',\n onClick: () => onEnter(crumb.path),\n },\n // 每一格都写**它自己那一层的名字**:第一格是文件系统根(`C:\\`),不是「上一级」——\n // 「上一级」是路径行那颗按钮的活(#744 返修:这一格原先串了那颗按钮的文案)。\n crumb.name,\n ),\n );\n });\n return React.createElement('div', { style: S.crumbs }, parts);\n}\n\n/** 可跳转的根那一行(Windows 上的盘符等):**就是本机全部磁盘**,故不加行首说明。\n * **少于两个根就不画**:一个根没有「跳到别处」可言。 */\nexport function rootsRow(state: BrowseState, onEnter: (path: string) => void): React.ReactElement | null {\n if (state.roots.length < 2) return null;\n const parts: React.ReactNode[] = state.roots.map((root) =>\n React.createElement(\n 'button',\n {\n key: root.path,\n type: 'button',\n style: { ...S.chromeButton, ...S.rootButton },\n title: ROOT_KIND_TEXT[root.kind] ?? ROOT_KIND_TEXT.other,\n // #1164 T5:同上(根跳转与普通按钮同口径)。\n 'data-ilife-press': 'dialog-root',\n 'data-ilife-longpress': 'dialog-root',\n onClick: () => onEnter(root.path),\n },\n root.path,\n ),\n );\n return React.createElement('div', { style: S.roots }, parts);\n}\n\n/** 一行子目录:点名字进它,点「选」把它当目标(「选」再点一次取消)。 */\nexport function entryRow(\n entry: DirectoryEntry,\n selected: boolean,\n labels: DirectoryBrowserLabels,\n onToggleSelect: (path: string) => void,\n onEnter: (path: string) => void,\n): React.ReactElement {\n return React.createElement(\n 'div',\n { key: entry.path, style: { ...S.row, padding: 0, ...(selected ? S.rowSelected : {}) } },\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.row, flex: 1, border: 'none' },\n // #1164 T5:条目进入与普通按钮同口径(按压+焦点双环+长按纯视觉环)。\n 'data-ilife-press': 'dialog-entry',\n 'data-ilife-longpress': 'dialog-entry',\n onClick: () => onEnter(entry.path),\n },\n '📁 ' + entry.name,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, marginRight: 8, padding: '2px 8px' },\n 'aria-pressed': selected,\n title: selected ? labels.selected : labels.select,\n // #1164 T5:条目选中与普通按钮同口径。\n 'data-ilife-press': 'dialog-select',\n 'data-ilife-longpress': 'dialog-select',\n onClick: () => onToggleSelect(entry.path),\n },\n selected ? '✓' : labels.select,\n ),\n );\n}\n\n/** 「新建文件夹」那一行(`creatingName === null` 时收起)。 */\nexport function createRow(\n labels: DirectoryBrowserLabels,\n onName: (name: string) => void,\n onCancel: () => void,\n onCreate: (name: string) => void,\n creatingName: string | null,\n): React.ReactElement | null {\n if (creatingName === null) return null;\n // #1164 T5:空名不可点(禁用态摇头+禁止指针);非空才可建(validateFolderName 的其余非法名仍走 notice 人话)。\n const createEmpty = creatingName.trim() === '';\n return React.createElement(\n 'div',\n { style: { ...S.pathRow, borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))' } },\n React.createElement('input', {\n style: S.input,\n value: creatingName,\n placeholder: labels.newFolder,\n 'aria-label': labels.newFolder,\n // #1164 T5:文本框只给焦点双环(不收缩、无长按环,与主面板输入框同口径)。\n 'data-ilife-focus': 'dialog-input',\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => onName(event.currentTarget.value),\n onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') onCreate(creatingName);\n if (event.key === 'Escape') onCancel();\n },\n }),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: createEmpty,\n // #1164 T5:创建确认与普通按钮同口径;禁用时摇头+禁止指针(共享 interactionCss)。\n 'data-ilife-press': 'dialog-create',\n 'data-ilife-longpress': 'dialog-create',\n onClick: () => onCreate(creatingName),\n },\n labels.createConfirm,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n // #1164 T5:创建取消与普通按钮同口径(常可用)。\n 'data-ilife-press': 'dialog-create-cancel',\n 'data-ilife-longpress': 'dialog-create-cancel',\n onClick: onCancel,\n },\n labels.createCancel,\n ),\n );\n}\n","/** 文件浏览器的视图半:把 `BrowseState` 画成一张对话框。**不认识宿主**,也不自己做 IO。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX,见 `tsdown.config.ts` 的既成口径)。\n * 只读状态、只回调;进哪一层、选到什么由操作半(`directory-browser-state.ts`)算。\n * 样式与四行控件在 `directory-browser-parts.ts`(那里跟着「多一行/少一行」动,本件只管组版)。\n */\n\nimport * as React from 'react';\nimport { canGoUp, rowsOf, targetOf } from './directory-browser-state.js';\nimport type { BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nimport { hiddenCount } from './directory-browser-contract.js';\nimport type { DirectoryListing } from './directory-browser-contract.js';\nimport { S, createRow, crumbRow, entryRow, rootsRow } from './directory-browser-parts.js';\nimport type { DirectoryBrowserLabels } from './directory-browser-parts.js';\n\nexport type { DirectoryBrowserLabels } from './directory-browser-parts.js';\n\n/** 组件 props:样式只吃这一格,别的一律不给。 */\nexport interface DirectoryBrowserProps {\n readonly open: boolean;\n readonly state: BrowseState;\n readonly labels: DirectoryBrowserLabels;\n readonly onPick: () => void;\n readonly onClose: () => void;\n readonly onEnter: (path: string) => void;\n readonly onUp: () => void;\n readonly onSelect: (path: string) => void;\n readonly onToggleHidden: () => void;\n readonly onDraft: (draft: string) => void;\n readonly onCommitDraft: () => void;\n readonly onCreate: (name: string) => void;\n readonly onCreatingChange: (name: string | null) => void;\n}\n\nconst EMPTY_LISTING: DirectoryListing = { path: '', home: '', crumbs: [], entries: [], truncated: false };\n\n/** 画一张文件浏览器对话框。`open=false` 时返回 null(本件不挂 portal,由调用方决定挂在哪)。 */\nexport function DirectoryBrowser(props: DirectoryBrowserProps): React.ReactElement | null {\n const { open, state, labels } = props;\n if (!open) return null;\n const rows = rowsOf(state);\n const target = targetOf(state);\n const listing = state.listing ?? EMPTY_LISTING;\n const hidden = hiddenCount(listing);\n // #1164 T5:对话框组 V1 全量(复用共享 interactionCss,不另起 tokens/链路)。\n // 按钮一律按压+长按纯视觉环(只做视觉标记,不注册动作回调、不进状态机分支);\n // 输入框/复选框只给焦点双环(不收缩、无长按环);禁用态摇头+禁止指针走共享 CSS。\n const upOff = !canGoUp(state);\n const goOff = state.draft.trim() === '';\n const pickOff = target === null;\n const body: React.ReactNode[] = [];\n body.push(crumbRow(state, props.onEnter));\n body.push(rootsRow(state, props.onEnter));\n body.push(\n React.createElement(\n 'div',\n { style: S.pathRow },\n React.createElement('input', {\n style: S.input,\n value: state.draft,\n placeholder: labels.pathPlaceholder,\n 'aria-label': labels.pathPlaceholder,\n // #1164 T5:路径框只给焦点双环(与主面板输入框同口径)。\n 'data-ilife-focus': 'dialog-input',\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => props.onDraft(event.currentTarget.value),\n onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') props.onCommitDraft();\n },\n }),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: upOff,\n 'data-ilife-press': 'dialog-up',\n 'data-ilife-longpress': 'dialog-up',\n onClick: props.onUp,\n },\n labels.up,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: goOff,\n 'data-ilife-press': 'dialog-go',\n 'data-ilife-longpress': 'dialog-go',\n onClick: props.onCommitDraft,\n },\n labels.go,\n ),\n ),\n );\n body.push(\n React.createElement(\n 'div',\n { style: S.list, role: 'listbox', 'aria-label': labels.title },\n rows.length === 0\n ? React.createElement('div', { style: S.empty }, state.phase === 'loading' ? labels.loading : labels.empty)\n : rows.map((entry) => entryRow(entry, state.selected === entry.path, labels, props.onSelect, props.onEnter)),\n ),\n );\n body.push(createRow(labels, props.onDraft, () => props.onCreatingChange(null), props.onCreate, state.creating));\n body.push(\n React.createElement(\n 'div',\n { style: S.footNote },\n hidden > 0\n ? React.createElement(\n 'label',\n { style: { fontSize: 12, cursor: 'pointer', marginRight: 10 } },\n React.createElement('input', {\n type: 'checkbox',\n checked: state.showHidden,\n // #1164 T5:复选框只给焦点双环(不收缩、无长按环)。\n 'data-ilife-focus': 'dialog-check',\n onChange: props.onToggleHidden,\n }),\n ' ' + labels.showHidden(hidden),\n )\n : null,\n state.creating === null\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.chromeButton, textDecoration: 'underline' },\n // #1164 T5:新建入口与普通按钮同口径。\n 'data-ilife-press': 'dialog-newfolder',\n 'data-ilife-longpress': 'dialog-newfolder',\n onClick: () => props.onCreatingChange(''),\n },\n labels.newFolder,\n )\n : null,\n state.notice !== null ? React.createElement('div', { style: S.error }, state.notice) : null,\n state.phase === 'failed' && state.failure !== null\n ? React.createElement('div', { style: S.error }, state.failure.message)\n : null,\n ),\n );\n body.push(\n React.createElement(\n 'div',\n { style: S.foot },\n React.createElement('span', { style: S.target, title: target ?? '' }, target === null ? '' : labels.willPick + target),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, ...(pickOff ? S.buttonOff : {}) },\n disabled: pickOff,\n // #1164 T5:无目标时不可点(禁用态摇头+禁止指针);有目标时与普通按钮同口径。\n 'data-ilife-press': 'dialog-pick',\n 'data-ilife-longpress': 'dialog-pick',\n onClick: props.onPick,\n },\n labels.open,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n // #1164 T5:取消与普通按钮同口径(常可用)。\n 'data-ilife-press': 'dialog-cancel',\n 'data-ilife-longpress': 'dialog-cancel',\n onClick: props.onClose,\n },\n labels.cancel,\n ),\n ),\n );\n return React.createElement(\n 'div',\n {\n style: S.scrim,\n role: 'presentation',\n onMouseDown: (event: React.MouseEvent<HTMLDivElement>) => {\n if (event.target === event.currentTarget) props.onClose();\n },\n },\n React.createElement(\n 'div',\n { style: S.dialog, role: 'dialog', 'aria-modal': true, 'aria-label': labels.title },\n React.createElement(\n 'div',\n { style: S.head },\n React.createElement('span', { style: S.title }, labels.title),\n // 标题右侧**不再重复当前路径**:同一条路径下面已经出现两次(面包屑与路径框),\n // 而盘符那行也要地方——头一行越干净越好(#744 第三轮现场反馈)。\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, marginLeft: 'auto' },\n 'aria-label': labels.close,\n title: labels.close,\n // #1164 T5:标题栏关闭与普通按钮同口径。\n 'data-ilife-press': 'dialog-close',\n 'data-ilife-longpress': 'dialog-close',\n onClick: props.onClose,\n },\n '✕',\n ),\n ),\n body,\n ),\n );\n}\n\n/** 目录行接线的 React 半边:把操作半那组动作直接喂给组件。\n *\n * 操作半(`directory-browser-state.ts` 的 `createDirectoryRowBrowser`)不碰 React,\n * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。\n *\n * `useSyncExternalStore` 是**必需**的:列举是异步落定的,不订阅的话这一帧画完就再没有重画,\n * 图上会永远停在「正在读取…」(#744 实测)。 */\nexport function DirectoryBrowserFromRow(props: {\n readonly open: boolean;\n readonly row: DirectoryRowBrowser;\n readonly labels: DirectoryBrowserLabels;\n}): React.ReactElement | null {\n const state = React.useSyncExternalStore(props.row.subscribe, props.row.state);\n return DirectoryBrowser({\n open: props.open,\n state,\n labels: props.labels,\n ...props.row.actions,\n });\n}\n\nexport { createBrowseController, createDirectoryRowBrowser, describe, rowsOf, targetOf } from './directory-browser-state.js';\nexport type { BrowseController, BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nexport type { DirectoryEntry, DirectoryListing, RootKind, RootRow } from './directory-browser-contract.js';\n","/** 共用标题 + 印章书签 · 纯组件(#996,不认任何一家)。\n *\n * 来源:原型 #995 变体 A(行尾悬签,人裁已定形)——标题左、签贴同行行尾向右探出。\n * 用法分两档(#1069 收窄后):`TitleBlock` 两家——`config-panel-view.ts` 的 `PanelBody`\n * (`product`=各家产品名/`purpose`=`配置`)与 `health-view.ts` 的 `HealthTable`\n * (`product`=各家产品名/`purpose`=`体检`);`Seal` 只跟 `PanelBody` 走——**签只印在配置头,\n * 体检头不带签**(负责人 2026-10-03 定;签的第二个用法是 `HealthTable`,本票已摘掉,签回到单一用法)。\n * 别处要同形标题才用 `TitleBlock`,用不出来就留在那两家;签再要第二个用法,先问负责人。\n *\n * 形状(逐值照原型锁死,不许自己重新设计):\n * 标题:同行双样式、无 `·`——`product` 700/1.15em 主色 + `purpose` 400/0.85em 次色,\n * 同行基线、`gap .5em`(16px 下=8px,满足地图 8px),整块 `h2`(`aria-label`=`product purpose`)。\n * 字号取原型锁(地图 Notes 写 1.08em/640 是 grill 共识早值,原型后锁 1.15em/700,以原型为准)。\n * 签:横向书签固定色 `#a5281b` 白字、`clip-path` 右端 V 尾切角 + 微旋 `-3°`、\n * `white-space:pre` 保空格、`role=\"note\"` 纯展示不可点(无点击、无 tabindex)。\n * 印文由调用方交 `sealText`(显示形一律带空格大写 HELP,空格=U+0020,997 接线);只配头给。\n *\n * 纪律(#739/#941):\n * 零字族(无 `fontFamily`、无 `font:` 简写)、字号一律 em;颜色走 DSH 主题别名,\n * 唯签固定色沿 health 红黄固定色先例(纸变印不变,印不管纸面派生)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\n\nconst S = {\n /** 整块 `h2`:同行基线双样式容器(原型 `.titleblock`;`flex:1 1 auto` 把签顶到行尾,390 宽可换行不挤)。 */\n titleBlock: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'baseline',\n gap: '0 0.5em',\n margin: 0,\n fontSize: '1em',\n lineHeight: 1.5,\n flex: '1 1 auto',\n minWidth: '9em',\n } as React.CSSProperties,\n /** 产品名(原型 `.product`:700/1.15em 主色)。 */\n product: {\n fontSize: '1.15em',\n fontWeight: 700,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** 用途(原型 `.purpose`:400/0.85em 次色)。 */\n purpose: {\n fontSize: '0.85em',\n fontWeight: 400,\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n /** 签(原型 `.seal`:固定印色+右端 V 尾+微旋,纯展示)。 */\n seal: {\n display: 'inline-block',\n flexShrink: 0,\n fontSize: '0.78em',\n lineHeight: 1.6,\n color: '#fff',\n background: '#a5281b',\n padding: '0.15em 0.9em 0.15em 0.8em',\n whiteSpace: 'pre',\n transform: 'rotate(-3deg)',\n clipPath: 'polygon(0 0, calc(100% - 0.65em) 0, 100% 50%, calc(100% - 0.65em) 100%, 0 100%)',\n } as React.CSSProperties,\n} as const;\n\nexport interface TitleBlockProps {\n /** 产品名(各家自己的那一格;缺席=空串,只印用途)。 */\n readonly product: string;\n /** 用途(`配置`/`体检`两档,不认第三档)。 */\n readonly purpose: string;\n}\n\nexport interface SealProps {\n /** 印文(显示形一律产品名+空格+大写 HELP,空格=U+0020;纯展示不可点)。 */\n readonly sealText: string;\n}\n\n/** 标题分层(同行双样式,无 `·`,整块 `h2`)。 */\nexport function TitleBlock(props: TitleBlockProps): React.ReactElement {\n const label = (props.product + ' ' + props.purpose).trim();\n return React.createElement(\n 'h2',\n { style: S.titleBlock, 'aria-label': label },\n React.createElement('span', { style: S.product }, props.product),\n React.createElement('span', { style: S.purpose }, props.purpose),\n );\n}\n\n/** 印章书签(横向固定印,纯展示)。 */\nexport function Seal(props: SealProps): React.ReactElement {\n return React.createElement(\n 'span',\n { style: S.seal, role: 'note', 'aria-label': props.sealText },\n props.sealText,\n );\n}\n","/** 印章卷轴 · 纯组件(`title-seal.ts` 的配套件,不认任何一家)。\n *\n * 来源:原型定稿 2026-10-05(记录见 `docs/plugins/plugin-manager/印章原型-定稿.md` 第九节)。\n * 点印后弹出的那块:**外框逐档照抄对应那枚印的材质**(底色+边线都照抄,不许简化成\"一圈金属色\"),\n * **中间是纯纸**,内容固定三段——进展/状态/计划。\n *\n * 三条形状(逐值照原型锁死):\n * 外框:厚 1.125em(16px 下=18px,这是下限;只给 10px 整张卡会读成\"白底加一圈边\"),\n * 材质随 `role`×`tier` 九档之一,挂 `roughFrame` 糙边滤镜。\n * 纸:`bg-layer-2` 底+`border-l1` 描边,压在材质上(外框那层在纸下面,只露一圈)。\n * 字:标题居中 1em/字距 .16em;档位字 .66em/字距 .42em;三段标签 .66em/字距 .34em;\n * 正文 .84em/行高 1.9;段间 1px 细线+.94em 上距。\n *\n * 颜色分治(照 `title-seal.ts` 的先例):\n * 印那一份(外框材质、档位字色)**固定色**——纸变印不变,印不管纸面派生;\n * 纸那一份(纸底、纸边、标题、正文、段间线)走 DSH 主题别名。\n *\n * 糙边滤镜两条分开:印是小块,`scale 2.2` 就够;外框有几百像素宽,同一条会被稀释到看不出,\n * 故外框单用 `scale 5` 的 `roughFrame`。滤镜只挂外框那层,纸不挂(否则纸的直边也会被抖)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\nimport type { SealRole, SealTier } from './config-panel-contract.js';\n// 卷轴标题直接复用印本体(与外面那枚同组件同 props,UI 天然一致)。\n// 与 `seal-stamp.ts` 是渲染期互引(双方只在组件函数体内用对方,无模块求值期依赖)。\nimport { SealStamp } from './seal-stamp.js';\n\n/** 糙边滤镜 id(文档作用域,故每页只许挂一次 `SealFilterDefs`)。 */\nconst ROUGH_EDGE = 'dshLifeSealRoughEdge';\nconst ROUGH_FRAME = 'dshLifeSealRoughFrame';\n\n/** 印那条糙边滤镜 id(`seal-stamp.ts` 引用,定义只此一处)。 */\nexport const SEAL_ROUGH_EDGE_ID = ROUGH_EDGE;\n\n/** 档位话:标题下那一行不再印档位字,直接说档位所处的阶段(渲染期用,顶层可放行)。 */\nexport const TIER_TEXT: Record<SealTier, string> = {\n gold: '精雕细琢中',\n silver: '全打通中',\n copper: '基础建设中',\n};\n\n/** 卷轴展开动画名(文档作用域,印前缀防撞;`S.popInner` 求值期即用,必须住 `S` 之前)。 */\nconst UNROLL_ANIMATION = 'dshLifeSealUnroll';\n\n/** 一档材质:外框那一份是固定色(印),档位字色也是固定色。 */\ninterface Material {\n /** 外框底色+边线(逐值照该枚印)。 */\n readonly frame: React.CSSProperties;\n /** 档位字与标签的色(印派生,固定色)。 */\n readonly accent: string;\n /** 分隔线同族色。 */\n readonly ruler: string;\n /** 四角铆钉(只有插件章的整面金属带有)。 */\n readonly rivet?: React.CSSProperties;\n}\n\n/** 纸面四色是印自带的(羊皮纸底+ availability 墨色),不跟宿主主题跑——\n * 深色下宿主变量会把纸染黑,故这里写死定稿值;字族仍继承(定稿零字族)。 */\nconst PAPER_BG = '#fdfaf2';\nconst PAPER_EDGE = '#e6d9bd';\nconst LABEL_MAIN = '#42506b';\nconst LABEL_SUB = '#4b4335';\n\n/** 九档材质(3 角色 × 3 档)。键就是原型里的 `role`/`tier`。 */\nconst MATERIALS: Record<string, Record<string, Material>> = {\n skill: {\n copper: {\n frame: {\n background: 'linear-gradient(180deg,#8a4a28,#6e3418)',\n border: '3px solid #a5652f',\n boxShadow: 'inset 0 0 0 1px #f8f1e2aa, inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d',\n },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n },\n silver: {\n frame: {\n background: 'linear-gradient(180deg,#b73124,#8e1f14)',\n border: '3px double #eef3f6',\n boxShadow: 'inset 0 0 0 1px #fff, inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d',\n },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n },\n gold: {\n frame: {\n background: 'linear-gradient(180deg,#d34a35,#a32216)',\n border: '3px double #f5d97a',\n boxShadow: 'inset 0 0 0 1px #fff8, inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d',\n },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n },\n },\n help: {\n copper: {\n frame: { background: 'linear-gradient(180deg,#b06a3a,#7e3f1d)', border: '3px solid #a5652f', boxShadow: 'inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d' },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n },\n silver: {\n frame: { background: 'linear-gradient(180deg,#8f979e,#5f666d)', border: '3px double #e8eef2', boxShadow: 'inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d' },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n },\n gold: {\n frame: { background: 'linear-gradient(180deg,#c63d2a,#a32216)', border: '3px double #f5d97a', boxShadow: 'inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d' },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n },\n },\n plugin: {\n copper: {\n frame: { background: 'linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)', boxShadow: 'inset 0 1px 0 #d99a5e88, inset 0 -2px 5px #0006, 0 0 0 1px #5e2c12, inset 0 2px 0 #ffffff26' },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)', boxShadow: 'inset 0 -1px 1px #00000055,0 1px 2px #00000088' },\n },\n silver: {\n frame: { background: 'linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)', boxShadow: 'inset 0 1px 0 #ffffffdd, inset 0 -2px 5px #0005, 0 0 0 1px #6f767c, inset 0 2px 0 #ffffff33' },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077' },\n },\n gold: {\n frame: { background: 'linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)', boxShadow: 'inset 0 1px 0 #fff8d8dd, inset 0 -2px 5px #0005, 0 0 0 1px #8a6a15' },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788' },\n },\n },\n};\n\nconst S = {\n /** 外框(`.frame`):绝对铺满,挂糙边滤镜;纸压在它上面,只露一圈。 */\n frame: {\n position: 'absolute',\n inset: 0,\n borderRadius: '1em',\n zIndex: 1,\n filter: 'url(#' + ROUGH_FRAME + ')',\n } as React.CSSProperties,\n /** 四角铆钉(只有插件章的整面金属带有)。 */\n rivet: {\n position: 'absolute',\n width: '0.69em',\n height: '0.69em',\n borderRadius: '50%',\n } as React.CSSProperties,\n /** 卷轴根:padding 就是外框厚度(.frame 铺满、纸在 padding 内)。 */\n scroll: {\n position: 'relative',\n fontSize: '1em',\n lineHeight: 1.7,\n borderRadius: '0.875em',\n padding: '1.125em',\n } as React.CSSProperties,\n /** 纸(`.paper`):纯纸,无纹理。 */\n paper: {\n position: 'relative',\n zIndex: 2,\n borderRadius: '0.25em',\n background: PAPER_BG,\n boxShadow: '0 1px 3px #3d241052, 0 0 0 1px ' + PAPER_EDGE + ', inset 0 1px 0 #fffefb',\n } as React.CSSProperties,\n inner: {\n position: 'relative',\n padding: '1.75em 2em 1.875em',\n } as React.CSSProperties,\n head: { textAlign: 'center', marginBottom: '0.5em' } as React.CSSProperties,\n /** 档位语(标题下那一行):档位字退役,改说档位话(金=精雕细琢中/银=全打通中/铜=基础建设中)。 */\n tier: {\n marginTop: '0.4375em',\n fontSize: '0.66em',\n letterSpacing: '0.42em',\n textIndent: '0.42em',\n fontWeight: 600,\n } as React.CSSProperties,\n orn: {\n display: 'flex',\n alignItems: 'center',\n gap: '0.5625em',\n margin: '1em 0 1.25em',\n } as React.CSSProperties,\n ornLine: { flex: '1 1 auto', height: 1 } as React.CSSProperties,\n ornDot: { width: '0.3125em', height: '0.3125em', transform: 'rotate(45deg)', opacity: 0.75, flex: '0 0 auto' } as React.CSSProperties,\n block: { margin: '0 0 0.9375em' } as React.CSSProperties,\n blockDivided: { margin: '0 0 0.9375em', paddingTop: '0.9375em', borderTop: '1px solid ' + PAPER_EDGE } as React.CSSProperties,\n blockLast: { margin: 0 } as React.CSSProperties,\n label: {\n fontSize: '0.66em',\n letterSpacing: '0.34em',\n textIndent: '0.34em',\n fontWeight: 600,\n marginBottom: '0.375em',\n } as React.CSSProperties,\n text: {\n margin: 0,\n fontSize: '0.84em',\n lineHeight: 1.9,\n letterSpacing: '0.02em',\n color: LABEL_SUB,\n } as React.CSSProperties,\n /** 卷轴浮层:卡片内绝对定位(卡片 `S.card` 是 relative 定位祖先),上沿悬在三枚章之下、左右各留半 em——\n * 不是 modal:无遮罩、不锁滚动,点章切换、点「关闭」收起。 */\n dialog: { position: 'absolute', left: '0.5em', right: '0.5em', top: '8.8em', zIndex: 10, display: 'flex', justifyContent: 'center', pointerEvents: 'auto' } as React.CSSProperties,\n /** 浮层内框:宽按 em 给(16px 下≈25em),窄卡上收满可用宽,永不捅破卡片;\n * 挂卷轴展开动画(从上而下舒卷,240ms;纯声明式,无 hook,纯函数可直测)。 */\n popInner: { position: 'relative', width: '25em', maxWidth: '100%', transformOrigin: '50% 0', animation: UNROLL_ANIMATION + ' 240ms ease-out' } as React.CSSProperties,\n close: {\n position: 'absolute',\n right: '-0.375em',\n top: '-2.25em',\n font: 'inherit',\n fontSize: '0.75em',\n color: '#fff',\n background: '#241f19b3',\n border: 'none',\n borderRadius: '0.875em',\n padding: '0.25em 0.875em',\n cursor: 'pointer',\n } as React.CSSProperties,\n} as const;\n\n/** 卷轴要显示的东西:角色 × 档位 + 标题 + 三段文案。 */\nexport interface SealScrollProps {\n /** 角色(技能章/HELP 章/插件章;决定外框是不是整面金属带、有没有铆钉)。 */\n readonly role: SealRole;\n /** 档位(决定外框材质)。 */\n readonly tier: SealTier;\n /** 标题(印文那一行,如 `技能`/`饼干记账 HELP`/`插件`)。 */\n readonly title: string;\n /** 进展。 */\n readonly progress: string;\n /** 状态。 */\n readonly status: string;\n /** 计划。 */\n readonly plan: string;\n}\n\nfunction materialOf(role: SealScrollProps['role'], tier: SealScrollProps['tier']): Material {\n const byTier = MATERIALS[role];\n const found = byTier?.[tier];\n if (!found) throw new Error('未知的印档:' + role + '/' + tier);\n return found;\n}\n\n/** 糙边滤镜定义:两条分开(印用 edge,卷轴外框用 frame),每页只挂一次。\n *\n * 挂在文档任意位置都行(`width/height=0` 不占位),同页重复挂会撞 id。 */\nexport function SealFilterDefs(): React.ReactElement {\n return React.createElement(\n 'svg',\n { width: 0, height: 0, 'aria-hidden': true, focusable: false, style: { position: 'absolute' } },\n React.createElement(\n 'filter',\n { id: ROUGH_EDGE, key: ROUGH_EDGE },\n React.createElement('feTurbulence', { baseFrequency: 0.9, numOctaves: 2, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 2.2 }),\n ),\n React.createElement(\n 'filter',\n { id: ROUGH_FRAME, key: ROUGH_FRAME, x: '-6%', y: '-6%', width: '112%', height: '112%' },\n React.createElement('feTurbulence', { baseFrequency: 0.9, numOctaves: 2, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 5 }),\n ),\n );\n}\n\n/** 一卷:外框材质 + 纯纸 + 进展/状态/计划三段。 */\nexport function SealScroll(props: SealScrollProps): React.ReactElement {\n const material = materialOf(props.role, props.tier);\n const frameChildren =\n props.role === 'plugin' && material.rivet\n ? [\n React.createElement('span', { key: 'tl', style: { ...S.rivet, ...material.rivet, left: '0.4375em', top: '0.4375em' } }),\n React.createElement('span', { key: 'tr', style: { ...S.rivet, ...material.rivet, right: '0.4375em', top: '0.4375em' } }),\n React.createElement('span', { key: 'bl', style: { ...S.rivet, ...material.rivet, left: '0.4375em', bottom: '0.4375em' } }),\n React.createElement('span', { key: 'br', style: { ...S.rivet, ...material.rivet, right: '0.4375em', bottom: '0.4375em' } }),\n ]\n : [];\n const line = (key: string, dir: string): React.ReactElement =>\n React.createElement('span', {\n key,\n style: { ...S.ornLine, background: 'linear-gradient(' + dir + 'deg,#0000,' + material.ruler + ')' },\n });\n const block = (key: string, name: string, text: string, divided: boolean, last = false): React.ReactElement =>\n React.createElement(\n 'div',\n { key, style: last ? (divided ? { ...S.blockLast, paddingTop: '0.9375em', borderTop: '1px solid ' + PAPER_EDGE } : S.blockLast) : divided ? S.blockDivided : S.block },\n React.createElement('div', { style: { ...S.label, color: material.accent } }, name),\n React.createElement('p', { style: S.text }, text),\n );\n return React.createElement(\n 'div',\n { style: S.scroll },\n React.createElement('div', { style: { ...S.frame, ...material.frame } }, ...frameChildren),\n React.createElement(\n 'div',\n { style: S.paper },\n React.createElement(\n 'div',\n { style: S.inner },\n React.createElement(\n 'div',\n { style: S.head },\n React.createElement(SealStamp, { role: props.role, tier: props.tier, label: props.title }),\n React.createElement('div', { style: { ...S.tier, color: material.accent } }, TIER_TEXT[props.tier]),\n ),\n React.createElement('div', { style: S.orn }, line('l', '90'), React.createElement('i', { style: { ...S.ornDot, background: material.accent } }), line('r', '270')),\n block('progress', '进展', props.progress, false),\n block('status', '状态', props.status, true),\n block('plan', '计划', props.plan, true, true),\n ),\n ),\n );\n}\n\n/** 卷轴浮层:popover + 点击别处关闭 + 卷轴展开动画 + 关闭。`open` 为假时不渲染(纯函数,不收自己的状态)。 */\nexport function SealScrollDialog(\n props: SealScrollProps & { readonly open: boolean; readonly onClose: () => void },\n): React.ReactElement | null {\n if (!props.open) return null;\n const scroll: SealScrollProps = {\n role: props.role,\n tier: props.tier,\n title: props.title,\n progress: props.progress,\n status: props.status,\n plan: props.plan,\n };\n return React.createElement(\n React.Fragment,\n null,\n // 收卷层:全屏透明,只收“点别处即关”,不 dim、不锁滚动(无 role,不算 modal 遮罩)。\n React.createElement('div', { style: { position: 'fixed', inset: 0, zIndex: 9, background: 'transparent' }, 'aria-hidden': true, onClick: props.onClose }),\n React.createElement(\n 'div',\n { style: S.dialog, role: 'dialog', 'aria-modal': false, 'aria-label': props.title },\n // 展开关键帧住在这里(scoped 名,宿主无样式表可借时唯一一条纯声明式动画路)。\n React.createElement('style', null, '@keyframes ' + UNROLL_ANIMATION + '{from{opacity:0;transform:translateY(-10px) scaleY(.7)}to{opacity:1;transform:none}}'),\n React.createElement(\n 'div',\n { style: S.popInner },\n // #1160 T1:按压收缩+焦点双环(卷轴浮层渲染在卡片内,样式由卡片那枚 <style> 罩住;印章本体仍豁免)。\n React.createElement('button', { type: 'button', style: S.close, 'data-ilife-press': 'seal-close', onClick: props.onClose }, '关闭'),\n React.createElement(SealScroll, scroll),\n ),\n ),\n );\n}\n","/** 九枚印章 · 纯组件(`title-seal.ts` 的配套件,不认任何一家)。\n *\n * 来源:验收页 `.scratch/seal-9-restore-approve-v1.html` v2(逐值照 `.scratch/seal-9-roles-v6.html`:\n * 技能横条 1/HELP 旧版 2b/插件双铆 3,渐变、边线、铆钉、糙边、字号 padding 全锁)。\n * 印文:技能签统一“技能”、插件签统一“插件”(印面不带档位字,档位只看颜色边线);\n * HELP 签印产品名加空格加大写 HELP。点击弹卷(`seal-scroll.ts` 的 `SealScrollDialog`,调用方持 open),\n * 故印是 `button`(`aria-haspopup=\"dialog\"`),hover 不出东西。\n *\n * 字号一律 em(16px 下与原型 px 逐值对齐:1.3125em=21px、1.0625em=17px);\n * 糙边走印那条(`SEAL_ROUGH_EDGE_ID`,定义只在 `seal-scroll.ts`,此处引用)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\nimport { SEAL_ROUGH_EDGE_ID } from './seal-scroll.js';\nimport type { SealRole, SealTier } from './config-panel-contract.js';\n\n/** 印的角色与档位见 `config-panel-contract.ts`(定义只那一处,此处引用)。 */\n\nexport interface SealStampProps {\n /** 角色:skill=技能横条/help=HELP 旧版 2b/plugin=插件双铆。 */\n readonly role: SealRole;\n /** 档位:copper=铜/silver=银/gold=金。 */\n readonly tier: SealTier;\n /** 印文那一行(技能/某 HELP/插件)。 */\n readonly label: string;\n readonly onSelect?: (() => void) | undefined;\n}\n\n/** 三角色底座(字号 padding 随角色,材质随档位)。 */\nconst ROLE_BASE: Record<SealRole, React.CSSProperties> = {\n skill: { fontSize: '1.0625em', padding: '0.5em 1.75em', letterSpacing: '.14em', borderRadius: '3px' },\n help: { fontSize: '1.0625em', padding: '0.5em 1em', letterSpacing: '.14em', borderRadius: '3px', color: '#f8f1e2' },\n plugin: { position: 'relative', justifyContent: 'center', fontSize: '1.0625em', padding: '0.5em 2em', letterSpacing: '.18em', borderRadius: '4px', color: '#f8f1e2' },\n};\n\n/** 九档印面(底色加边线都照抄对应那枚印,不许简化)。 */\nconst FACE: Record<SealRole, Record<SealTier, React.CSSProperties>> = {\n skill: {\n copper: { background: 'linear-gradient(180deg,#8a4a28,#6e3418)', color: '#f8f1e2', border: '3px solid #a5652f', boxShadow: 'inset 0 0 0 1px #f8f1e2aa' },\n silver: { background: 'linear-gradient(180deg,#b73124,#8e1f14)', color: '#f8f1e2', border: '3px double #eef3f6', boxShadow: 'inset 0 0 0 1px #fff' },\n gold: { background: 'linear-gradient(180deg,#d34a35,#a32216)', color: '#fff8e0', border: '3px double #f5d97a', boxShadow: 'inset 0 0 0 1px #fff8,0 4px 16px #c9a22755' },\n },\n help: {\n copper: { background: 'linear-gradient(180deg,#b06a3a,#7e3f1d)', border: '3px solid #a5652f' },\n silver: { background: 'linear-gradient(180deg,#8f979e,#5f666d)', border: '3px double #e8eef2' },\n gold: { background: 'linear-gradient(180deg,#c63d2a,#a32216)', border: '3px double #f5d97a' },\n },\n plugin: {\n copper: { background: 'linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)' },\n silver: { background: 'linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)' },\n gold: { background: 'linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)' },\n },\n};\n\n/** 插件整面金属带的描边(随档)。 */\nconst BAND_EDGE: Record<SealTier, React.CSSProperties> = {\n copper: { boxShadow: 'inset 0 1px 0 #d99a5e88,inset 0 -2px 5px #0006,0 0 0 1px #5e2c12' },\n silver: { boxShadow: 'inset 0 1px 0 #ffffffdd,inset 0 -2px 5px #0005,0 0 0 1px #6f767c' },\n gold: { boxShadow: 'inset 0 1px 0 #fff8d8dd,inset 0 -2px 5px #0005,0 0 0 1px #8a6a15,0 3px 14px #c9a22766' },\n};\n\n/** 插件左右铆钉(随档,两颗)。 */\nconst RIVET: Record<SealTier, React.CSSProperties> = {\n copper: { background: 'radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)', boxShadow: 'inset 0 -1px 1px #00000055,0 1px 2px #00000088' },\n silver: { background: 'radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077' },\n gold: { background: 'radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788' },\n};\n\n/** 一枚印章(button,点章弹卷)。未知组合直接抛错,不静默退化。 */\nexport function SealStamp(props: SealStampProps): React.ReactElement {\n const face = FACE[props.role][props.tier];\n if (face === undefined) throw new Error('未知印章组合:' + String(props.role) + '/' + String(props.tier));\n const style: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n cursor: 'pointer',\n fontWeight: 800,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n filter: 'url(#' + SEAL_ROUGH_EDGE_ID + ')',\n ...ROLE_BASE[props.role],\n ...face,\n };\n const buttonProps = { type: 'button' as const, style, 'aria-label': props.label, 'aria-haspopup': 'dialog' as const, onClick: props.onSelect };\n if (props.role !== 'plugin') return React.createElement('button', buttonProps, props.label);\n const band: React.CSSProperties = { position: 'absolute', top: '-1px', bottom: '-1px', left: '-1px', right: '-1px', zIndex: 1, borderRadius: '4px', ...BAND_EDGE[props.tier] };\n const rivet: React.CSSProperties = { position: 'absolute', zIndex: 3, width: '0.5em', height: '0.5em', borderRadius: '50%', top: '50%', transform: 'translateY(-50%)', ...RIVET[props.tier] };\n return React.createElement('button', buttonProps,\n React.createElement('span', { style: { ...band, background: (face.background as string) }, 'data-seal-band': '1' }),\n React.createElement('span', { style: { ...rivet, left: '0.5em' }, 'data-seal-rivet': 'left' }),\n React.createElement('span', { style: { ...rivet, right: '0.5em' }, 'data-seal-rivet': 'right' }),\n React.createElement('span', { style: { position: 'relative', zIndex: 2, textShadow: '0 1px 2px #00000088' } }, props.label),\n );\n}","/** 共用配置面板 · 视图半:一行怎么画(`Row`)、整面怎么画(`PanelBody`)。\n *\n * 本件是**纯函数**:吃一份状态与一组回调,回一棵 React 元素树,自己不留任何状态、不取任何数。\n * 为什么这么分:状态机与副作用住在 `config-panel.ts` 那一层,于是本件在 Node 里就能直测\n * (照 `directory-browser-ui.ts` 的既有作法:用例把组件当普通函数调,直接读回那棵树)。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX)。样式表 `S` 就在本件里——面板怎么画只有这一处。\n *\n * **外观真源=`docs/agents/配置区域视觉模型-v3.1.html`(#920 阶段二起)**:本件的 `S` 逐项照它的\n * CSS(`:32-205`)、行模板照它的 `rowShell`/`masterShell`/`cardShell`(`:349-451`)、状态徽标照它的\n * `badgeHTML`(`:544-552`)那七档。**颜色/间距/圆角/字号/层级一律照取值,不许自己重新设计**;\n * 与本件不同的只有下面这四条(每条都在这里写明为什么,改之前先读 #920 的证据件):\n * ① **字号一律 em,没有 px**:跨包锁 `test/panel-type-739.test.mjs` 钉死「零 `font:` 简写/零字族/\n * 字号一律相对单位」——宿主没有界面字号 token,绝对 px 会在别的窗口尺寸上失真。v3.1 的 px 按它\n * 自己已锁死的那两对锚点换算(`13.5px↔1.08em`、`11.5px↔0.92em` ⇒ **1px=0.08em**),一个不丢。\n * ② **值列不写等宽字族**:v3.1 的值列要 `font-family: var(--ic-mono)`,而 `#739 ①` 明令共用件零\n * `fontFamily`/零 `font:` 简写 ⇒ 值继承宿主字族。这是与 v3.1 的**一处已知不同**(#920 证据件有账)。\n * ③ **换行判据交给 CSS,不按字符数估宽**:v3.1 靠 JS 量文本宽在两套模板间两选一(审查 P0-4/P0-5:\n * 每列 6.048px、真阈值 40–43 列而注释按 34 估 ⇒ 9 行被无谓拆两行、快面一次性长高 194px)。\n * 本件没有 DOM 可量,故照审查给的修法把同一个决定交给排版引擎:行是 `flex-wrap` 容器,标签列定宽、\n * 值列 `flex-basis:auto`(=内容的自然宽)——装不下时**整行让位**(值落第二行、按钮仍在第一行行尾,\n * 与 v3.1 的 `.is-wide` 同形);值再长就在自己那一行里 `overflow-wrap:anywhere` 折下去。\n * **零省略号、零截断**(`white-space` 不用 `nowrap`、`text-overflow` 一次都不出现)。\n * ④ **画不出来的三样**用等价形态顶上:分组箭头 `::before`/`[open]` 旋转/keyframes 动画内联样式\n * 给不出 ⇒ 画静态「›」与静态环(形状与 v3.1 同,只少了动)。\n *\n * 三条定稿(v3)在本件落地:\n * ① **只读行的目录按钮不画**(不画一枚点了也没反应的死按钮);\n * ② 目录行的按钮字面**两档合一**,一律「浏览文件夹」(入口供不了时整枚不画);\n * ③ **每行一枚「复制」**:复制这一行完好的值文本;点下去按钮就地变「已复制」再变回,不弹提示。\n *\n * 对外只经 `config-panel-api.ts` 那一道门;本件自己不出门。\n */\n\nimport * as React from 'react';\nimport { ADVANCED_CHEV_MS, ADVANCED_CHEV_SHIFT_PX, ADVANCED_GROUP_TITLE, ADVANCED_OPEN_MAX_PX, ADVANCED_OPEN_MS, DISABLED_OPACITY, DISABLED_SHAKE_MS, FOCUS_RING_GAP_PX, FOCUS_RING_WIDTH_PX, HOVER_GLOW_EDGE_PERCENT, HOVER_GLOW_SOFT_PERCENT, HOVER_LIFT_PX, HOVER_SPOTLIGHT_RADIUS_PX, HOVER_WASH_PERCENT, INTERACTION_TRANSITION, LIQUID_GHOST_MS, LONGPRESS_MS, LONGPRESS_RING_HEIGHT_PX, NOTICE_FADE_MS, NOTICE_RISE_PX, PRESS_SCALE, SPOTLIGHT_FADE_MS, SPOTLIGHT_PEAK_PERCENT, TAB_DOT_PULSE_MS, TAB_HOVER_WASH_PERCENT, TAB_TRANSITION } from './config-panel-contract.js';\nimport type { ConfigItem, ConfigSurfaceReply } from './config-panel-contract.js';\nimport { DirectoryBrowserFromRow } from './directory-browser-ui.js';\nimport type { DirectoryRowBrowser } from './directory-browser-state.js';\nimport type { DirectoryRowEntry } from './directory-browser-api.js';\nimport { TitleBlock } from './title-seal.js';\nimport { SealStamp } from './seal-stamp.js';\nimport { SealFilterDefs, SealScrollDialog } from './seal-scroll.js';\nimport type { SealInstance } from './config-panel-contract.js';\n\n/** 面板的样式表:取值逐项照 v3.1(颜色走 DSH 主题别名,写死值只做回退;字号一律相对单位 em)。\n *\n * 键名一个都不许改:六家附加块真消费 `okText`/`error`/`rows`/`label`/`muted`/`hint`/`info`/\n * `bar`/`btn` 这九格(`plugin-memo-ilife/src/client.ts`、`plugin-schedule-ilife/src/client.ts` 的\n * `PanelStyleSlots`),跨包锁另钉 `card`/`input`/`btnPrimary`/`btnPick`/`summary`/`title`/`version`。 */\n/** #941/丁:**浅色档距自合成**——宿主在浅色下四层底同白(`bg-base = bg-layer-1/2/3`),\n * 拿不到可用的层级差,所以三处面(卡描边/沉底盒底/输入框描边)由我们自己按墨色派生。\n *\n * 取 `label-primary` 当墨色分母:浅色下它是近黑、深色下它是近白 ⇒ **同一条表达式两套主题都成立**\n * (浅色补出可用的档距,深色退化成今天那几档观感)。百分比来自维护者选定的方案丁:\n * 卡描边 19%/沉底盒底 9%/输入框描边 24%/骨架条 21%。这是本仓第一处\"自定值\",属真源增补\n * (v3.1 只有深色一套 token,从未覆盖浅色)。 */\nconst PANEL_INK = 'var(--dsw-alias-label-primary, #f9fafb)';\nconst inkMix = (percent: number, into?: string): string =>\n into === undefined\n ? `color-mix(in srgb, ${PANEL_INK} ${percent}%, transparent)`\n : `color-mix(in srgb, ${PANEL_INK} ${percent}%, ${into})`;\n/** 卡片描边:浅色 ≈`#cfcfcf` ⇔ 深色 ≈`#4a4b4d`。 */\nconst PANEL_CARD_EDGE = inkMix(19);\n/** 沉底盒底(母版行/高级组体/状态行):浅色 ≈`#e9e9e9` ⇔ 深色 ≈`#3a3b3d`。 */\nconst PANEL_SUNKEN_FILL = inkMix(9, 'var(--dsw-alias-bg-layer-1, #232324)');\n/** 输入框描边:浅色 ≈`#c4c4c4` ⇔ 深色 ≈`#5a5b5d`。 */\nconst PANEL_FIELD_EDGE = inkMix(24);\n/** 骨架条:浅色 ≈`#c9ccd2`(1.6:1)⇔ 深色 ≈`#404143`。 */\nconst PANEL_SKELETON_FILL = inkMix(21);\n\n/** T1 按压与焦点(#1160):原型 V1 的两个真实行为,样式只此一处生成。\n *\n * 用法:按钮加 `data-ilife-press`(按压+焦点双环),输入框加 `data-ilife-focus`\n *(仅焦点双环,文本框不收缩)。`PanelBody` 在卡片首位挂一枚 `<style>`,选择器按属性名收敛、\n * 只罩本卡内的这两类,不漏到宿主别的按钮上。数值全引契约四格(按压/隔离/宽度/过渡),\n * 颜色全走主题变量(隔离环取卡面底,焦点环取 `--ilife-focus` 退品牌色,永不写死蓝)。\n * 悬停/长按/折叠/复制变勾/提示/液态归后票,本函数只出按压与焦点。\n *\n * T2 追加(#1161):悬停聚光+淡洗+辉光+上浮、禁用摇头+禁止指针、长按 500ms 纯视觉环+回弹、\n * 无障碍三约束(触屏降级、键盘焦点可见、减少动态静态替代)——数值全引契约 T2 十格,详见函数内注释。\n *\n * T3 追加(#1162):高级折叠(220ms 分级入场+箭头旋转 200ms+悬停右移 2px)+轻提示渐显\n * (180ms 上浮 8px)+低端机(update: slow)静态兜底——数值全引契约 T3 六格;\n * 液态拉伸影子消散归 #1165(本函数不动页签,页签静态层见 tabInteractionCss)。 */\n// T2 焦点色变量(与 T1 行内字面逐字同值;T1 那一行保持字面不变——像素脚本按源码窗口断言它,改动会误报)。\nconst T2_FOCUS_VAR = 'var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))';\nexport function interactionCss(): string {\n return (\n '[data-ilife-press]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-focus]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-press]:active{transform:scale(' + String(PRESS_SCALE) + ')}' +\n '[data-ilife-press]:focus-visible,[data-ilife-focus]:focus-visible{outline:none;' +\n 'box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))}' +\n '[data-ilife-press]{position:relative;overflow:hidden}' +\n '[data-ilife-press]:hover:not(:disabled){background:color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_WASH_PERCENT) + '%, transparent);' +\n 'border-color:' + T2_FOCUS_VAR + ';' +\n 'box-shadow:0 0 0 1px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_EDGE_PERCENT) + '%, transparent),' +\n '0 4px 16px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_SOFT_PERCENT) + '%, transparent);' +\n 'transform:translateY(-' + String(HOVER_LIFT_PX) + 'px)}' +\n '[data-ilife-press]:active:hover:not(:disabled){transform:scale(' + String(PRESS_SCALE) + ')}' +\n '[data-ilife-press]:focus-visible:hover:not(:disabled){box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px ' + T2_FOCUS_VAR + ',' +\n '0 4px 16px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_SOFT_PERCENT) + '%, transparent)}' +\n '[data-ilife-press]::before{content:\"\";position:absolute;inset:0;' +\n 'background:radial-gradient(' + String(HOVER_SPOTLIGHT_RADIUS_PX) + 'px circle at var(--mx,50%) var(--my,50%),' +\n 'color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(SPOTLIGHT_PEAK_PERCENT) + '%, transparent),transparent 65%);' +\n 'opacity:0;transition:opacity ' + String(SPOTLIGHT_FADE_MS / 1000) + 's ease;pointer-events:none}' +\n '[data-ilife-press]:hover:not(:disabled)::before{opacity:1}' +\n '[data-ilife-press]:disabled{cursor:not-allowed;opacity:' + String(DISABLED_OPACITY) + '}' +\n '[data-ilife-press]:disabled:hover{animation:ilifeShake ' + String(DISABLED_SHAKE_MS / 1000) + 's ease}' +\n '@keyframes ilifeShake{0%,100%{transform:translateX(0)}25%{transform:translateX(-2px)}75%{transform:translateX(2px)}}' +\n '[data-ilife-longpress]{position:relative;overflow:hidden}' +\n '[data-ilife-longpress]::after{content:\"\";position:absolute;left:0;bottom:0;height:' + String(LONGPRESS_RING_HEIGHT_PX) + 'px;width:0;' +\n 'background:' + T2_FOCUS_VAR + ';pointer-events:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:ilifeLongRing ' + String(LONGPRESS_MS / 1000) + 's linear forwards}' +\n '@keyframes ilifeLongRing{from{width:0}to{width:100%}}' +\n '[data-ilife-advanced=\"head\"]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-advanced=\"mark\"]{display:inline-block;transition:transform ' + String(ADVANCED_CHEV_MS / 1000) + 's ease}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"mark\"]{transform:rotate(90deg)}' +\n '[data-ilife-advanced=\"head\"]:hover [data-ilife-advanced=\"mark\"]{color:' + T2_FOCUS_VAR + ';transform:translateX(' + String(ADVANCED_CHEV_SHIFT_PX) + 'px)}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:ilifeFoldIn ' + String(ADVANCED_OPEN_MS / 1000) + 's ease}' +\n '@keyframes ilifeFoldIn{from{max-height:0;opacity:0}to{max-height:' + String(ADVANCED_OPEN_MAX_PX) + 'px;opacity:1}}' +\n '[data-ilife-notice=\"line\"]{animation:ilifeToastIn ' + String(NOTICE_FADE_MS / 1000) + 's ease}' +\n '@keyframes ilifeToastIn{from{opacity:0;transform:translateY(' + String(NOTICE_RISE_PX) + 'px)}to{opacity:1;transform:none}}' +\n '@media (hover:none){[data-ilife-press]:hover:not(:disabled){transform:none}[data-ilife-press]::before{display:none}}' +\n '@media (prefers-reduced-motion:reduce){[data-ilife-press],[data-ilife-focus]{transition:none}' +\n '[data-ilife-press]:disabled:hover{animation:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:none;width:100%}' +\n '[data-ilife-advanced=\"mark\"]{transition:none}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:none}' +\n '[data-ilife-notice=\"line\"]{animation:none}}' +\n '@media (update: slow){[data-ilife-press],[data-ilife-focus]{transition:none}' +\n '[data-ilife-press]::before{display:none}' +\n '[data-ilife-press]:disabled:hover{animation:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:none;width:100%}' +\n '[data-ilife-advanced=\"mark\"]{transition:none}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:none}' +\n '[data-ilife-notice=\"line\"]{animation:none}}'\n );\n}\n\n/** T6 页签条交互(#1165,唯一生成处;载体 `client.ts` 页签条消费,不自画)。\n *\n * 原型 V1 页签四件:悬停淡洗 12%+辉光边框(无上浮、无聚光,按钮那套不串用)、圆点呼吸 .8s\n *(悬停驱动,scale 1→1.5/opacity 1→.6)、焦点双环(沿用 T1 2px+5px,跟主题色,同色页签可见)、\n * 选中态透明(glider 在下层给 pill,页签自身透明;减少动态时 CSS 以 !important 回实心 pill)。\n * 液态彗星式由载体 `liquidTo` 驱动(拉伸+影子消散+时长公式见契约 T6 tokens),本函数只出静态层:\n * glider-layer 定位+goo 滤镜引用、ghost 影子过渡(180ms)、glider/ghost 底色(主题变量)。\n * 数值全引契约 T6 四格(淡洗/呼吸/过渡/影子),颜色永不写死蓝(缺席退橙 #f6ad55)。\n * 低端机模糊降级(#1162):update: slow 下 glider 层(含 goo 模糊)关闭、选中态回实心 pill(与减少动态同形)。 */\nexport function tabInteractionCss(): string {\n return (\n '[data-ilife-tablist]{position:relative;isolation:isolate}' +\n '[data-ilife-glider-layer]{position:absolute;inset:0;z-index:0;filter:url(#ilife-goo);pointer-events:none}' +\n '[data-ilife-glider],[data-ilife-ghost]{position:absolute;top:0;left:0;border-radius:999px;background:' + T2_FOCUS_VAR + '}' +\n '[data-ilife-ghost]{opacity:0}' +\n '[data-ilife-tab]{position:relative;z-index:1;transition:' + TAB_TRANSITION + '}' +\n '[data-ilife-tab]:hover{background:color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(TAB_HOVER_WASH_PERCENT) + '%, transparent);' +\n 'border-color:' + T2_FOCUS_VAR + '}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:ilifeDotPulse ' + String(TAB_DOT_PULSE_MS / 1000) + 's ease infinite}' +\n '@keyframes ilifeDotPulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.5);opacity:.6}}' +\n '[data-ilife-tab]:focus-visible{outline:none;' +\n 'box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px ' + T2_FOCUS_VAR + '}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:transparent;border-color:transparent}' +\n '[data-ilife-ghost]{transition:opacity ' + String(LIQUID_GHOST_MS / 1000) + 's ease}' +\n '@media (hover:none){[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}}' +\n '@media (prefers-reduced-motion:reduce){[data-ilife-tablist] [data-ilife-glider-layer]{display:none}' +\n '[data-ilife-tab]{transition:none}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))!important;border-color:transparent!important}}' +\n '@media (update: slow){[data-ilife-tablist] [data-ilife-glider-layer]{display:none}' +\n '[data-ilife-tab]{transition:none}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))!important;border-color:transparent!important}}'\n );\n}\n\nconst S = {\n /** `.ic-card`:`background:var(--ic-surface)`(=bg-layer-1)+`border:1px solid var(--ic-line-strong)`\n * +`border-radius:12px`+`padding:14px`+`box-shadow:inset 0 1px 0 rgba(255,255,255,.06)`。\n * 基准字号**不钉在卡片上**(#739 ⑤:跟着宿主走)。 */\n card: {\n position: 'relative',\n padding: 14,\n borderRadius: 12,\n // #941:卡片的边界在浅色下原本等于没有(四层底同白)⇒ 改成按墨色派生的可见描边。\n border: '1px solid ' + PANEL_CARD_EDGE,\n background: 'var(--dsw-alias-bg-layer-1, #232324)',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.06)',\n } as React.CSSProperties,\n /** `.ic-head{display:flex;align-items:center;gap:10px;min-height:22px}`(徽标靠 `margin-left:auto` 右贴)。\n * #996:加 `flexWrap:wrap`——标题(`TitleBlock`,`flex:1 1 auto`+`minWidth:9em`)与签\n * (`flex-shrink:0`)在 390 宽下换行不挤(原型 A 行尾悬签);`gap`/`minHeight` 照 v3.1 不动。 */\n head: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 10, minHeight: 22 } as React.CSSProperties,\n /** 印覆盖层(1158 红框区):卡片右上绝对区,只罩标题+头两行(底栏永远罩不到);本身镂空穿透,印各自可点、缝隙漏过。 */\n sealOverlay: { position: 'absolute', top: 0, right: 0, width: '38%', height: '9em', pointerEvents: 'none', zIndex: 5 } as React.CSSProperties,\n /** `.ic-title{font-size:13.5px;font-weight:640}`——字号按 60 行那条锚点换算成 `1.08em`。\n * #996:卡片标题行改用 `TitleBlock`(见 `title-seal.ts`,原型 A 锁 1.15em/700+0.85em/400),\n * 这一格保留供附加块(`ConfigStyles` 键名不许改),头部本身不再用它。 */\n title: { fontSize: '1.08em', fontWeight: 640 } as React.CSSProperties,\n /** `.ic-file{margin-top:4px}` 的那 4px 挂在两行的公共容器上:`info` 自己一格 margin 都不写\n * (#743 ①:头部两行间距不一家一个数,故这一项一律不写 margin)。 */\n fileWrap: { marginTop: 4 } as React.CSSProperties,\n /** #939:骨架条(照真源 `.ic-sk`:`display:block;height:17px;border-radius:4px`)。\n * 高度按一行正文画——inline-block 跟着父级行高走会让骨架行比填充后的文字行高 4px、六家叠起来就是一片下移。\n * 底色走派生值(宿主专用骨架档 `bg-skeleton` 在浅色下只有 1.09:1,等于看不见)。 */\n skeleton: {\n display: 'block',\n height: 17,\n borderRadius: 4,\n background: PANEL_SKELETON_FILL,\n } as React.CSSProperties,\n /** #936:状态行那一块(照真源 `.ic-status`)——沉底面盒子,里面三行:状态行/路径行/官网行。 */\n statusBox: {\n marginTop: 11,\n padding: '10px 12px',\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n } as React.CSSProperties,\n /** `.ic-srow{display:flex;align-items:center;gap:8px;font-size:12px;flex-wrap:wrap}`(12px ⇒ 0.86em)。 */\n statusRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n fontSize: '0.86em',\n flexWrap: 'wrap',\n } as React.CSSProperties,\n /** `.ic-dot{width:8px;height:8px;border-radius:50%}`——底色按档位在组件里补。 */\n statusDot: { width: 8, height: 8, borderRadius: '50%', flex: '0 0 auto' } as React.CSSProperties,\n /** `.ic-sname{font-weight:600;white-space:nowrap}`。 */\n statusName: { fontWeight: 600, whiteSpace: 'nowrap' } as React.CSSProperties,\n /** `.ic-stxt{color:var(--ic-muted);min-width:0}`。 */\n statusText: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)', minWidth: 0 } as React.CSSProperties,\n /** `.ic-sfind{display:flex;align-items:center;gap:8px;margin-top:7px}`。 */\n statusFind: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 7 } as React.CSSProperties,\n /** `.ic-spath{font-size:10.5px;color:var(--ic-faint);white-space:nowrap;overflow:hidden}`\n * ——单行不折、超出裁掉(**不写省略号**:本件有一条\"零省略号\"的跨包锁)。\n * **不写 `fontFamily`**:v3.1 这一格要 `var(--ic-mono)`,但 `#739 ①` 明令共用件零字族(宿主没有界面字族\n * token,钉死会在别的窗口尺寸上失真)——与真源的同一处已知不同,账在 #920 证据件里。 */\n statusPath: {\n fontSize: '0.75em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n minWidth: 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n } as React.CSSProperties,\n /** `.ic-ver{font-size:10px;border:1px solid var(--ic-line-strong);border-radius:999px;padding:0 6px}`。 */\n statusVersion: {\n fontSize: '0.72em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 999,\n padding: '0 6px',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-url{display:inline-block;margin-top:7px;font-size:10.5px;color:var(--ic-faint)}`\n * ——字族同样继承宿主(`#739 ①`:共用件零 `fontFamily`),理由同上一格。 */\n statusUrl: {\n display: 'inline-block',\n marginTop: 7,\n fontSize: '0.75em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n } as React.CSSProperties,\n /** `.ic-file b{color:var(--ic-faint);font-weight:400;margin-right:5px}`(「配置文件」「数据目录」那两个字)。 */\n fileLabel: {\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n fontWeight: 400,\n marginRight: 5,\n } as React.CSSProperties,\n /** `.ic-file{color:var(--ic-muted);font-size:11.5px;word-break:break-all}`(11.5px ⇒ `0.92em`)。 */\n info: {\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n fontSize: '0.92em',\n wordBreak: 'break-all',\n } as React.CSSProperties,\n /** `.ic-muted` 那一档(次要字)。 */\n muted: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)', fontSize: '0.92em' } as React.CSSProperties,\n /** 错误字:取 v3.1 的 `--ic-danger`;字号 `1em`(#739 钉死)。 */\n error: {\n marginTop: 8,\n color: 'var(--dsw-alias-state-error-primary, #e0685f)',\n fontSize: '1em',\n whiteSpace: 'pre-wrap',\n } as React.CSSProperties,\n /** 成功字:取 v3.1 的 `--ic-ok`;字号 `0.96em`(#739 钉死)。 */\n okText: {\n marginTop: 8,\n color: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n fontSize: '0.96em',\n } as React.CSSProperties,\n /** 行组的容器(六家附加块也拿它当外框;v3.1 没有对应类,故只给一个中性块)。 */\n rows: { display: 'block' } as React.CSSProperties,\n /** `.ic-row`:三列栅格 + `gap:4px 10px` + `padding:7px 0` + 一条 `.ic-line` 下边线。\n * 本件把它落成**换行弹性盒**(见文件头 ③):标签定宽在第一行,值装不下时整行让位,\n * 动作槽绝对定位贴在行尾(`.ic-row .ic-slot[data-slot^=\"a:\"]` 那一格)。 */\n row: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0',\n borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n } as React.CSSProperties,\n /** `.ic-row:last-child{border-bottom:0}`——内联样式没有 `:last-child`,由画它的人明说。 */\n rowLast: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0',\n } as React.CSSProperties,\n /** `.ic-under{margin:9px 0 0 8px;border-left:1px solid var(--ic-line-strong);padding-left:13px}`:\n * **跟随行**相对它的可改行(母版行)缩进 8+1+13=**22px**,左边挂一条线。\n *\n * 取值逐字照抄;只有一处形态差别要说清:`.ic-under` 在 v3.1 里是**一整块**(同一个可改行的一批\n * 跟随者共用一个 div、共一条左边线、块首还有 9px 上边距),而共用件手上只有「谁是跟随行」这一个\n * 集合(由六家交的 `followKeysOf` 钩子逐键探出),**没有**「这一行归哪一块」的分块归属\n * (同一个键可以同时挂在两张跟随表上,钩子只回一个去重后的并集)。故这里按**行**落这条缩进:\n * 缩进量与左边线同值,块首那 9px 上边距不落(落下去会在每两行之间多出一道 9px 的缝)。 */\n followerRow: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0 7px 13px',\n marginLeft: 8,\n borderLeft: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n } as React.CSSProperties,\n /** 跟随行的最后一行:同上,去掉下边线(照 `.ic-row:last-child{border-bottom:0}`)。 */\n followerRowLast: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0 7px 13px',\n marginLeft: 8,\n borderLeft: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n } as React.CSSProperties,\n /** `.ic-row .ic-k{font-size:11.5px;color:var(--ic-muted)}`;`--ic-kw:118px` 是标签列的定宽。 */\n rowKey: {\n flex: '0 0 auto',\n minWidth: 118,\n fontSize: '0.92em',\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n } as React.CSSProperties,\n /** `.ic-row .ic-v{font-size:11px;min-width:0}`(11px ⇒ `0.88em`)+ 文件头 ③ 的让位口径:\n * `flex:1 1 auto` ⇒ 装不下整行让位;`overflow-wrap:anywhere` ⇒ 折下去、零省略号;\n * `paddingRight` 给行尾那枚按钮(+列间距)让出宽度——就是 `.ic-row` 第三列那一格。 */\n rowValue: {\n flex: '1 1 auto',\n minWidth: 0,\n paddingRight: 56,\n fontSize: '0.88em',\n whiteSpace: 'normal',\n overflowWrap: 'anywhere',\n lineHeight: 1.5,\n } as React.CSSProperties,\n /** 行尾动作槽(复制/跟随标记):绝对定位贴行尾,行一换行它仍留在第一行行尾(=`.is-wide` 那一形)。 */\n rowActs: {\n position: 'absolute',\n right: 0,\n top: 7,\n display: 'flex',\n gap: 4,\n alignItems: 'center',\n } as React.CSSProperties,\n /** `.ic-follow{font-size:11px;color:var(--ic-attn);white-space:nowrap}`(11px ⇒ `0.88em`)。 */\n follow: {\n fontSize: '0.88em',\n color: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n whiteSpace: 'nowrap',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n } as React.CSSProperties,\n /** `.ic-follow::before{content:'↳';opacity:.8}`——内联样式给不出伪元素,画成真的那一个字符。 */\n followMark: { opacity: 0.8 } as React.CSSProperties,\n /** `.ic-master`:母版行那一块盒子(`--ic-sunken` 底 + `border-radius:8px` + `padding:11px 12px`\n * + `margin-top:11px` + 内高光)。 */\n master: {\n // #941:沉底盒的底改成按墨色派生——浅色下 `bg-layer-3` 与卡面同白(盒子看不见),派生值给出一档可见的面差。\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n padding: '11px 12px',\n marginTop: 11,\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.05)',\n } as React.CSSProperties,\n /** `.ic-master .ic-lab{font-weight:600;display:flex;align-items:center;gap:7px}`;\n * 字号**不写**(#739 ④:`label` 这一项该继承宿主字号)。 */\n label: { display: 'flex', alignItems: 'center', gap: 7, fontWeight: 600 } as React.CSSProperties,\n /** `.ic-chip{font-size:10.5px;border:1px solid var(--ic-accent);border-radius:999px;padding:0 6px}`(10.5px ⇒ `0.84em`)。 */\n chip: {\n fontSize: '0.84em',\n border: '1px solid var(--dsw-alias-brand-primary, #f9fafb)',\n borderRadius: 999,\n padding: '0 6px',\n color: 'var(--dsw-alias-brand-primary, #f9fafb)',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-master .ic-hint{color:var(--ic-muted);font-size:11.5px;margin-top:3px;line-height:1.5}`。 */\n hint: {\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n fontSize: '0.92em',\n marginTop: 3,\n lineHeight: 1.5,\n } as React.CSSProperties,\n /** `.ic-master input`:整行宽、`margin-top:7px`、`padding:5px 8px`、`--ic-bg` 底、`--ic-line-strong` 描边、\n * `border-radius:6px`。字号与字族**不写**(#739 ④/①:`input` 继承宿主)。 */\n input: {\n display: 'block',\n width: '100%',\n marginTop: 7,\n padding: '5px 8px',\n boxSizing: 'border-box',\n background: 'var(--dsw-alias-bg-base, #151517)',\n // #932/#941:描边换成按墨色派生的可见档——原 `border-l2` 在浅色下只有 1.26:1,等于没有边界\n //(维护者判\"不够明显\"),派生值给到 ≈1.6:1;深色下比原先略亮一档。\n border: '1px solid ' + PANEL_FIELD_EDGE,\n borderRadius: 6,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** `.ic-master input.is-dirty`:`border:2px solid var(--ic-attn)` + `padding:4px 7px`\n * + `box-shadow:0 0 0 3px <琥珀 22%>`;2px 描边与 5→4 的内边距对消,宽高不变\n * (审查「站得住」第 7 条)。 */\n inputDirty: {\n display: 'block',\n width: '100%',\n marginTop: 7,\n padding: '4px 7px',\n boxSizing: 'border-box',\n background: 'var(--dsw-alias-bg-base, #151517)',\n border: '2px solid var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderRadius: 6,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n boxShadow: '0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 22%, transparent)',\n } as React.CSSProperties,\n /** 布尔档那枚方框(v3.1 的母版行只有文本框,这一格按输入框的同一套外边距对齐)。 */\n checkbox: { marginTop: 7 } as React.CSSProperties,\n /** `.ic-acts{display:flex;gap:5px;margin-top:7px;flex-wrap:wrap;align-items:center}`(母版行的按钮行)。 */\n acts: { display: 'flex', gap: 5, marginTop: 7, flexWrap: 'wrap', alignItems: 'center' } as React.CSSProperties,\n /** `.ic-btn.is-mini{font-size:11px;padding:2px 7px;color:var(--ic-muted)}`——`btnPick` 是锁钉死的那一格,\n * 字号**不写**(#739 ④:该继承宿主字号),形状照 v3.1。 */\n btnPick: {\n padding: '2px 7px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-invalid{margin-top:7px;padding:7px 9px;border-radius:6px;font-size:11.5px;line-height:1.5;\n * background:color-mix(in srgb, var(--ic-attn) 16%, transparent);border:1px solid var(--ic-attn)}`。 */\n invalid: {\n marginTop: 7,\n padding: '7px 9px',\n borderRadius: 6,\n fontSize: '0.92em',\n lineHeight: 1.5,\n background: 'color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 16%, transparent)',\n border: '1px solid var(--dsw-alias-state-warn-primary, #e0a33e)',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** `.ic-group{margin-top:14px}`(高级组默认收起)。 */\n advanced: { marginTop: 14 } as React.CSSProperties,\n /** `.ic-group > .ic-ghead{display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;\n * font-weight:600;padding:2px 0}`——字号**不写**(#739 ④:`summary` 该继承宿主字号)。 */\n summary: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n cursor: 'pointer',\n listStyle: 'none',\n fontWeight: 600,\n padding: '2px 0',\n } as React.CSSProperties,\n /** `.ic-ghead::before{content:'›';color:var(--ic-muted)}`——画成真的那一个字符(内联样式给不出伪元素)。 */\n groupMark: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)' } as React.CSSProperties,\n /** #934:`.ic-gnote` 那条副文案的样式随之删除——副文案本身已按维护者裁定整条撤掉(零消费者的槽位不留)。 */\n /** `.ic-group > .ic-gbody{margin-top:9px;padding:11px 12px;background:var(--ic-sunken);\n * border-radius:8px;box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}`。 */\n groupBody: {\n marginTop: 9,\n padding: '11px 12px',\n // #941:同母版行——沉底面走派生值(浅色下 `bg-layer-3` 与卡面同白)。\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.04)',\n } as React.CSSProperties,\n /** `.ic-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:14px;padding-top:12px;\n * border-top:1px solid var(--ic-line)}` + **贴下沿吸住**(v3.1 的 `.ic-attnbar.is-sticky` 是它唯一的\n * 吸底口径:`position:sticky;bottom:6px;z-index:3`;本票按票面「贴下沿」取 0,好让底栏与下沿齐平)。 */\n bar: {\n display: 'flex',\n gap: 8,\n alignItems: 'center',\n flexWrap: 'wrap',\n marginTop: 14,\n paddingTop: 12,\n borderTop: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n position: 'sticky',\n bottom: 0,\n zIndex: 3,\n background: 'var(--dsw-alias-bg-layer-1, #232324)',\n } as React.CSSProperties,\n /** `.ic-bar .ic-note{margin-left:auto;color:var(--ic-faint);font-size:11.5px}`(就绪态那半句)。 */\n barNote: {\n marginLeft: 'auto',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n fontSize: '0.92em',\n } as React.CSSProperties,\n /** 有改动时底栏那半句(黄字,粘在按钮组左侧)。 */\n saveMsg: {\n marginLeft: 'auto',\n color: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontSize: '0.92em',\n } as React.CSSProperties,\n /** `.ic-btn{font-size:12px;padding:4px 10px;border-radius:6px;border:1px solid var(--ic-line-strong);\n * background:transparent;color:var(--ic-text)}`——字号**不写**(#739 ④)。 */\n btn: {\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-btn.is-primary{background:var(--ic-accent);border-color:var(--ic-accent);color:var(--ic-accent-ink);\n * font-weight:500}`——`--ic-accent` 取 `--dsw-alias-brand-primary`,前景取 `label-primary-foreground`。 */\n btnPrimary: {\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-brand-primary, #f9fafb)',\n background: 'var(--dsw-alias-brand-primary, #f9fafb)',\n color: 'var(--dsw-alias-label-primary-foreground, #0f1115)',\n fontWeight: 500,\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** #934:原先这里有一格 `version`(各家附加块的版本行字面)。版本行已按维护者裁定整条撤掉\n *(那两家面板内的渲染点 + 六家各自那份字面副本),这一格随之删除:没人取的槽位不留。 */\n /** `.ic-badge{font-size:11px;line-height:1;padding:4px 9px;border-radius:999px;white-space:nowrap;\n * display:inline-flex;align-items:center;gap:6px;border:1px solid transparent}`(11px ⇒ `0.88em`)。 */\n badge: {\n fontSize: '0.88em',\n lineHeight: 1,\n padding: '4px 9px',\n borderRadius: 999,\n whiteSpace: 'nowrap',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n border: '1px solid transparent',\n } as React.CSSProperties,\n /** `.ic-badge .ic-bd{width:6px;height:6px;border-radius:50%;background:currentColor}`。 */\n badgeDot: { width: 6, height: 6, borderRadius: '50%', background: 'currentColor' } as React.CSSProperties,\n /** `.ic-spin{width:11px;height:11px;border-radius:50%;border:1.5px solid rgba(0,0,0,.25);\n * border-top-color:currentColor}`——旋转那半要 `@keyframes`(内联样式给不出),故只画环。 */\n badgeSpinner: {\n width: 11,\n height: 11,\n borderRadius: '50%',\n border: '1.5px solid rgba(0,0,0,.25)',\n borderTopColor: 'currentColor',\n display: 'inline-block',\n } as React.CSSProperties,\n /** `.ic-badge.is-dirty`:`--ic-attn` 实底 + `--ic-attn-ink` 深字(那 3:1 是设计专门解过的对比度)。 */\n badgeDirty: {\n color: '#241703',\n background: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderColor: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontWeight: 600,\n } as React.CSSProperties,\n /** `.ic-badge.is-busy`:与 dirty 同色(写的时候还在那一档上)。 */\n badgeBusy: {\n color: '#241703',\n background: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderColor: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontWeight: 600,\n } as React.CSSProperties,\n /** `.ic-badge.is-ok{color:var(--ic-ok);border-color:var(--ic-ok);background:color-mix(in srgb, var(--ic-ok) 14%, transparent)}`。 */\n badgeOk: {\n color: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n borderColor: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n background: 'color-mix(in srgb, var(--dsw-alias-state-success-primary, #4ec9a0) 14%, transparent)',\n } as React.CSSProperties,\n /** `.ic-badge.is-bad{color:var(--ic-danger-ink);background:var(--ic-danger);border-color:var(--ic-danger)}`。 */\n badgeBad: {\n color: '#fff5f4',\n background: 'var(--dsw-alias-state-error-primary, #e0685f)',\n borderColor: 'var(--dsw-alias-state-error-primary, #e0685f)',\n fontWeight: 600,\n } as React.CSSProperties,\n};\n\n/** 样式表的形状(附加块照面板的视觉写,不各写一套内联样式)。 */\nexport type ConfigStyles = typeof S;\n\n/** 附加块拿到的那两格。 */\nexport interface PanelParts {\n /** 面板自己的样式表。 */\n readonly styles: ConfigStyles;\n /** 配置面整面回执;还没读到(读取中/读取失败)时为 null。 */\n readonly reply: ConfigSurfaceReply | null;\n}\n\n/** 一行点「复制」之后就地显示成哪一档字面。 */\nexport type CopyState = 'idle' | 'done' | 'failed';\n\n/** 复制那一枚按钮的字面(定稿 v3 第三条:就地变,不弹提示)。 */\nexport function copyLabelOf(state: CopyState | undefined): string {\n if (state === 'done') return '已复制';\n if (state === 'failed') return '复制失败';\n return '复制';\n}\n\n/** 面板状态徽标那七档(v3.1 的 `SKILL_STATES` 与 `badgeHTML` 逐档对应)。\n *\n * 七档的落点各不相同:`readFailed` 不画徽标——它把整面换成「一整屏错误 + 恰两枚按钮」那一支;\n * `ready`(就绪)也不画(v3.1 的 `badgeHTML` 在就绪态回空串);其余五档画在卡片标题行右端。 */\nexport type PanelBadge = 'ready' | 'dirty' | 'busy' | 'saved' | 'invalid' | 'writeFailed' | 'readFailed';\n\n/** 算徽标要看的几格事实(全是面板已经拿到的东西,不另取数)。 */\nexport interface BadgeFacts {\n readonly readFailed: boolean;\n readonly busy: boolean;\n readonly writeFailed: boolean;\n /** 技能侧报出「配置里有值用不了」这一事实(#915 第二步落地前恒为 false,见票面「遗留出口」)。 */\n readonly invalid: boolean;\n readonly dirtyCount: number;\n readonly saved: boolean;\n}\n\n/** 状态 → 徽标那一档(v3.1 `paint()` 里的先后次序:读取失败/写入中/写入失败/值非法/有改动/已保存/就绪)。 */\nexport function panelBadgeOf(facts: BadgeFacts): PanelBadge {\n if (facts.readFailed) return 'readFailed';\n if (facts.busy) return 'busy';\n if (facts.writeFailed) return 'writeFailed';\n if (facts.invalid) return 'invalid';\n if (facts.dirtyCount > 0) return 'dirty';\n if (facts.saved) return 'saved';\n return 'ready';\n}\n\n/** 徽标那一档画的字(v3.1 `badgeHTML` 的字面,只把「已保存 · 刚刚」的演示时刻换成档名)。 */\nexport function badgeTextOf(kind: PanelBadge, dirtyCount: number): string {\n if (kind === 'dirty') return `${dirtyCount} 项未保存`;\n if (kind === 'busy') return '正在写入';\n if (kind === 'saved') return '已保存';\n if (kind === 'invalid') return '配置里有值用不了';\n if (kind === 'writeFailed') return '写入失败';\n return '';\n}\n\n/** 徽标那一档的底色(v3.1 的四条 `.ic-badge.is-*`)。 */\nfunction badgeStyleOf(kind: PanelBadge): React.CSSProperties {\n if (kind === 'dirty' || kind === 'invalid') return { ...S.badge, ...S.badgeDirty };\n if (kind === 'busy') return { ...S.badge, ...S.badgeBusy };\n if (kind === 'saved') return { ...S.badge, ...S.badgeOk };\n if (kind === 'writeFailed') return { ...S.badge, ...S.badgeBad };\n return S.badge;\n}\n\n/** 画卡片标题行右端那枚状态徽标(就绪态与读取失败那一支不画)。 */\nexport function PanelBadgeView(props: { readonly kind: PanelBadge; readonly dirtyCount: number }): React.ReactElement | null {\n const text = badgeTextOf(props.kind, props.dirtyCount);\n if (text === '') return null;\n return React.createElement(\n 'span',\n { style: badgeStyleOf(props.kind) },\n props.kind === 'busy'\n ? React.createElement('span', { style: S.badgeSpinner })\n : React.createElement('span', { style: S.badgeDot }),\n text,\n );\n}\n\nexport interface RowProps {\n readonly item: ConfigItem;\n readonly value: string;\n readonly disabled: boolean;\n readonly onChange: (key: string, next: string) => void;\n /** 目录行才有:入口三态(`native`/`browse`/`none`)。 */\n readonly browser?: DirectoryRowEntry | null | undefined;\n /** 脏行:这一行与刚读到的整面不一致时画标记。 */\n readonly dirty?: boolean;\n /** 跟随行:只读派生行在触发键变脏时给「将跟随更新」态。 */\n readonly follow?: boolean;\n /** 点这一行的「复制」:把该行的值文本交出去。\n * 定稿 v3 要求每行一枚,所以按钮**一律画**;这一格缺席时按钮 `disabled` 而不是点了就炸\n * (面板那边永远给这一格,缺席只可能是别的调用点少交了东西)。 */\n readonly onCopy?: ((key: string, text: string) => void) | undefined;\n /** 复制那一枚按钮当刻的字面(不给=「复制」)。 */\n readonly copyState?: CopyState | undefined;\n /** 技能侧报出的「这一行真用不了」(#915 第二步):给了就原样画 `message`,不比较、不合成。\n * 缺席=这一行没问题,什么都不画。 */\n readonly alert?: { readonly code: string; readonly message: string } | null | undefined;\n /** 这一行是所在那一组的最后一行(v3.1 的 `.ic-row:last-child{border-bottom:0}`;\n * 内联样式没有 `:last-child`,行序是客户端常量,故由画整面的人算好交进来)。 */\n readonly last?: boolean | undefined;\n /** **跟随行**:这一行跟着某个可改行变(静态关系,与当刻脏不脏无关)⇒ 照 v3.1 的 `.ic-under`\n * 相对那个可改行缩进 22px、左边挂一条线。只读行才用得上(可改行自己是母版行,不缩进)。 */\n readonly nested?: boolean | undefined;\n}\n\n/** 一行怎么画(照 v3.1 的两套行模板):\n *\n * **只读行**(`.ic-row`)=标签—值同行、值装不下整行让位、行尾一枚「复制」——**不画输入框**\n * (值来自技能侧解析,页面上本来就不给改),也不画那行说明(v3.1 的 `rowShell` 里没有它)。\n * **可改行**(`.ic-master`)=标签 +「可改」小徽标 + 说明 + 整行输入框 + 按钮行。\n *\n * 目录档=文本框 + 一枚唤起目录选择的按钮;入口三态是 `none`(命名空间拿不到、或这条路已被拒)\n * **就不画按钮**,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。\n * 只读行:目录入口**一枚都不画**(定稿 v3 ①)。 */\nexport function Row(props: RowProps): React.ReactElement {\n const { item } = props;\n const readonly = item.readonly === true;\n const disabled = props.disabled || readonly;\n const copy = React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled: props.onCopy === undefined,\n // #1160 T1:按压收缩+焦点双环(纯视觉,不进状态机;禁用态浏览器不触发 :active)。\n // #1161 T2:长按 500ms 纯视觉环(只做视觉标记,不注册任何动作回调、不进状态机分支)。\n 'data-ilife-press': 'copy',\n 'data-ilife-longpress': 'copy',\n onClick: () => props.onCopy?.(item.key, props.value),\n },\n copyLabelOf(props.copyState),\n );\n const follow =\n props.follow === true\n ? React.createElement(\n 'span',\n { style: S.follow },\n React.createElement('span', { style: S.followMark }, '↳'),\n '将跟随更新',\n )\n : null;\n\n if (readonly) {\n /** 跟随行落 `.ic-under` 那一档缩进;其余按是不是本组最后一行挑有没有下边线。 */\n const rowStyle =\n props.nested === true ? (props.last === true ? S.followerRowLast : S.followerRow) : props.last === true ? S.rowLast : S.row;\n return React.createElement(\n 'div',\n { style: rowStyle },\n React.createElement('span', { style: S.rowKey }, item.title),\n React.createElement('span', { style: S.rowValue }, props.value),\n React.createElement('span', { style: S.rowActs }, follow, copy),\n );\n }\n\n const onChange = (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.value);\n const control =\n item.control === 'switch'\n ? React.createElement('input', {\n type: 'checkbox',\n style: S.checkbox,\n checked: props.value === 'true',\n disabled,\n // #1160 T1:键盘焦点双环(文本框不收缩,只给焦点)。\n 'data-ilife-focus': 'input',\n onChange: (e: React.ChangeEvent<HTMLInputElement>) =>\n props.onChange(item.key, e.target.checked ? 'true' : 'false'),\n })\n : React.createElement('input', {\n style: props.dirty === true ? S.inputDirty : S.input,\n type: item.control === 'number' ? 'number' : 'text',\n value: props.value,\n disabled,\n spellCheck: false,\n // #1160 T1:键盘焦点双环(文本框不收缩,只给焦点)。\n 'data-ilife-focus': 'input',\n onChange,\n });\n /** 目录入口:只读行不画(定稿 v3 ①),供不了(`none`/缺席)也不画。 */\n const entry = props.browser ?? null;\n const browse =\n item.control === 'directory' && entry !== null && entry.mode !== 'none'\n ? React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled,\n // #1160 T1:按压收缩+焦点双环(只读行不画本枚,见定稿 v3 ①)。\n // #1161 T2:长按纯视觉环(同上)。\n 'data-ilife-press': 'browse',\n 'data-ilife-longpress': 'browse',\n // 回这枚 Promise 是有意的:React 不看 onClick 的返回值,而用例能直接 await 它。\n onClick: () => entry.onOpen(item.key),\n },\n '浏览文件夹',\n )\n : null;\n return React.createElement(\n 'div',\n { style: S.master },\n React.createElement(\n 'div',\n { style: S.label },\n item.title,\n React.createElement('span', { style: S.chip }, '可改'),\n ),\n React.createElement('div', { style: S.hint }, item.hint),\n control,\n React.createElement('div', { style: S.acts }, browse, copy),\n props.alert == null\n ? null\n : React.createElement('div', { style: S.invalid }, props.alert.message),\n );\n}\n\n/** 面板状态:读取中/就绪/整面读取失败。 */\nexport type PanelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly surface: ConfigSurfaceReply }\n | { readonly kind: 'failed'; readonly message: string };\n\n/** 整面的全部输入:状态 + 行表 + 各档回调(本件只读它、只回调)。 */\nexport interface PanelBodyProps {\n /** 卡片标题(各家自己的产品名)。 */\n readonly title: string | undefined;\n /** 右区三枚签(1145:各家交自家三枚 HELP+技能+插件;缺席=一行都不印,现红签已退役)。 */\n readonly seals?: readonly SealInstance[] | undefined;\n /** 家下标(1158:六家各取 0-5 之一,决定自家三枚倾角与槽位)。 */\n readonly sealSeed?: number | undefined;\n /** 点开的那枚下标(弹卷开合住状态层,本件只读;缺席=全关)。 */\n readonly selectedSeal?: number | null | undefined;\n /** 点章(本件只回调,状态在 `config-panel.ts`)。 */\n readonly onSealSelect?: ((index: number) => void) | undefined;\n /** 关卷。 */\n readonly onSealClose?: (() => void) | undefined;\n readonly items: readonly ConfigItem[];\n readonly state: PanelState;\n readonly draft: Readonly<Record<string, string>>;\n readonly busy: boolean;\n /** 成功提示(已保存/已重置)。 */\n readonly notice: string | null;\n /** 写入失败那一支:落点在底栏紧下面(与「就地失败」分开摆)。 */\n readonly writeError: string | null;\n /** 就地失败那一支(选目录那类)。 */\n readonly error: string | null;\n readonly picking: boolean;\n readonly browseRow: DirectoryRowBrowser | null;\n readonly rowEntry: DirectoryRowEntry | null;\n readonly dirtyKeys: readonly string[];\n readonly followKeys: readonly string[];\n /** **静态**跟随行名单(由各家交的 `followKeysOf` 钩子逐键探出来的,与当刻脏不脏无关):\n * 名单里的只读行按 v3.1 的 `.ic-under` 缩进;空缺=一行都不缩进。 */\n readonly followerKeys?: readonly string[] | undefined;\n readonly copy: { readonly key: string; readonly ok: boolean } | null;\n /** 技能侧报出的「配置里有值用不了」那一格事实(#915 第二步落地前缺席=不亮那一档)。 */\n readonly invalid?: boolean | undefined;\n readonly onCopy: (key: string, text: string) => void;\n readonly onChange: (key: string, next: string) => void;\n readonly onSave: () => void;\n readonly onReset: () => void;\n readonly onRetry: () => void;\n /** 自家附加块(版本行/状态行):给了就画在面板主体之后、动作条之前。 */\n readonly extra?: ((parts: PanelParts) => React.ReactNode) | undefined;\n}\n\n/** 应用内浏览器对话框的文案(面板自带,逐条对应图上那几个位置)。 */\nconst BROWSER_LABELS = {\n title: '选择文件夹',\n close: '关闭',\n up: '上一级',\n pathPlaceholder: '直接填绝对路径,回车即进入',\n go: '转到',\n showHidden: (n: number) => '显示隐藏目录(' + n + ')',\n empty: '这个目录里没有子目录。',\n loading: '正在读取',\n newFolder: '新建文件夹',\n createConfirm: '创建',\n createCancel: '取消',\n select: '选',\n selected: '已选',\n open: '选定这个目录',\n cancel: '取消',\n willPick: '将选定:',\n} as const;\n\n/** 画整面(四支控件、三支错误落位、底栏、附加块槽都在这里)。 */\nexport function PanelBody(props: PanelBodyProps): React.ReactElement {\n const { state, title } = props;\n const surface = state.kind === 'ready' ? state.surface : null;\n const product = title ?? '';\n const seals = props.seals ?? [];\n const seed = props.sealSeed ?? 0;\n /** 三行定序(六家一致:HELP 上/技能中/插件下;HELP 永远 z 最上)。 */\n const order = [0, 1, 2];\n /** 三行行高(上/中/下;纵向净空各留约 10px,糙边抖动也碰不到)。 */\n const TOPS = ['0.3em', '3.4em', '6.2em'];\n /** 轻微倾角(±0.5°~±2.5°,方向随机):家下标×槽位哈希,纯函数、每次同值,不过大。 */\n const tiltOfSlot = (slot: number): string => {\n const q = (seed * 5 + slot * 11 + 3) % 10;\n return String((q % 2 === 0 ? -1 : 1) * (0.5 + Math.floor(q / 2) * 0.5));\n };\n /** 右间距(三枚全部居右收敛,各家各行 0.2em~1.1em 错开,永不出面板右边界)。 */\n const padRightOfSlot = (slot: number): string => String(0.2 + ((seed * 7 + slot * 3 + 1) % 6) * 0.18) + 'em';\n const zOfRole = (role: string): number => role === 'help' ? 4 : role === 'skill' ? 3 : 2;\n const overlay = seals.length === 0 ? null : React.createElement(\n 'div',\n { style: S.sealOverlay },\n React.createElement(SealFilterDefs, {}),\n ...order.map((dataIndex, slot) => React.createElement(\n 'span',\n { key: seals[dataIndex].role + ':' + seals[dataIndex].tier, style: { position: 'absolute', top: TOPS[slot], right: padRightOfSlot(slot), zIndex: zOfRole(seals[dataIndex].role), pointerEvents: 'auto', display: 'inline-flex', transform: 'rotate(' + tiltOfSlot(slot) + 'deg)' } },\n // 章面缩到 80%:印本体(九档锁死值)不动,只在用法处压一层字号——印内 em 全跟下来,3px 边线原样保留。\n React.createElement('span', { style: { fontSize: '0.8em', display: 'inline-flex' } },\n React.createElement(SealStamp, { role: seals[dataIndex].role, tier: seals[dataIndex].tier, label: seals[dataIndex].label, onSelect: props.onSealSelect === undefined ? undefined : props.onSealSelect.bind(null, dataIndex) })),\n )),\n );\n const openSeal = props.selectedSeal === null || props.selectedSeal === undefined ? null : seals[props.selectedSeal] ?? null;\n const dialog = openSeal === null ? null : React.createElement(SealScrollDialog, {\n open: true, onClose: props.onSealClose ?? (() => undefined),\n role: openSeal.role, tier: openSeal.tier, title: openSeal.label,\n progress: openSeal.progress, status: openSeal.status, plan: openSeal.plan,\n });\n const headDataDir = surface === null ? '' : surface.resolved?.['dbDir'] ?? surface.dataDir;\n const ready = state.kind === 'ready';\n const dirtyCount = ready ? props.dirtyKeys.length : 0;\n const badge = panelBadgeOf({\n readFailed: state.kind === 'failed',\n busy: props.busy,\n writeFailed: props.writeError !== null,\n invalid: props.invalid === true,\n dirtyCount,\n saved: props.notice !== null,\n });\n const head = React.createElement(\n 'div',\n { style: S.head },\n React.createElement(TitleBlock, { product, purpose: '配置' }),\n React.createElement(PanelBadgeView, { kind: badge, dirtyCount }),\n dialog,\n );\n /** #939:这一块的高度由**客户端常量**决定、不由回执时序决定——加载期就把两行连标签一起画出来,\n * 值位画骨架条(`.ic-sk`),回执到了**原地填值** ⇒ 回执零位移(原先加载期只画一行小灰字、\n * 回执到了才长出两行,整块下移 21px)。\n *\n * 判断式按 `state.kind`:**不许**照 `surface === null` 写——它同时覆盖失败态,照写会让失败屏长出两行假骨架。 */\n const loading = state.kind === 'loading';\n /** #981:骨架宽度分档——底座仍是 `S.skeleton`(高度/圆角/底色唯一定义地),这里只覆盖宽度。\n * 配置文件行 68%(宽屏上限 420px)、数据目录行 46%(上限 300px),两行不等即错位感。 */\n const fileValue = (text: string | null, skeletonStyle: React.CSSProperties = S.skeleton): React.ReactNode =>\n text === null ? React.createElement('span', { style: skeletonStyle, 'data-ilife-skeleton': 'value' }) : text;\n const fileRow = (\n label: string,\n text: string | null,\n skeletonStyle: React.CSSProperties,\n tail?: React.ReactNode,\n ): React.ReactElement =>\n React.createElement(\n 'div',\n { style: S.info },\n React.createElement('b', { style: S.fileLabel }, label),\n fileValue(text, skeletonStyle),\n tail === undefined ? null : tail,\n );\n const fileLines =\n !loading && surface === null\n ? null\n : React.createElement(\n 'div',\n { style: S.fileWrap },\n fileRow(\n '配置文件',\n surface === null ? null : surface.path,\n { ...S.skeleton, width: '68%', maxWidth: 420 },\n // #939:这一句原先独占第三行、首次运行时会多 17px ⇒ 挂到同一行的行尾,两行高度恒定。\n surface !== null && surface.created\n ? React.createElement('span', { style: S.muted }, '(配置文件刚按默认值生成)')\n : null,\n ),\n fileRow('数据目录', surface === null ? null : headDataDir, { ...S.skeleton, width: '46%', maxWidth: 300 }),\n );\n\n const parts: PanelParts = { styles: S, reply: surface };\n const extra = props.extra === undefined ? null : props.extra(parts);\n\n if (state.kind === 'failed') {\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n fileLines,\n React.createElement('div', { style: S.error }, state.message),\n extra,\n overlay,\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement('button', { style: S.btn, type: 'button', 'data-ilife-press': 'retry', 'data-ilife-longpress': 'retry', onClick: props.onRetry }, '重试'),\n React.createElement('button', { style: S.btn, type: 'button', 'data-ilife-press': 'reset', 'data-ilife-longpress': 'reset', onClick: props.onReset }, '重置为默认'),\n ),\n // #1160 T1:按压+焦点双环的样式只此一枚 <style>,罩本卡内 data-ilife-press/focus 两类。\n // 放末位:首位会把既有形状判据 kidsOf(card)[0]===head 顶掉(920 红),末位不影响任何既有断言。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n }\n\n const common = props.items.filter((item) => item.tier === 'common');\n const advanced = props.items.filter((item) => item.tier === 'advanced');\n /** 还没读到整面(`loading`)时**框架照画**:行表与控件形状是客户端常量,不必等任何回执。\n * 值先空着、控件与三枚底栏键一律不可用;回执到了由 `ready` 那一支把值填上(见 `config-panel.ts`)。 */\n const frozen = props.busy || !ready;\n const followers = props.followerKeys ?? [];\n const renderRow = (item: ConfigItem, last: boolean) =>\n React.createElement(Row, {\n key: item.key,\n item,\n value: props.draft[item.key] ?? '',\n disabled: frozen,\n onChange: props.onChange,\n browser: props.rowEntry,\n dirty: ready && props.dirtyKeys.includes(item.key),\n follow: ready && props.followKeys.includes(item.key),\n onCopy: ready ? props.onCopy : undefined,\n copyState: props.copy !== null && props.copy.key === item.key ? (props.copy.ok ? 'done' : 'failed') : 'idle',\n alert: ready ? surface?.alerts?.[item.key] ?? null : null,\n last,\n nested: followers.includes(item.key),\n });\n /** 每组最后那一行才免掉下边线(`.ic-row:last-child`);母版行自己不带线,不必管。 */\n const lastIndexOf = (list: readonly ConfigItem[]) => {\n for (let i = list.length - 1; i >= 0; i -= 1) if (list[i].readonly === true) return i;\n return -1;\n };\n const commonLast = lastIndexOf(common);\n const advancedLast = lastIndexOf(advanced);\n\n const dirty = ready && dirtyCount > 0;\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n fileLines,\n React.createElement(\n 'div',\n { style: S.rows },\n common.map((item, index) => renderRow(item, index === commonLast)),\n ),\n advanced.length === 0\n ? null\n : React.createElement(\n 'details',\n { style: S.advanced, 'data-ilife-advanced': 'group' },\n React.createElement(\n 'summary',\n { style: S.summary, 'data-ilife-advanced': 'head' },\n React.createElement('span', { style: S.groupMark, 'data-ilife-advanced': 'mark' }, '›'),\n ADVANCED_GROUP_TITLE,\n ),\n React.createElement(\n 'div',\n { style: S.groupBody, 'data-ilife-advanced': 'body' },\n advanced.map((item, index) => renderRow(item, index === advancedLast)),\n ),\n ),\n extra,\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement(\n 'button',\n { style: dirty ? S.btnPrimary : S.btn, type: 'button', disabled: frozen || !dirty, 'data-ilife-press': 'save', 'data-ilife-longpress': 'save', onClick: props.onSave },\n props.busy ? '处理中' : dirty ? `保存(${dirtyCount} 项未保存)` : '保存',\n ),\n React.createElement('button', { style: S.btn, type: 'button', disabled: frozen, 'data-ilife-press': 'reset', 'data-ilife-longpress': 'reset', onClick: props.onReset }, '重置为默认'),\n React.createElement('button', { style: S.btn, type: 'button', disabled: frozen, 'data-ilife-press': 'retry', 'data-ilife-longpress': 'retry', onClick: props.onRetry }, '重新读取'),\n // 那半句话是**最后一个孩子**:v3.1 的 `.ic-bar .ic-note{margin-left:auto}` 靠它把说明顶到行尾,\n // 三枚按钮留在左边(顺序反了就会成「说明靠左、按钮靠右」,与真源相反)。\n dirty\n ? React.createElement('div', { style: S.saveMsg }, `浏览改动后请点保存(${dirtyCount} 项未保存),保存后跟随项自动更新`)\n : ready\n ? React.createElement('div', { style: S.barNote }, '没有未保存的改动')\n // #939:原先加载期在头部独占一行「配置读取中」;搬到底栏这一格——不删(首屏没有第二个\"正在读\"的表示),\n // 但也不占头部那两行的高度(`config-panel-908` 仍按文字断言它在这张卡里)。\n : React.createElement('div', { style: S.barNote }, '配置读取中'),\n ),\n props.picking ? React.createElement('div', { style: S.muted }, '已唤起系统文件夹对话框:选中后自动填上,取消则不动。') : null,\n props.browseRow !== null\n ? React.createElement(DirectoryBrowserFromRow, { open: true, row: props.browseRow, labels: BROWSER_LABELS })\n : null,\n props.writeError !== null\n ? React.createElement('div', { style: S.error, role: 'alert' }, props.writeError)\n : null,\n props.error !== null ? React.createElement('div', { style: S.error }, props.error) : null,\n props.notice !== null ? React.createElement('div', { style: S.okText, 'data-ilife-notice': 'line' }, props.notice) : null,\n overlay,\n // #1160 T1:按压+焦点双环的样式只此一枚 <style>,放末位(首位会顶掉 kidsOf(card)[0]===head 的既有判据)。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n}\n\n/** #936:**状态行**(照真源 v3.1 的 `.ic-status`)——两家(备忘/作息)共用这一件,各自只交数据。\n *\n * 形状(逐值照真源):\n * ① 盒子:`padding:10px 12px` + 沉底面 + `1px` 描边 + 圆角 `8px`;\n * ② 第一行:状态点(8px 圆,底色按档位)+ 名字(600)+ 一行短句;动作按钮靠 `margin-left:auto` 顶到行尾;\n * ③ 第二行:路径(等宽、单行不折、超出裁掉)+ 版本胶囊(`version === null` 就不画——真源骨架期就是 `hidden`);\n * ④ 官网行(等宽、可点新窗口)。\n *\n * 骨架期(`path === null`):值位画骨架条(同一个 `.ic-sk` 口径),两枚按钮照画 ⇒ 与就绪态**同高**、\n * 填充时不跳(真源 `planRows()` 的口径)。 */\nexport type StatusTone = 'ok' | 'warn' | 'bad' | 'pending';\n\n/** 状态点的底色:三档状态色 + 默认灰(照真源 `.is-ok/.is-warn/.is-bad` 与 `.ic-dot` 的默认档)。 */\nconst STATUS_DOT_COLOR: Record<StatusTone, string> = {\n ok: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n warn: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n bad: 'var(--dsw-alias-state-error-primary, #e0685f)',\n pending: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n};\n\nexport interface StatusBlockProps {\n /** 名字(真源 `.ic-sname`),屏上逐字显示。 */\n readonly name: string;\n /** 档位:点色与默认灰。 */\n readonly tone: StatusTone;\n /** 一行短句(真源 `.ic-stxt`)。 */\n readonly text: string;\n /** 第二行的路径;`null` ⇒ 画骨架条(加载期)。 */\n readonly path: string | null;\n /** 版本胶囊里的版本号;`null`/不传 ⇒ 不画胶囊。 */\n readonly version?: string | null | undefined;\n /** 动作按钮(0-2 枚;第一枚顶到行尾)。 */\n readonly actions?: readonly { readonly text: string; readonly onPress: () => void }[] | undefined;\n /** 官网行:显示文字 + 跳转目标(面板不自己拼地址);不给 ⇒ 不画这一行。 */\n readonly link?: { readonly text: string; readonly href: string } | undefined;\n}\n\nexport function StatusBlock(props: StatusBlockProps): React.ReactElement {\n const actions = props.actions ?? [];\n return React.createElement(\n 'div',\n { style: S.statusBox, 'data-ilife-status': 'block' },\n React.createElement(\n 'div',\n { style: S.statusRow },\n React.createElement('span', { style: { ...S.statusDot, background: STATUS_DOT_COLOR[props.tone] } }),\n React.createElement('span', { style: S.statusName }, props.name),\n React.createElement('span', { style: S.statusText }, props.text),\n ...actions.map((action, index) =>\n React.createElement(\n 'button',\n {\n key: action.text,\n style: index === 0 ? { ...S.btnPick, marginLeft: 'auto' } : S.btnPick,\n type: 'button',\n // #1160 T1:按压收缩+焦点双环(状态行动作钮同口径)。\n 'data-ilife-press': 'status-action',\n 'data-ilife-longpress': 'status-action',\n onClick: action.onPress,\n },\n action.text,\n ),\n ),\n ),\n React.createElement(\n 'div',\n { style: S.statusFind },\n props.path === null\n // #981:状态行路径位恢复真源定宽 180px(v3.1 `:422`),不跟面板走。\n ? React.createElement('span', { style: { ...S.skeleton, width: 180 }, 'data-ilife-skeleton': 'statusPath' })\n : React.createElement('span', { style: S.statusPath }, props.path),\n props.version === undefined || props.version === null || props.version === ''\n ? null\n : React.createElement('span', { style: S.statusVersion }, props.version),\n ),\n props.link === undefined\n ? null\n : React.createElement(\n 'a',\n {\n style: S.statusUrl,\n href: props.link.href,\n target: '_blank',\n rel: 'noreferrer',\n // #1160 T1:键盘焦点双环(链接只给焦点,不收缩)。\n 'data-ilife-focus': 'status-link',\n },\n props.link.text,\n ),\n );\n}\n","/** 配置体检:各家技能回给面板的那份报告的形状(唯一真相在技能侧,本件只是面板侧镜像)。\n *\n * 为什么形状这样定(票 #706「开工前的形状裁定」第一条):**判据住技能侧**。\n * 每条检查项的有效值只有技能源码算得出来(例:`resolveDbDir()` 里「空串=按默认落点」),\n * 插件包对技能的全部能力是 spawn `cmd_read` 与读 stdout 信封,看不到那些算式。所以:\n *\n * 技能侧出**判据**(每条给红/黄/绿 + 一句话 + 去哪修);\n * 插件包出**透传**(把技能回执原样交回,不重写一个字);\n * 总管出**总览与渲染**(零特例:不认任何一家,只认下面这个形状)。\n *\n * 于是「同一份数据、不许两处各算一遍」是结构上的性质:面板一次取数拿到六份报告,\n * 总览一行与各家那张表都从同一个快照读(见 `health-panel.ts` 的单一状态源)。\n *\n * 三档判据的语义(照检查表 `docs/research/check-table-671-life-panel-20260917.html`):\n * · `red`=现在用不了,必须去修;\n * · `yellow`=现在这状态能用,但有人真去用那个功能时会撞上(例:照片目录没配);\n * · `green`=正常。\n */\n\n/** 三档判据。 */\nexport type HealthStatus = 'red' | 'yellow' | 'green';\n\n/** 一条检查项的回执。 */\nexport interface HealthItem {\n /** 稳定标识:配置项就是它在配置文件里的键路径(`db.dir`),非配置项用点式名(`xunji.cli`)。 */\n readonly id: string;\n /** 页面上那一行的中文标题(技能侧定,面板不重写)。 */\n readonly title: string;\n readonly status: HealthStatus;\n /** 一句话:现在是什么状态,带上路径或门槛这类实证。 */\n readonly message: string;\n /** 去哪修。`green` 时可为空。 */\n readonly action: string;\n /** 这个值从哪来(只陈述,不判好坏):`配置文件`/`默认值`/`环境`。非取值类检查项可省。 */\n readonly source?: string;\n}\n\n/** 一家的体检报告。 */\nexport interface HealthReport {\n /** 技能名(`calorie`/`bill`/…),等于该家配置文件的主体名。 */\n readonly skill: string;\n /** 实际生效的那份配置文件路径。 */\n readonly configPath: string;\n /** 实际生效的数据目录。 */\n readonly dataDir: string;\n readonly items: readonly HealthItem[];\n /** #936:飞书 CLI 那一格(**可选**——只有作息那种\"状态行数据从体检出\"的家才带;旧技能包不带)。\n * 形状与技能侧体检回执的 `lark` 格同形:档位 + 路径 + 短版本号。 */\n readonly lark?: { readonly tier: string; readonly cliPath: string | null; readonly version: string | null } | undefined;\n}\n\n/** 体检出口:技能侧在**进分派层之前**拦下的那一类只读命令名(照三个配置命令的先例)。\n *\n * 载荷:入参 `{}`;回包 `{report}`(技能侧 envelope 的 `data`)。**只报不改**:\n * 不建目录、不写文件、不落默认配置——凡是要建目录才算得出的结论,一律当「不在」报。 */\nexport const HEALTH_CHECK_KEY = 'config.check' as const;\n\n/** 面板第二段参数(与各家的端点名同值,和各家的 `RPC_CHANNEL` 配成一次调用)。 */\nexport const HEALTH_ENDPOINT = 'config.check' as const;\n\n/** 三档排序:数字大的更严重(总览一行取**最严重**那档当那一家的灯)。 */\nconst RANK: Readonly<Record<HealthStatus, number>> = { green: 0, yellow: 1, red: 2 };\n\n/** 一家的总判据:全部项里最严重的那一档;一条都没有时回 `null`(不冒充绿)。 */\nexport function worstStatus(items: readonly HealthItem[]): HealthStatus | null {\n let worst: HealthStatus | null = null;\n for (const item of items) {\n if (worst === null || RANK[item.status] > RANK[worst]) worst = item.status;\n }\n return worst;\n}\n\n/** 三档计数(面板那一行「红 N 黄 N」用的就是它)。 */\nexport function countByStatus(items: readonly HealthItem[]): Readonly<Record<HealthStatus, number>> {\n const out = { red: 0, yellow: 0, green: 0 };\n for (const item of items) out[item.status] += 1;\n return out;\n}\n\n/** 回执校验:面板只渲染自己认得的形状,认不出即报「回执异常」,不猜、不返空冒充正常。 */\nexport function isHealthReport(raw: unknown): raw is HealthReport {\n if (typeof raw !== 'object' || raw === null) return false;\n const report = raw as Partial<HealthReport>;\n if (typeof report.skill !== 'string' || report.skill.length === 0) return false;\n if (typeof report.configPath !== 'string' || typeof report.dataDir !== 'string') return false;\n if (!Array.isArray(report.items)) return false;\n return report.items.every((item: unknown) => {\n if (typeof item !== 'object' || item === null) return false;\n const row = item as Partial<HealthItem>;\n return typeof row.id === 'string' && typeof row.title === 'string'\n && (row.status === 'red' || row.status === 'yellow' || row.status === 'green')\n && typeof row.message === 'string' && typeof row.action === 'string';\n });\n}\n","/** 面板到各家的配置体检:一次取数、六份报告(纯异步函数:无 DOM、无 react;传输口注入,便于单测)。\n *\n * 三条纪律:\n * · **只走各家自己的电话**(`rpc.call(CARRIER_BASE, <电话名>, {method, payload}, …)`)——总管不认任何\n * 一家的端点、也不加任何单品的依赖;电话名与端点名都是各家契约件公开的事实。\n * ⚠ **载体形状**(#735 真机踩到):第一段必须是**载体基段** `/api`,第二段是那家的电话名\n * (=它注册在 `/api<通道>` 上的那一段),端点名写进载荷的 `method`。写错第一段就是 404,\n * 而 404 只落成「那一家取不到数」,屏上看起来像按钮坏了(先例:总管自己的电话在 #678 栽过同一处,\n * 见 `update-contract.ts` 的 `CARRIER_BASE`)。\n * · **一次取数,六份报告**:总览那行与各家那张表读的是同一份结果(见 `health-panel.ts` 的单一状态源),\n * 不许两处各算一遍。\n * · 有界超时:一家不回不该拖死整屏(技能侧 spawn 上限 20 s,这里给 25 s)。\n */\nimport { HEALTH_ENDPOINT, isHealthReport } from './health-contract.js';\nimport type { HealthReport } from './health-contract.js';\nimport { CARRIER_BASE } from './update-contract.js';\nimport type { RpcCallFace } from './dsh-ctx.js';\n\n/** 传输口:与面板共用同一张脸(`connection.rpc.call` 的四参形状,见 dsh-ctx 的 RpcCallFace)。 */\nexport type HealthCallFace = RpcCallFace;\n\n/** 一家的电话名:通道名去掉前导斜杠那一段(各家宿主半注册在 `/api` + 通道名,信封 `method` 就是它)。 */\nexport function phoneOf(channel: string): string {\n return channel.startsWith('/') ? channel.slice(1) : channel;\n}\n\n/** 整批取数的时限(毫秒),**不是**一家一份:六家的活是在**同一个宿主线程**上排队干的。\n *\n * 为什么不是「技能侧 spawn 上限 20 s + 一点余量」(票 #740 对抗式审查第二轮逮到):\n * 各家的宿主半用的是 `spawnSync`(各单品的 `src/bridge.ts` 里 `SPAWN_TIMEOUT_MS = 20_000`),\n * 它**阻塞**宿主的事件循环——六通电话同时打进来,真正干活的是一个接一个串起来的。\n * 于是「六家之和」才是这一批的真实上界:按一家 20 s 算最坏 120 s,常见情形(一家 1~3 s)约 10 s。\n * 60 s 取两者之间:给常见情形留足余量,又不至于让一次卡死把按钮按成两分钟不动。\n * 单家的错照样逐家报(那家页签里的体检表会写「体检超时(60s)…」)。 */\nexport const HEALTH_TIMEOUT_MS = 60_000 as const;\n\n/** 一家的一行:报告取到了就是 `report`,取不到就是 `error`(缺席的包不进这张表)。 */\nexport interface HealthFetchRow {\n /** 该家插件包名(=爱生活页签槽 id,也是缺席判定的那个 join 键)。 */\n readonly id: string;\n readonly report: HealthReport | null;\n readonly error: string | null;\n}\n\n/** 一次取数的结果:每家一行(顺序照页签顺序)。 */\nexport interface HealthFetchResult {\n readonly rows: readonly HealthFetchRow[];\n /** 取数过程本身的错(通道缺席之类),不是某一家的问题。 */\n readonly error: string | null;\n}\n\n/** 账本条目里本票要用的那一段(页签槽注册时写进 options)。 */\nexport interface HealthTabRow {\n readonly id: string;\n readonly channel: string;\n}\n\nfunction timeoutText(): string {\n return '体检超时(' + String(Math.round(HEALTH_TIMEOUT_MS / 1000)) + 's):技能没在时限内回话。';\n}\n\nasync function fetchOne(call: HealthCallFace, row: HealthTabRow): Promise<{ readonly report: HealthReport | null; readonly error: string | null }> {\n try {\n const result = await call(\n CARRIER_BASE,\n phoneOf(row.channel),\n { method: HEALTH_ENDPOINT, payload: {} },\n AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n );\n if (!result || typeof result !== 'object' || typeof (result as { ok?: unknown }).ok !== 'boolean') {\n return { report: null, error: '回执信封异常(非 ok 信封)。' };\n }\n if (!result.ok) {\n const error = (result as { error?: { code?: unknown; message?: unknown } }).error;\n const message = typeof error?.message === 'string' && error.message.length > 0 ? error.message : '体检没跑起来。';\n return { report: null, error: message };\n }\n const value = (result as { value?: unknown }).value;\n if (!isHealthReport(value)) return { report: null, error: '体检报告形状认不出(技能包与总管版本对不上?)。' };\n return { report: value, error: null };\n } catch (error) {\n if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {\n return { report: null, error: timeoutText() };\n }\n return { report: null, error: '体检没跑起来:' + (error instanceof Error ? error.message : String(error)) };\n }\n}\n\n/** 取六家的体检报告(缺席的包不在此列,由调用方按账本判断)。\n *\n * `onRow`(票 #741):某一家一落定就回调一次——面板据此**按家增量**画,不再等齐全部。\n * 六家的电话是并发打出去的,但宿主里干活的只有一个线程(各单品宿主半 `spawnSync`),\n * 所以真实到达顺序就是它们被串行干完的顺序;先回来的那家先画出来,用户才看得见进度。 */\nexport async function loadHealthReports(\n call: HealthCallFace | null,\n tabs: readonly HealthTabRow[],\n onRow?: (row: HealthFetchRow) => void,\n): Promise<HealthFetchResult> {\n if (typeof call !== 'function') return { rows: [], error: '宿主连接缺席:connection.rpc.call 不可用' };\n const rows = await Promise.all(tabs.map(async (tab): Promise<HealthFetchRow> => {\n const one = await fetchOne(call, tab);\n const row: HealthFetchRow = { id: tab.id, report: one.report, error: one.error };\n // 回调里的异常不许把整批拖下水:这是画图的旁路,不是取数本身。\n try {\n onRow?.(row);\n } catch {\n /* 旁路失败不影响整批结果 */\n }\n return row;\n }));\n return { rows, error: null };\n}\n","/** 配置体检的状态源:**一份快照**喂两处(面板顶部那行总览 + 各家页签那张表)。\n *\n * 为什么是一个 hook 而不是两处各取一次(票 #706 验收第三条「同一份数据,不许两处各算一遍」):\n * 这一处取数、这一处存状态,总览与各家表都从它读 —— 那条验收因此是**结构上的性质**,不是靠自觉。\n *\n * 取数是**按需**的(点「体检一次」才跑):没有轮询、没有定时器(静态门那条「无数据轮询」照旧)。\n */\nimport * as React from 'react';\nimport { loadHealthReports } from './health-fetch.js';\nimport type { HealthCallFace, HealthFetchRow, HealthTabRow } from './health-fetch.js';\nimport { worstStatus } from './health-contract.js';\nimport type { HealthReport, HealthStatus } from './health-contract.js';\n\n/** 一家的状态:没跑过/正在跑/有报告/这一家出错。 */\nexport type HealthPhase = 'idle' | 'running' | 'ready' | 'failed';\n\nexport interface HealthPanelFace {\n /** 一次取数的结果:包名 → 该家的状态(缺席的包不在表里)。 */\n readonly rows: Readonly<Record<string, { readonly phase: HealthPhase; readonly report: HealthReport | null; readonly error: string | null }>>;\n /** 取数过程本身的错(通道缺席之类)。 */\n readonly error: string | null;\n /** 正在跑。 */\n readonly running: boolean;\n /** 跑一次(六家一起)。 */\n run(): void;\n}\n\nconst IDLE = { phase: 'idle' as HealthPhase, report: null, error: null };\n\n/** 一家的灯:没报告=`null`(不冒充绿)。 */\nexport function lightOf(row: { readonly report: HealthReport | null } | undefined): HealthStatus | null {\n return row?.report ? worstStatus(row.report.items) : null;\n}\n\n/** 体检状态源:挂载时不自动跑(体检要读盘、要起子进程,按需跑),点一次跑六家。\n *\n * **按家增量**(票 #741):某一家一落定就画那一家——不再等全部齐了才一次性 setRows。\n * 取数口仍然只有一处(`loadHealthReports`),它把「一家好了」的回调交给这里 patch 状态。 */\nexport function useHealthPanel(getCall: () => HealthCallFace | null, tabs: readonly HealthTabRow[]): HealthPanelFace {\n const [rows, setRows] = React.useState<HealthPanelFace['rows']>({});\n const [error, setError] = React.useState<string | null>(null);\n const [running, setRunning] = React.useState(false);\n const run = React.useCallback(() => {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('run:tabs=' + String(tabs.length));\n setRunning(true);\n setError(null);\n const targets = tabs;\n setRows(Object.fromEntries(targets.map((tab) => [tab.id, { ...IDLE, phase: 'running' as HealthPhase }])));\n void (async () => {\n const patch = (row: HealthFetchRow): void => {\n // 函数式更新:这家落定时别家可能也刚落定,直接读旧 state 会丢掉那一次。\n const next = { phase: (row.report ? 'ready' : 'failed') as HealthPhase, report: row.report, error: row.error };\n setRows((previous) => ({ ...previous, [row.id]: next }));\n // 只记条数与第一条的错(渲染台的调试图;浏览器里没人写它,写入失败无所谓)。\n try {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('row=' + row.id + ' ok=' + String(row.report !== null) + ' err=' + String(row.error));\n } catch {\n /* 调试图写不进去不影响正经事 */\n }\n };\n const loaded = await loadHealthReports(getCall(), targets, patch);\n try {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('rows=' + String(loaded.rows.length) + ' err=' + String(loaded.error) + ' first=' + String(loaded.rows[0]?.error ?? 'null'));\n } catch {\n /* 调试图写不进去不影响正经事 */\n }\n setError(loaded.error);\n setRunning(false);\n })();\n }, [getCall, tabs]);\n const face: HealthPanelFace = { rows, error, running, run };\n // 出图页的取数口(渲染台的 hooks 替身没有真事件环,页内点按钮那条路不落定取数)。\n // 只在有人预先挂了 `__T706_FACE_SINK__` 时写一次,浏览器里没人挂它,等于没有这一行。\n const sink = (globalThis as { __T706_FACE_SINK__?: (value: HealthPanelFace) => void }).__T706_FACE_SINK__;\n if (typeof sink === 'function') {\n try {\n sink(face);\n } catch {\n /* 出图口的错不影响正经渲染 */\n }\n }\n return face;\n}\n\n/** 汇总行该显示哪一句错(票 #735):取数过程本身的错优先;**一家报告都没回来**时,把第一条家错误顶上来。\n *\n * 为什么要有这一格:各家的错只画在那家页签里的体检表上,一行摘要看不见 ⇒ 用户点完只见「体检中…」闪一下、\n * 接着仍旧「还没体检」,会以为按钮坏了(真机上就是这么被误判的;那次六家全 404,汇总行一声不吭)。\n * 有报告回来时不顶:那时各家那张表里已经把错说清楚了,摘要行只管读数。 */\nexport function summaryErrorOf(face: Pick<HealthPanelFace, 'rows' | 'error'>): string | null {\n if (face.error !== null) return face.error;\n const rows = Object.values(face.rows);\n if (rows.some((row) => row.report !== null)) return null;\n return rows.find((row) => row.error !== null)?.error ?? null;\n}\n","/** 配置体检的两个画法:面板顶部那行总览(六家一盏灯)+ 各家那张表。\n *\n * 面板侧纪律(沿 `client.ts`/`update-panel.ts` 既有口径):`createElement` 手写(禁 JSX)、内联 style、\n * 颜色走 DSH 主题别名(深浅主题自适应,写死值只做回退)、不 import 任何单品包、不 import 技能实现。\n *\n * 判据与文案**全部来自各家技能的报告**,本件一个字都不重写——只按三档上色、按状态挑文案。\n */\nimport * as React from 'react';\nimport { countByStatus, worstStatus } from './health-contract.js';\nimport type { HealthItem, HealthReport, HealthStatus } from './health-contract.js';\nimport { TitleBlock } from './title-seal.js';\nimport { interactionCss } from './config-panel-view.js';\n\n/** 三档的配色(绿走主题别名;红黄用实色,理由见下)。\n *\n * 红与黄为什么必须落在**不同明度带**上:这两档要一起出现在同一行灯里,用户扫一眼要能分开\n * 「红了(用不了)」与「黄了(能用但要撞上)」。评审连着两轮点「黄灯偏暗与红相近」,所以:\n * · 红=深红(暗、重);\n * · 黄=高饱和的琥珀(亮、跳)——它在白底上的对比度仍然够(`#b45309` 对白 ≈ 4.9:1)。\n * 绿不给底色:它是「正常」,不该和要处理的那两档抢注意力。 */\nexport const STATUS_COLOR: Readonly<Record<HealthStatus, string>> = {\n red: '#c0392b',\n yellow: '#e08a00',\n green: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n};\n\n/** 整行底色的浅一档:饱和降一档,免得红黄行「刺眼」压过正文(评审第二轮的扣分点)。 */\n/** 红黄两档整行上底的底色。红灯要**明显重于**黄灯:复评实测原先两个底色亮度几乎一样\n * (红 `#FBF3F3` 对黄 `#FCF3E5`,只差蓝通道),最严重那一档反而最淡——红色提到 0.14。 */\nconst STATUS_TINT: Readonly<Record<HealthStatus, string>> = {\n red: 'rgba(192, 57, 43, 0.14)',\n yellow: 'rgba(224, 138, 0, 0.10)',\n green: 'transparent',\n};\n\n/** 档位词(灯里那几个字)用的色:红黄要**够深**才读得清,故另取一枚比灯更深的值。 */\nexport const STATUS_TEXT: Readonly<Record<HealthStatus, string>> = {\n red: '#a5281b',\n yellow: '#8a5200',\n green: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n};\n\nconst STATUS_LABEL: Readonly<Record<HealthStatus, string>> = { red: '红', yellow: '黄', green: '绿' };\n\n/** 档位的**形状**标记:颜色之外的第二条读数(灰度截图、色弱视角下也分得开谁要处理)。 */\nconst STATUS_MARK: Readonly<Record<HealthStatus, string>> = { red: '✕', yellow: '!', green: '✓' };\n\n/** 面板上的文字色与分隔线(与 `client.ts` 同一套别名)。 */\nconst INK = 'var(--dsw-alias-label-primary, inherit)';\nconst INK_DIM = 'var(--dsw-alias-label-secondary, #9a9a9a)';\nconst BORDER = 'var(--dsw-alias-border-l1, rgba(128,128,128,.35))';\n\nexport const HEALTH_STYLE = {\n box: {\n padding: '10px 12px',\n marginBottom: 10,\n borderRadius: 10,\n border: '1px solid ' + BORDER,\n } as React.CSSProperties,\n headRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, marginBottom: 4 } as React.CSSProperties,\n head: { fontSize: 13, fontWeight: 700, color: INK } as React.CSSProperties,\n /** 顶部那一行汇总(票 #732):一行装下「配置体检 + 一句总账 + 体检按钮」,三样都不独占行。\n * 它替掉了原先「标题行 + 单独一行的按钮 + 一排灯按钮 + 两行图例」(同屏实测 153px → 一行)。 */\n summaryRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n marginBottom: 10,\n } as React.CSSProperties,\n summaryText: { flex: '1 1 auto', minWidth: 0, fontSize: 12.5, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } as React.CSSProperties,\n /** 各家那张表里的说明行(「还没体检:点上面那个…」那几句)。 */\n meta: { color: INK_DIM, fontSize: 12, lineHeight: 1.7, marginTop: 12 } as React.CSSProperties,\n btn: {\n border: '1px solid ' + BORDER,\n background: 'transparent',\n color: INK,\n borderRadius: 8,\n flex: '0 0 auto',\n padding: '3px 10px',\n fontSize: 12.5,\n fontWeight: 600,\n cursor: 'pointer',\n } as React.CSSProperties,\n error: { color: STATUS_COLOR.red, fontSize: 12, lineHeight: 1.7, marginTop: 6 } as React.CSSProperties,\n} as const;\n\n/** 值得一眼看见的那两档(档位名与整行文字都上色;绿行保持淡)。 */\nfunction isAttention(status: HealthStatus): boolean {\n return status === 'red' || status === 'yellow';\n}\n\n/** 「去哪修」那一行的正文:各家交回来的 action 里若已自带「去哪修:」,这里剥掉一层——\n * 面板已经带了这个前缀,两边都写就会印成「去哪修:去哪修:…」。只剥开头那一次,别的一字不动。 */\nexport function actionText(action: string): string {\n const trimmed = action.trim();\n return trimmed.startsWith('去哪修:') ? trimmed.slice(4).trim() : trimmed;\n}\n\n/** 报文里能扫出来的每一段路径(盘符绝对路径;非 Windows 形状的退回 `/` 打头那一支)。 */\nfunction rawPathsIn(text: string): readonly string[] {\n return text.match(/[A-Za-z]:\\/[^\\s()「」,。;]+/g) ?? text.match(/\\/[^\\s()「」,。;]+/g) ?? [];\n}\n\n/** 一段落点的**根**(盘符):`C:/a/b` → `C:/`。用来把「同一个盘、但根下不同枝」的落点分开算,\n * 免得一个 D 盘上的旁证(比如「配了但目录不在:D:/media」)把 C 盘那几条的缩写全拖没。 */\nfunction rootOf(path: string): string {\n const drive = /^[A-Za-z]:\\//.exec(path);\n return drive === null ? '/' : drive[0];\n}\n\n/** 一段落点的公共目录前缀(按 `/` 切、整段比):`C:/a/b/c` + `C:/a/b/d` → `C:/a/b`。 */\nfunction commonDirOf(paths: readonly string[]): string {\n if (paths.length < 2) return '';\n const split = paths.map((path) => path.replace(/[.,。;]+$/, '').split('/'));\n const first = split[0] as string[];\n let size = 0;\n while (size < first.length && split.every((parts) => parts[size] === first[size])) size += 1;\n return size < 1 ? '' : first.slice(0, size).join('/');\n}\n\n/** 一份报文里**按根分开**的落点前缀(长的在前):只给「同一个根下至少两条落点」的根算前缀——\n * 这个根下只有一条落点时不缩(没有第二条可比,缩出来反而是瞎猜)。\n * 为什么要分根:一家报里常有别的盘的旁证(备忘录用 D 盘上那份媒体目录),只算一个总前缀,\n * 一条旁证就能把整张表的缩写全拖没(#706 出图复评:备忘录那张表整片没缩、记账那张缩了)。 */\nexport function dirPrefixesOf(report: HealthReport): readonly string[] {\n return dirPrefixesIn(pathsOf(report).join(' '));\n}\n\n/** `dirPrefixesOf` 的正文版:一段文字里(报文正文 + 表头两条混在一起)按根分组取公共目录前缀。 */\nexport function dirPrefixesIn(text: string): readonly string[] {\n const paths = rawPathsIn(text);\n const roots = [...new Set(paths.map(rootOf))];\n const prefixes: string[] = [];\n for (const root of roots) {\n const prefix = commonDirOf(paths.filter((path) => rootOf(path) === root));\n if (prefix !== '') prefixes.push(prefix);\n }\n // 长的在前:一个落点命中多条前缀时,最长的那个才是它真正的目录(缩短后不歧义)。\n return prefixes.sort((a, b) => b.length - a.length);\n}\n\n/** 那一行「配置文件 … · 数据目录 …」+ 各条报文里的长路径,都缩成「公共前缀 + 短尾巴」。 */\nfunction shorten(path: string, prefixes: readonly string[]): string {\n const hit = prefixes.find((prefix) => path.startsWith(prefix + '/'));\n return hit === undefined ? path : '…/' + path.slice(hit.length + 1);\n}\n\n/** 一家里所有会印出来的路径。 */\nexport function pathsOf(report: HealthReport): readonly string[] {\n const out: string[] = [report.configPath, report.dataDir];\n for (const item of report.items) out.push(item.message);\n return out;\n}\n\n/** 把报文里那一长串路径按公共前缀缩短(只动打印,不动事实)。\n * 只按**扫得出来的那一段段落点**去缩,不做全串替换:既不误伤长路径,也不会把一段路劈成半截。 */\nexport function shortenPathsIn(text: string, prefixes: readonly string[]): string {\n const candidates = rawPathsIn(text);\n if (candidates.length === 0) return text;\n // 长的在前:先缩长的那条,缩完它自己就挡住了它的父目录,不会二次缩。\n const longestFirst = [...new Set(candidates.map((path) => path.replace(/[.,。;]+$/, '')))].sort(\n (a, b) => b.length - a.length,\n );\n let out = text;\n for (const path of longestFirst) {\n const hit = prefixes.find((prefix) => path.startsWith(prefix + '/'));\n if (hit === undefined) continue;\n out = out.split(hit + '/').join('…/');\n }\n return out;\n}\n\n/** 一盏灯:一家一名一档(数到几个红黄绿)。落点是那家页签(票 #732:面板不再单开一排灯按钮)。 */\nexport interface HealthLightRow {\n readonly id: string;\n readonly title: string;\n readonly status: HealthStatus | null;\n readonly counts: Readonly<Record<HealthStatus, number>>;\n /** 这一家交没交体检通道(页签槽 `options.channel`)。false=这家没装/没接上:圆点保持缺席态,不许点。 */\n readonly hasChannel: boolean;\n}\n\n/** 从六份报告数出那一行的灯(每家按最严重那一档上色)。 */\nexport function lightsOf(\n tabs: readonly { readonly id: string; readonly title: string; readonly hasChannel?: boolean }[],\n reports: Readonly<Record<string, HealthReport | undefined>>,\n): readonly HealthLightRow[] {\n return tabs.map((tab) => {\n const report = reports[tab.id];\n const items = report?.items ?? [];\n return {\n id: tab.id,\n title: tab.title,\n status: report ? worstStatus(items) : null,\n counts: countByStatus(items),\n // 缺席与「装了但还没体检」在报告上都长成 null,分开只能靠这一格(票 #732)。\n hasChannel: tab.hasChannel ?? true,\n // 这里曾短期印过「最严重那一项的名字」当区分度,**已撤回**:实测六家常坏在同一条通用检查项上\n // (都是「数据目录」),印出来六家一模一样,是噪声不是区分度(出图复评实测逐字抄了六个「数据目录」)。\n // 「哪一项坏了」本来就由那家自己那张表回答,灯上不再重复。\n };\n });\n}\n\n/** 一截读数:数字本身(`2`/`3`),颜色即档位(`status` 决定色);`—`(缺席没报告)。`status` 为 null 的那些走淡色。 */\nexport interface HealthCountSeg {\n readonly status: HealthStatus | null;\n readonly text: string;\n}\n\n/** 把一份计数拆成**按档分色**的几截。红黄各自一截、各自上色,互不串色;\n * 数字本身不带档位字(#951):颜色即档位,胶囊形状由调用方配 `COUNT_CAPSULE`/`TAB_NOTE_STYLE`。 */\nexport function countSegsOf(light: HealthLightRow): readonly HealthCountSeg[] {\n if (light.status === null) return [{ status: null, text: '—' }];\n const segs: HealthCountSeg[] = [];\n for (const status of ['red', 'yellow'] as const) {\n const count = light.counts[status];\n if (count > 0) segs.push({ status, text: String(count) });\n }\n return segs.length > 0 ? segs : [{ status: 'green', text: String(light.counts.green) }];\n}\n\n/** 几截读数之间那个分隔符:不许染档位色(它不属于任何一档)。 */\nconst SEG_SEP = ' · ';\n\n/** 一家的读数**写成一行**(纯文本版:给页签上那枚小数字用)。 */\nfunction countText(light: HealthLightRow): string {\n return countSegsOf(light).map((piece) => piece.text).join(SEG_SEP);\n}\n\n/** 数字胶囊的形状(#951):页签那枚 `TAB_NOTE_STYLE` 的同形,汇总与表头共用。\n * 颜色不进这一格,调用方按档配 `STATUS_TEXT[status]`——颜色即档位,字上不再写档位名。 */\nexport const COUNT_CAPSULE_STYLE: React.CSSProperties = {\n border: '1px solid ' + BORDER,\n borderRadius: 999,\n padding: '0 6px',\n margin: '0 2px',\n fontSize: 11,\n fontWeight: 700,\n} as const;\n\n/** 汇总行那一句:绿要显眼(它是「正常」这件事本身),没通道的那几家不冒充绿。\n *\n * 「没有体检出口」与「还没跑过体检」是**两件事**(票 #735):前者那颗「体检一次」按下去不会有任何反应,\n * 必须自己说出来——两态共用一句「还没体检」时,用户与诊断都会被引到「点了没跑」上去。 */\nfunction overallText(lights: readonly HealthLightRow[]): React.ReactElement {\n const pending = lights.filter((light) => !light.hasChannel).length;\n const withChannel = lights.filter((light) => light.hasChannel);\n const tail = pending > 0 ? ' · ' + String(pending) + ' 家没有体检出口' : '';\n if (withChannel.length === 0) {\n const what = lights.length === 0\n ? '还没有任何一家登记体检出口'\n : String(lights.length) + ' 家都没有体检出口(通道名没登记上)';\n return React.createElement('span', { style: { color: STATUS_TEXT.red, fontWeight: 700 } }, '没有可体检的家:' + what);\n }\n const allGreen = withChannel.every((light) => light.status === 'green');\n if (allGreen && pending === 0) {\n return React.createElement('span', { style: { color: STATUS_COLOR.green, fontWeight: 700 } }, '六家正常');\n }\n const parts: React.ReactElement[] = [];\n for (const status of ['red', 'yellow'] as const) {\n let count = 0;\n for (const light of lights) count += light.counts[status];\n if (count === 0) continue;\n if (parts.length > 0) parts.push(React.createElement('span', { key: status + '-sep', style: { color: INK_DIM } }, SEG_SEP));\n parts.push(\n React.createElement(\n 'span',\n { key: status, style: { ...COUNT_CAPSULE_STYLE, color: STATUS_TEXT[status] } },\n String(count),\n ),\n );\n }\n if (parts.length === 0) {\n // 一份报告都没跑过;有出口的已经全绿、只是还有没出口的家时,不许把「还没体检」安到它们头上。\n return React.createElement('span', { style: { color: INK_DIM } }, (allGreen ? '有出口的都正常' : '还没体检') + tail);\n }\n parts.push(React.createElement('span', { key: 'todo', style: { color: INK } }, ' 要处理' + tail));\n return React.createElement('span', null, parts);\n}\n\n/** 那枚小数字用的底色(比灯色浅一档,压在页签上不抢眼)。 */\nconst STATUS_DOT: Readonly<Record<HealthStatus, string>> = {\n red: STATUS_COLOR.red,\n yellow: STATUS_COLOR.yellow,\n green: STATUS_COLOR.green,\n};\n\n/** 页签圆点的颜色与语义(票 #732):从「实心/空心=装没装」改成「颜色=体检档位」。 */\nexport const TAB_DOT = {\n /** 没装(页签槽账本里没有这一家)。 */\n absent: STATUS_DOT.green,\n /** 装了、但还没跑过体检。 */\n unchecked: INK_DIM,\n} as const;\n\n/** 页签圆点该用什么色:装了按档位(红黄绿),没装/没跑过走缺席态。 */\nexport function tabDotColor(light: HealthLightRow): string {\n if (!light.hasChannel) return TAB_DOT.absent;\n return light.status === null ? TAB_DOT.unchecked : STATUS_DOT[light.status];\n}\n\n/** 页签上那枚小数字(票 #732):红黄那几家,家名后带一条计数;全绿与没跑过不带。 */\nexport const TAB_NOTE_STYLE: React.CSSProperties = {\n marginLeft: 5,\n border: '1px solid ' + BORDER,\n borderRadius: 999,\n padding: '0 5px',\n fontSize: 11,\n fontWeight: 700,\n} as const;\n\n/** 那枚小数字写什么:只报「要处理」的那两档;一家里红黄都有时先报红那一档(圆点已经按最严重的档上色了)。 */\nexport function tabNote(light: HealthLightRow): HealthCountSeg | null {\n if (!light.hasChannel || light.status === null || light.status === 'green') return null;\n return countSegsOf(light)[0] ?? null;\n}\n\n/** 顶部那一行汇总(票 #732:不再一家一盏灯按钮,一行总账 + 一个体检按钮)。\n *\n * 与页签的分工:这行只答「一共几条要处理」;「哪几家、各几条」由页签的圆点与小数字答;\n * 「具体哪一条坏了、去哪修」由那家页签里的 `HealthTable` 答。 */\nexport function HealthSummaryLine(props: {\n readonly lights: readonly HealthLightRow[];\n readonly running: boolean;\n readonly error: string | null;\n readonly onRun: () => void;\n}): React.ReactElement {\n return React.createElement(\n 'div',\n { style: HEALTH_STYLE.summaryRow, 'data-ilife-health': 'summary' },\n React.createElement('div', { style: HEALTH_STYLE.head }, '配置体检'),\n React.createElement(\n 'div',\n { style: HEALTH_STYLE.summaryText },\n // 一家都没有出口这一态由 overallText 自己说(#735):这里不再用一句「还没体检」把两件事糊成一件。\n props.error !== null ? React.createElement('span', { style: { color: STATUS_TEXT.red } }, props.error) : overallText(props.lights),\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: props.running ? { ...HEALTH_STYLE.btn, opacity: 0.55, cursor: 'default' } : HEALTH_STYLE.btn,\n disabled: props.running,\n onClick: props.onRun,\n // 「只看不改」那三条口径从屏上撤下来了(它原先和两排圆点的图例挤在一行),\n // 但它**仍然要说出口**:按钮悬停是它能待的地方(票 #732;测试按产物里的字符串守着)。\n title: '对六家各跑一次配置体检(只看不改:不建目录、不改配置、不自动重置)',\n // #1163 T4:体检按钮复用共享交互缝(按压+悬停+焦点+禁用+长按纯视觉环),tokens 只引共享层。\n // 长按纯视觉:CSS 驱动 0→100%(0.5s),零回调零状态,不进状态机分支;折叠/复制/提示/液态本面不适用。\n 'data-ilife-press': 'health-run',\n 'data-ilife-longpress': 'health-run',\n },\n props.running ? '体检中…' : '体检一次',\n ),\n // #1163 T4:同一生成处 interactionCss(),一枚 style 罩本行内两类属性;更新面无渲染点(见票内豁免证据)。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n}\n\n/** 正正常常的那些:绿条只留「名字」,一句话与「去哪修」都不印(票面验收第一条:正常的收成一行)。 */\nfunction isAttentionItem(item: HealthItem): boolean {\n return isAttention(item.status);\n}\n\n/** 表头那组数字(#951):与页签/汇总同口径——只印数字胶囊,颜色即档位,不印档位字。 */\nfunction headerCounts(report: HealthReport): React.ReactElement {\n const counts = countByStatus(report.items);\n const status = worstStatus(report.items);\n const segs = countSegsOf({ id: 'header', title: '', status, counts, hasChannel: true });\n return React.createElement(\n 'div',\n { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 } },\n segs.map((seg, index) => seg.status === null\n ? React.createElement('span', { key: index, style: { color: INK_DIM } }, seg.text)\n : React.createElement(\n 'span',\n { key: index, style: { ...COUNT_CAPSULE_STYLE, color: STATUS_TEXT[seg.status] } },\n seg.text,\n )),\n );\n}\n\n/** 一家那张表:**要处理的**(红黄)一条一块、整行上底带一句话与「去哪修」;\n * **正常的**(绿)收成一行并列的小字,点得到、扫得完(票面验收第一条:正常的收成一行,有问题的才展开)。 */\nexport function HealthTable(props: {\n readonly title: string;\n readonly phase: 'idle' | 'running' | 'ready' | 'failed';\n readonly report: HealthReport | null;\n readonly error: string | null;\n}): React.ReactElement {\n const { report } = props;\n const prefixes: readonly string[] = report ? dirPrefixesOf(report) : [];\n return React.createElement(\n 'div',\n { style: HEALTH_STYLE.box, 'data-ilife-health': 'table' },\n React.createElement(\n 'div',\n { style: HEALTH_STYLE.headRow },\n React.createElement(TitleBlock, { product: props.title, purpose: '体检' }),\n report ? headerCounts(report) : null,\n ),\n props.phase === 'idle'\n ? React.createElement('div', { style: HEALTH_STYLE.meta }, '还没体检:点上面那个「体检一次」。')\n : null,\n props.phase === 'running' ? React.createElement('div', { style: HEALTH_STYLE.meta }, '正在体检…') : null,\n props.error !== null ? React.createElement('div', { style: HEALTH_STYLE.error }, props.error) : null,\n report\n ? React.createElement(\n 'div',\n null,\n // 行内落点缩成「…/短尾」是为了不把每行撑成两行;但「去哪修:先建这个目录」要照着建,\n // 就得把**它到底在哪儿**说清楚(出图复评五份里两份点名「照屏修不了」)。\n // 措辞有讲究:不能写成「下面行内的「…/」都接在这里」——行里本来就有个「…/data」,\n // 接上基目录会拼成「…/data/data」,自相矛盾(复评原话)。故只**点明缩写代表哪一层**。\n report.dataDir !== ''\n ? React.createElement('div', { style: { color: INK_DIM, fontSize: 11.5, marginTop: 2 } },\n '落点根目录:' + report.dataDir\n + (report.configPath !== '' ? '(配置文件 ' + report.configPath + ')' : ''))\n : null,\n report.items.filter(isAttentionItem).map((item) =>\n React.createElement(\n 'div',\n {\n key: item.id,\n 'data-ilife-health': 'item',\n 'data-status': item.status,\n // 红黄两档整行上底:扫一眼先看见「要处理的」。\n style: {\n padding: '7px 8px',\n margin: '2px 0',\n borderTop: '1px solid ' + BORDER,\n borderRadius: 6,\n fontSize: 13,\n lineHeight: 1.75,\n background: STATUS_TINT[item.status],\n },\n },\n React.createElement(\n 'div',\n { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6 } },\n // 档位**不只靠颜色**认:形状标记(✕/!)+档位字,色弱与灰度截图下也分得开。\n // 原先这里还有一个同色的实心圆点,与标记、档位字三者同义同色(复评两份点名「没有主次」),\n // 且页签那排(`client.ts:349`)也用同样的圆点表示「这家装没装」——同屏两套圆点语义,\n // 故本件的条目里**去掉圆点**,把圆点这一格语汇留给页签。\n React.createElement('span', {\n style: { color: STATUS_TEXT[item.status], fontWeight: 700, fontSize: 13, lineHeight: 1 },\n 'aria-hidden': 'true',\n }, STATUS_MARK[item.status]),\n React.createElement('span', { style: { fontWeight: 650, color: INK } }, item.title),\n React.createElement('span', {\n style: { color: STATUS_TEXT[item.status], fontWeight: 700 },\n }, STATUS_LABEL[item.status]),\n // 来源那一格**只在它不只是「默认值」时印**:四行全印「来自默认值」等于零区分度,\n // 而且「值从哪来」这件事正文里已经写全了(复评原话:「这四个字零区分度」)。\n item.source && item.source !== '默认值'\n ? React.createElement('span', { style: { color: INK_DIM } }, '· 来自' + item.source)\n : null,\n ),\n React.createElement('div', { style: { color: INK }, title: item.message }, shortenPathsIn(item.message, prefixes)),\n item.action.length > 0\n ? React.createElement('div', { style: { color: INK_DIM } }, '去哪修:' + actionText(item.action))\n : null,\n ),\n ),\n // 正常的那一档:一条都不用点开,名字排成一行(`data-ilife-health=\"ok\"` 供判据核对「一条不少」)。\n // 给它**一条绿边+一层极淡底**,让它读起来是「一组正常项」,而不是四行问题之后的一句脚注\n // (复评原话:「占多数的那一档在视觉上几乎退成脚注」)——条目仍然收成一行(票面第一条)。\n report.items.some((item) => !isAttentionItem(item))\n ? React.createElement(\n 'div',\n {\n 'data-ilife-health': 'ok',\n style: {\n display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '2px 10px',\n marginTop: 8, padding: '6px 8px',\n borderLeft: '3px solid ' + STATUS_COLOR.green,\n borderRadius: 6,\n fontSize: 12, lineHeight: 1.6, color: INK_DIM,\n },\n },\n // 绿档也要**看得见**:只给一行灰字时,五份出图复评里三份独立指到「全屏没有绿色、\n // 绿=正常这一档等于不存在」。故这一档前面加一个绿点(与红黄同一个「点」语汇),\n // 说明它们就是正常项——条目**仍然收成一行**(票面第一条:正常的收成一行)。\n React.createElement('span', {\n style: {\n width: 8, height: 8, borderRadius: '50%', display: 'inline-block',\n background: STATUS_COLOR.green, flex: '0 0 auto',\n },\n 'aria-hidden': 'true',\n }),\n React.createElement('span', { style: { fontWeight: 650, color: INK } }, '正常 ' + String(countByStatus(report.items).green) + ' 条'),\n React.createElement('span', { style: { color: INK_DIM } }, '(下列各项都正常工作)'),\n report.items.filter((item) => !isAttentionItem(item)).map((item) =>\n React.createElement('span', { key: item.id, 'data-ilife-health': 'ok-item' }, '· ' + item.title),\n ),\n )\n : null,\n )\n : null,\n );\n}\n","/** 「本机根清单」的取数接线(客户端半):把一通电话变成图上那一行可跳转的根。\n *\n * **不认识宿主**:载体基段、通道名(第二段)、电话名全由调用方传进来——各家从自己的宿主镜像里拿\n * 那几个字面量(和 `openRowBrowser` 的 `refusalCode` 同一条规矩)。本件只做两件事:\n * 发一次调用、把回执信封拆成根清单;**任何失败都当空清单**(图上少画一行,不报错、不挡路)。\n */\n\nimport type { RootKind, RootRow } from './directory-browser-contract.js';\n\n/** 取数口:`connection.rpc.call` 的形状,每次现取(连接后到也不永久缺席)。 */\nexport type RootsCall = (\n base: string,\n endpoint: string,\n payload: unknown,\n signal?: AbortSignal,\n) => Promise<unknown>;\n\n/** 认一认某个值像不像一个根(认不出的行丢掉:图的干净比多画一行重要)。 */\nfunction isRootRow(raw: unknown): raw is RootRow {\n if (typeof raw !== 'object' || raw === null) return false;\n const row = raw as { path?: unknown; kind?: unknown };\n return typeof row.path === 'string' && row.path !== '' && typeof row.kind === 'string';\n}\n\n/** 信封 → 根清单(纯函数,**永不抛**):`{ok:true, value:{roots:[…]}}` 之外的形状一律当空。 */\nexport function readRootsAnswer(raw: unknown): readonly RootRow[] {\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as { ok?: unknown; value?: unknown };\n if (answer.ok !== true) return [];\n const value = (typeof answer.value === 'object' && answer.value !== null ? answer.value : {}) as { roots?: unknown };\n if (!Array.isArray(value.roots)) return [];\n return value.roots.filter(isRootRow).map((row) => ({ path: row.path, kind: row.kind as RootKind }));\n}\n\n/** 建一个「根清单取数器」:给调用口与三个名字,回一个 `() => Promise<RootRow[]>`。\n *\n * 界面按需调它(开图一次),不在渲染里调,也不缓存到这里——缓存是宿主半的事。 */\nexport function createRootsSource(input: {\n readonly getCall: () => unknown;\n readonly base: string;\n readonly endpoint: string;\n readonly method: string;\n}): () => Promise<readonly RootRow[]> {\n return async () => {\n const call = input.getCall();\n if (typeof call !== 'function') return [];\n try {\n return readRootsAnswer(\n await (call as RootsCall)(input.base, input.endpoint, { method: input.method, payload: {} }),\n );\n } catch {\n return [];\n }\n };\n}\n","/** dsh-life-pack client 适配器(六边形:browser 侧;爱生活单卡方案)。\n *\n * 真实 DSH 契约(出处见 docs/agents/dsh-client-contract.md §5/§11):\n * - 本文件经 tsdown 打成 loader 工厂包(browser/CJS + 注册包裹),classic 执行\n * 只注册,副作用全在 factory 内;传递闭包禁 node 内建与 ESM 语法(回路\n * test/client-bundle-48.test.mjs 看门)。\n * - inject 短名 ['slots','connection']:本包现在有 RPC(装与更新的能力由宿主半提供,\n * 电话名与轮询间隔由宿主转交,面板不写死),读了 ctx.connection 就必须声明 'connection'(#48 血例)。\n * - 爱生活页签条走 slot 驱动(settings-plugins 先例,禁纯 useState 手画 tab):\n * section 声明 children 爱生活页签槽,各技能往里注册自家技能设置页;\n * tab 行从 ledger 投影(entries+getVersion+subscribe),面板用 props.renderSlot\n * 按 `{only}` 投影(一次只挂载一个)+ visited 缓存;ledger 无某技能=缺席,\n * 显示推荐安装只读文本(不返空冒充)。\n * - 组件 React.createElement 手写(禁 JSX),无数据轮询、无 document/window/process。\n * - 面板外壳两处(票 #679):标题行右上角两个入口(星/气泡,悬停出说明文字,\n * 窄窗口折到标题下方);页签面板之后是底部「作者其他插件」引流卡(四行,点开新窗口)。\n * 文案与网址都取自 nav.ts 的两张静态表,本文件只画不算。\n */\n\nimport * as React from 'react';\nimport { MANAGER_PLUGIN, MANAGER_TABS, MORE_PLUGINS, PANEL_LINKS, recoFor } from './nav.js';\nimport type { ManagerTab } from './nav.js';\nimport { CONFIG_TAB_SLOT } from './update-contract.js';\nimport { managerCallAdapter, mountLifeBatchPanel, mountLifeUpdateEntry } from './update-dialog.js';\nimport { tabInteractionCss } from './config-panel-view.js';\nimport { interactionCss } from './config-panel-view.js';\nimport { LIQUID_BASE_MS, LIQUID_DIST_FACTOR, LIQUID_EASE, LIQUID_GHOST_MS, LIQUID_MAX_MS, LIQUID_MIN_MS, LIQUID_STRETCH_X, LIQUID_STRETCH_Y } from './config-panel-contract.js';\nimport { summaryErrorOf, useHealthPanel } from './health-panel.js';\nimport { HealthSummaryLine, HealthTable, STATUS_TEXT, TAB_DOT, TAB_NOTE_STYLE, lightsOf, tabDotColor, tabNote } from './health-view.js';\nimport { HEALTH_ENDPOINT } from './health-contract.js';\nimport type {\n ClientCtx,\n ConfigTabRow,\n LifePackSectionProps,\n RpcCallFace,\n SlotLedgerEntry,\n} from './dsh-ctx.js';\n\nexport const inject = ['slots', 'connection'];\n\n/** 爱生活页签槽名:定义在 `update-contract.ts`(宿主判「已装产物有没有注册代码」也用这个名字,一处定义)。 */\nexport { CONFIG_TAB_SLOT } from './update-contract.js';\n\n/** 共用件「目录浏览器」从包门转出(票 #744):六个单品插件的设置页要用的就是这两样——\n * 「开图接线 + 入口三态判定」(纯逻辑,`./directory-browser` 子路径)与\n * 「对话框组件」(`./directory-browser-ui` 子路径,吃 React)。\n *\n * 六家的 client 束**不能**从本条 `./client` 取:这一条是 loader 工厂包\n * (`window.__ModuleLoader__.load(...)` 的注册壳),`require` 拿到的是空 exports。\n * 所以对外要用的是那两个普通子路径;这里只是同源转出,供本包内部与用例使用。 */\nexport { DirectoryBrowserFromRow } from './directory-browser-ui.js';\nexport type { DirectoryBrowserLabels, DirectoryBrowserProps } from './directory-browser-ui.js';\nexport { createBrowseController, createDirectoryRowBrowser, rowsOf, canGoUp, targetOf, entryPath } from './directory-browser-state.js';\nexport type { BrowseController, BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nexport { pickerModeOf, readPickAnswer } from './directory-browser-contract.js';\nexport type { DirectoryBrowseFace, DirectoryListing, PickOutcome, RootKind, RootRow } from './directory-browser-contract.js';\nexport { createRootsSource, readRootsAnswer } from './directory-browser-roots.js';\n\n/** 包名 → 本家那条客户端通道(票 #735)。取值面是导航表那份镜像,不再从页签槽账本的自定义选项里读:\n * 装机槽位面不透传自定义键,读了恒是空串(详见 `nav.ts` 上 `ManagerTab.channel` 的注释)。 */\nconst CHANNEL_BY_PLUGIN = new Map(MANAGER_TABS.map((tab) => [tab.plugin, tab.channel]));\n\n/** 总管视觉(内联 style;颜色走 DSH 主题别名,深浅主题自适应,写死值只做回退;与技能面板同语言)。 */\nconst S = {\n headRow: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: 8,\n margin: '2px 0 8px',\n } as React.CSSProperties,\n head: { fontSize: 15, fontWeight: 700, flex: '0 0 auto', color: 'var(--dsw-alias-label-primary, inherit)' } as React.CSSProperties,\n headActions: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 2,\n marginLeft: 'auto',\n flex: '0 0 auto',\n } as React.CSSProperties,\n iconLink: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 26,\n height: 26,\n borderRadius: 6,\n border: '1px solid transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n textDecoration: 'none',\n fontSize: 14,\n lineHeight: 1,\n } as React.CSSProperties,\n /** #933:页签格里「配置卡 + 体检卡」这一列的堆叠间距(唯一口径,别再给某一张卡加 margin)。 */\n tabStack: { display: 'flex', flexDirection: 'column', gap: 10 } as React.CSSProperties,\n /** #937:标题行里那枚版本胶囊(形状照真源 v3.1 的 `.ic-ver`:10px 字、1px 描边、999 圆角)。 */\n versionCapsule: {\n fontSize: 10.5,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 999,\n padding: '0 7px',\n whiteSpace: 'nowrap',\n flex: '0 0 auto',\n fontWeight: 400,\n } as React.CSSProperties,\n tablist: { display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 10 } as React.CSSProperties,\n tab: {\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n borderRadius: 999,\n padding: '4px 12px',\n fontSize: 13,\n cursor: 'pointer',\n } as React.CSSProperties,\n tabActive: {\n background: 'var(--dsw-alias-brand-primary, #0a84ff)',\n borderColor: 'transparent',\n // 前景取**与 brand-primary 成对**的宿主别名(#931):写死 `#fff` 会在深色主题下撞色\n // (宿主的 brand-primary 在深色是近白 `#f9fafb`,白字压上去只有 1.045:1)。\n color: 'var(--dsw-alias-label-primary-foreground, #0f1115)',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border-l1, rgba(128,128,128,.45))',\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n cmd: {\n display: 'block',\n marginTop: 8,\n padding: '8px 10px',\n borderRadius: 8,\n background: 'var(--dsw-alias-bg-base, rgba(128,128,128,.12))',\n color: 'var(--dsw-alias-label-primary, inherit)',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n userSelect: 'all',\n } as React.CSSProperties,\n moreCard: {\n marginTop: 16,\n padding: '10px 12px',\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n } as React.CSSProperties,\n moreTitle: {\n fontSize: 13,\n fontWeight: 700,\n marginBottom: 4,\n color: 'var(--dsw-alias-label-primary, inherit)',\n } as React.CSSProperties,\n moreRow: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '2px 8px',\n padding: '7px 4px',\n textDecoration: 'none',\n color: 'inherit',\n } as React.CSSProperties,\n morePkg: {\n flex: '0 1 auto',\n minWidth: 0,\n overflowWrap: 'anywhere',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n fontWeight: 650,\n } as React.CSSProperties,\n moreDesc: {\n flex: '1 1 180px',\n minWidth: 0,\n fontSize: 11.5,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n moreIcon: {\n flex: '0 0 auto',\n marginLeft: 'auto',\n display: 'inline-flex',\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n /** 更新入口挂点:只占位,按钮本体由上游入口件渲染(票 1170)。 */\n updateEntrySlot: {\n display: 'inline-flex',\n alignItems: 'center',\n } as React.CSSProperties,\n /** 批量弹窗遮罩(自家壳:颜色走主题变量,按钮复用 interactionCss 同一缝)。 */\n updateBackdrop: {\n position: 'fixed',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 16,\n background: 'rgba(0, 0, 0, 0.45)',\n zIndex: 1000,\n } as React.CSSProperties,\n updateBox: {\n position: 'relative',\n width: 'min(720px, 92vw)',\n maxHeight: '84vh',\n overflow: 'auto',\n borderRadius: 12,\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n background: 'var(--dsw-alias-bg-layer-1, #232324)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n padding: '12px 12px 16px',\n } as React.CSSProperties,\n updateClose: {\n position: 'sticky',\n top: 0,\n float: 'right',\n marginLeft: 8,\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n borderRadius: 999,\n padding: '4px 12px',\n fontSize: 13,\n cursor: 'pointer',\n } as React.CSSProperties,\n};\n\n/** 更新区(票 1170 薄接线):标题行挂入口按钮,弹窗里是七目标批量面板;缺席页签仍显示静态推荐,不进批量 targets。 */\n\n/** 标签解析(resolveSlotLabel 同形:thunk 跟活,无则空字串;见 slots lib:27-29)。 */\nfunction resolveLabel(label: SlotLedgerEntry['options']['label']): string {\n if (typeof label === 'function') return label();\n return label ?? '';\n}\n\n/** 外链图标(行尾那个;纯装饰,语义在链接自己的 aria-label/文字上)。 */\nfunction ExternalIcon(): React.ReactElement {\n return React.createElement(\n 'svg',\n {\n viewBox: '0 0 24 24',\n width: 13,\n height: 13,\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.9,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n 'aria-hidden': 'true',\n focusable: 'false',\n },\n React.createElement('path', { d: 'M13.5 4.5H19.5V10.5' }),\n React.createElement('path', { d: 'M19.5 4.5L11 13' }),\n React.createElement('path', { d: 'M18 14.5V18A2 2 0 0 1 16 20H6A2 2 0 0 1 4 18V8A2 2 0 0 1 6 6H9.5' }),\n );\n}\n\n/** 标题行右上角两件:星(去本仓点 Star)+ 气泡(去本仓开 issue),悬停出说明文字。\n *\n * 三件并排里的第三件「检查更新」住隔壁票(#678):本票只留位子——它就接在本组件之前,\n * 不画一个点了没反应的假按钮。窄窗口靠 headRow 的 flexWrap 折到标题下方,\n * 标题与两件都是 flex:'0 0 auto',谁也不挤谁。\n */\nfunction PanelActions(): React.ReactElement {\n return React.createElement(\n 'div',\n { style: S.headActions },\n PANEL_LINKS.map((link) =>\n React.createElement(\n 'a',\n {\n key: link.key,\n href: link.url,\n target: '_blank',\n rel: 'noreferrer',\n title: link.tip,\n 'aria-label': link.tip,\n style: S.iconLink,\n },\n React.createElement('span', { 'aria-hidden': 'true' }, link.glyph),\n ),\n ),\n );\n}\n\n/** 底部「作者其他插件」引流卡:四行(包名 + 一句说明 + 行尾外链图标),点开新窗口。\n *\n * 卡片在页签面板之后,六页签切换它都在(它不属于任何一个页签)。\n */\nfunction MorePluginsCard(): React.ReactElement {\n return React.createElement(\n 'div',\n { style: S.moreCard },\n React.createElement('div', { style: S.moreTitle }, '作者其他插件'),\n MORE_PLUGINS.map((row) =>\n React.createElement(\n 'a',\n {\n key: row.pkg,\n href: row.url,\n target: '_blank',\n rel: 'noreferrer',\n title: row.pkg + ' 的 GitHub 仓库',\n style: S.moreRow,\n },\n React.createElement('span', { style: S.morePkg }, row.pkg),\n React.createElement('span', { style: S.moreDesc }, row.desc),\n React.createElement('span', { style: S.moreIcon }, React.createElement(ExternalIcon, null)),\n ),\n ),\n );\n}\n\n/** 总管自述版本(票 #737;#937 改口):**不手写**,问宿主——宿主读自己这份已安装包的 `package.json`。\n *\n * 三态(#937):`loading`(首帧就画胶囊占位 `…`)/`ready`(读到,画包名 · 版本)/`missing`(读不到,画「版本未知」)。\n * **失败要重试**:原先只取一次、一失败就永远停在未知(#937 查出来的真缺陷);这里按 1s/3s/8s 退避重试三次。\n * 与卡路里 #130 同一条路:面板是浏览器产物、禁 node 内建,读盘只许在宿主半。 */\n/** 读中 / 读不到 两种胶囊文案(#937:占位符用 `…`,读不到给人话,不把 `unknown` 印给人看)。 */\n/** 构建期注入的版本号(tsdown `define`,值取自本包 `package.json` 的 `version`;票 #986)。\n *\n * 为什么烙进产物而不是问宿主:这行字是**标签**,延迟不该依赖宿主可用性——此前走\n * `ilife-manager.version` 电话,面板要先过一次往返,接不上还要按退避表等,首帧只能显示占位。\n * 漂移(改了包描述文件却忘了重建产物)由构建门咬住:产物里的注入值必须等于包版本。 */\ndeclare const __LIFE_PACK_VERSION__: string;\n\n/** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */\nconst MANAGER_VERSION_TEXT = MANAGER_PLUGIN + ' · ' + __LIFE_PACK_VERSION__;\n\n/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */\nfunction LifePackSection(props: LifePackSectionProps & { getCall: () => RpcCallFace | null }): React.ReactElement {\n const tabsId = React.useId();\n const tabRefs = React.useRef<Array<HTMLButtonElement | null>>([]);\n const rows: ConfigTabRow[] = props.useTabs((value) => value);\n const present = new Set(rows.map((r) => r.id));\n // 配置体检(#706):一张表六份报告,顶部那行汇总与各家那张表都从它读(同一份数据)。\n // 通道名的来源见 CHANNEL_BY_PLUGIN(#735:账本那一格读不到,改取导航表那份镜像)。\n const healthTabs = React.useMemo(\n () => rows.filter((row) => row.channel.length > 0).map((row) => ({ id: row.id, channel: row.channel })),\n [rows],\n );\n const health = useHealthPanel(() => props.getCall(), healthTabs);\n /** 六家的灯(票 #732):按**页签槽账本**那一排过(装了但没体检通道的也在内,它显缺席态)。 */\n const lights = React.useMemo(\n () => lightsOf(\n rows.map((row) => ({ id: row.id, title: row.label, hasChannel: row.channel.length > 0 })),\n Object.fromEntries(Object.entries(health.rows).map(([id, row]) => [id, row.report ?? undefined])),\n ),\n [rows, health.rows],\n );\n const lightOf = (id: string) => lights.find((light) => light.id === id);\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined);\n /** 批量弹窗开关(票 1170):入口件 onActivate 置真,自家关闭按钮与 Esc 置假。 */\n const [batchOpen, setBatchOpen] = React.useState(false);\n const entryRef = React.useRef<HTMLSpanElement | null>(null);\n const dialogRef = React.useRef<HTMLDivElement | null>(null);\n const callReady = props.getCall() !== null;\n /** 入口件挂载:连接后到才挂(取用器现取),卸载即 unmount(只停入口轮询)。 */\n React.useEffect(() => {\n const mount = entryRef.current;\n if (mount === null || !callReady) return;\n const entry = mountLifeUpdateEntry(mount, managerCallAdapter(props.getCall()), () => {\n setBatchOpen(true);\n });\n return () => {\n entry.unmount();\n };\n // getCall 本身是取用器(引用稳定),只跟连接就绪态重跑。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [callReady]);\n /** 批量面板挂载:弹窗开才挂,关即 unmount(只停轮询,宿主侧安装继续跑)。 */\n React.useEffect(() => {\n const mount = dialogRef.current;\n if (mount === null || !batchOpen || !callReady) return;\n const panel = mountLifeBatchPanel(mount, managerCallAdapter(props.getCall()));\n return () => {\n panel.unmount();\n };\n // 同上。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [batchOpen, callReady]);\n function closeBatch(): void {\n setBatchOpen(false);\n }\n function onDialogKeyDown(event: React.KeyboardEvent): void {\n if (event.key === 'Escape') {\n event.stopPropagation();\n setBatchOpen(false);\n }\n }\n const [visitedIds, setVisitedIds] = React.useState<ReadonlySet<string>>(() => new Set());\n const active = MANAGER_TABS.some((t) => t.plugin === activeId)\n ? (activeId as string)\n : MANAGER_TABS[0].plugin;\n React.useEffect(() => {\n setVisitedIds((previous) => {\n if (previous.has(active)) return previous;\n return new Set([...previous, active]);\n });\n }, [active]);\n /** 预热:section 一挂载,就把其余几家**错峰**挂上(隐藏面板照常挂载、只切 `hidden`),\n * 让各家那一次配置读提前跑掉——用户点哪个页签,多半值已经在了。\n * 错峰(每 150ms 一家)是为了不让六家子进程同时抢开工:首帧那一口气优先留给框架。\n * 只在页签账本非空时起一次(`rows` 要等各家 slot 注入后才齐)。\n *\n * **一次性**(p10 定时器门的例外依据):每个页签只挂一次(`warmed` 闸门 + 一批固定数量的定时器),\n * 不是轮询、也不是重试——它没有「次数」可言;卸载即 `clearTimeout` 清干净。\n * 不做轮询是本意:这里只补一次提前量,取数与刷新仍旧由各家用例自己的通道走。 */\n const warmed = React.useRef(false);\n const tabCount = rows.length;\n React.useEffect(() => {\n if (warmed.current || tabCount === 0) return;\n warmed.current = true;\n const STAGGER_MS = 150;\n const timers = MANAGER_TABS\n .filter((tab) => rows.some((row) => row.id === tab.plugin))\n .map((tab, index) => setTimeout(() => {\n setVisitedIds((previous) => (previous.has(tab.plugin) ? previous : new Set([...previous, tab.plugin])));\n }, STAGGER_MS * (index + 1)));\n return () => {\n for (const timer of timers) clearTimeout(timer);\n };\n }, [tabCount]);\n function labelFor(tab: ManagerTab): string {\n const row = rows.find((r) => r.id === tab.plugin);\n return row && row.label.length > 0 ? row.label : tab.title;\n }\n void 0;\n function onTabKeyDown(event: React.KeyboardEvent, index: number): void {\n let nextIndex: number | undefined;\n switch (event.key) {\n case 'ArrowRight': nextIndex = (index + 1) % MANAGER_TABS.length; break;\n case 'ArrowLeft': nextIndex = (index - 1 + MANAGER_TABS.length) % MANAGER_TABS.length; break;\n case 'Home': nextIndex = 0; break;\n case 'End': nextIndex = MANAGER_TABS.length - 1; break;\n default: return;\n }\n event.preventDefault();\n const next = MANAGER_TABS[nextIndex];\n setActiveId(next.plugin);\n tabRefs.current[nextIndex]?.focus();\n }\n /** T6 液态彗星式滑移(#1165):glider 在下层走 pill,页签自身透明(选中态内联透明+CSS 兜底)。\n *\n * 原型 `liquidTo` 原式:时长 200+距离×0.35、夹 220–380ms、缓动 cubic-bezier(.3,1.1,.4,1);\n * 滑移中 glider 在 45% 处拉伸 1.28/.86(Web Animations,失败则 CSS 过渡仍送到位);\n * ghost 影子留在起点、180ms 消散(彗星式,非桥接残影)。首挂只摆位不动。\n * 无 document/window/process:只经 refs 量量(offset*)与写样式;减少动态由 CSS 媒体查询接管(本 effect 不判断)。 */\n const gliderRef = React.useRef<HTMLSpanElement | null>(null);\n const ghostRef = React.useRef<HTMLSpanElement | null>(null);\n const prevTabRect = React.useRef<{ readonly left: number; readonly top: number; readonly width: number; readonly height: number } | null>(null);\n React.useEffect(() => {\n const index = MANAGER_TABS.findIndex((tab) => tab.plugin === active);\n const el = index < 0 ? null : tabRefs.current[index];\n const glider = gliderRef.current;\n const ghost = ghostRef.current;\n if (el === null || el === undefined || glider === null || ghost === null) return;\n const next = { left: el.offsetLeft, top: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight };\n const prev = prevTabRect.current;\n prevTabRect.current = next;\n const place = (rect: { readonly left: number; readonly top: number; readonly width: number; readonly height: number }): void => {\n glider.style.left = String(rect.left) + 'px';\n glider.style.top = String(rect.top) + 'px';\n glider.style.width = String(rect.width) + 'px';\n glider.style.height = String(rect.height) + 'px';\n };\n if (prev === null || (prev.left === next.left && prev.top === next.top && prev.width === next.width)) {\n place(next);\n return;\n }\n const dist = Math.abs(next.left - prev.left);\n const dur = Math.min(LIQUID_MAX_MS, Math.max(LIQUID_MIN_MS, LIQUID_BASE_MS + dist * LIQUID_DIST_FACTOR));\n ghost.style.transition = 'none';\n ghost.style.left = String(prev.left) + 'px';\n ghost.style.top = String(prev.top) + 'px';\n ghost.style.width = String(prev.width) + 'px';\n ghost.style.height = String(prev.height) + 'px';\n ghost.style.opacity = '1';\n void ghost.offsetWidth;\n ghost.style.transition = 'opacity ' + String(LIQUID_GHOST_MS / 1000) + 's ease';\n ghost.style.opacity = '0';\n glider.style.transition = 'none';\n place(prev);\n void glider.offsetWidth;\n glider.style.transition =\n 'left ' + String(dur) + 'ms ' + LIQUID_EASE + ',top ' + String(dur) + 'ms ' + LIQUID_EASE + ',width ' + String(dur) + 'ms ' + LIQUID_EASE;\n place(next);\n try {\n glider.animate(\n [\n { transform: 'scaleX(1) scaleY(1)' },\n { transform: 'scaleX(' + String(LIQUID_STRETCH_X) + ') scaleY(' + String(LIQUID_STRETCH_Y) + ')', offset: 0.45 },\n { transform: 'scaleX(1) scaleY(1)', offset: 0.8 },\n { transform: 'scaleX(1) scaleY(1)' },\n ],\n { duration: dur, easing: 'ease-out' },\n );\n } catch {\n // 无 Web Animations 也无妨:left/top/width 的 CSS 过渡仍把滑移送到位\n }\n }, [active]);\n return React.createElement(\n 'div',\n null,\n React.createElement(\n 'div',\n { style: S.headRow },\n React.createElement('div', { style: S.head }, '爱生活'),\n // #986:版本号构建期注入,首帧就是真值;不再有读中/读不到两态(漂移由构建门守)。\n React.createElement(\n 'span',\n { style: S.versionCapsule, 'data-ilife-version': 'capsule' },\n MANAGER_VERSION_TEXT,\n ),\n React.createElement(\n 'div',\n { style: S.headActions },\n React.createElement('span', {\n style: S.updateEntrySlot,\n ref: (element: HTMLSpanElement | null) => {\n entryRef.current = element;\n },\n }),\n React.createElement(PanelActions, null),\n ),\n ),\n React.createElement(HealthSummaryLine, {\n lights,\n running: health.running,\n // #735:取数失败也要在这一行看得见(各家的错只画在那家页签里的表上,摘要行沉默=用户以为按钮坏了)。\n error: summaryErrorOf(health),\n onRun: health.run,\n }),\n React.createElement(\n 'div',\n { role: 'tablist', 'aria-label': '爱生活技能页签', style: { ...S.tablist, position: 'relative', isolation: 'isolate' }, 'data-ilife-tablist': 't6' },\n // T6(#1165):一枚页签样式+goo 滤镜+glider 滑块层;页签按钮本身仍按 slot 账本+MANAGER_TABS 映射(机制不动,只加表现)。\n React.createElement('style', { 'data-ilife-tabs': 't6' }, tabInteractionCss()),\n React.createElement(\n 'svg',\n { width: 0, height: 0, style: { position: 'absolute' }, 'aria-hidden': 'true' },\n React.createElement(\n 'defs',\n null,\n React.createElement(\n 'filter',\n { id: 'ilife-goo' },\n React.createElement('feGaussianBlur', { in: 'SourceGraphic', stdDeviation: 6, result: 'b' }),\n React.createElement('feColorMatrix', { in: 'b', values: '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 22 -11' }),\n ),\n ),\n ),\n React.createElement(\n 'span',\n { 'data-ilife-glider-layer': 't6', 'aria-hidden': 'true' },\n React.createElement('span', {\n 'data-ilife-ghost': 't6',\n ref: (element: HTMLSpanElement | null) => {\n ghostRef.current = element;\n },\n }),\n React.createElement('span', {\n 'data-ilife-glider': 't6',\n ref: (element: HTMLSpanElement | null) => {\n gliderRef.current = element;\n },\n }),\n ),\n MANAGER_TABS.map((tab, index) => {\n const selected = tab.plugin === active;\n const light = lightOf(tab.plugin);\n // 圆点从「实心/空心=装没装」换成「颜色=体检档位」(票 #732):装了就是实心、\n // 颜色按档位走;没装的保持缺席态、不许点(点它只会跳到一张缺席卡)。\n const dotColor = light === undefined\n ? (present.has(tab.plugin) ? TAB_DOT.unchecked : TAB_DOT.absent)\n : tabDotColor(light);\n const note = light === undefined ? null : tabNote(light);\n return React.createElement(\n 'button',\n {\n key: tab.plugin,\n ref: (element: HTMLButtonElement | null) => {\n tabRefs.current[index] = element;\n },\n id: tabsId + '-tab-' + tab.plugin,\n type: 'button',\n role: 'tab',\n 'data-ilife-tab': 'tab',\n 'aria-selected': selected,\n 'aria-controls': tabsId + '-panel-' + tab.plugin,\n tabIndex: selected ? 0 : -1,\n // T6(#1165):选中态自身透明、glider 在下层给 pill(原型 V1 同形);减少动态时 CSS 以 !important 回实心 pill。\n style: selected ? { ...S.tab, ...S.tabActive, background: 'transparent', borderColor: 'transparent' } : S.tab,\n onClick: () => {\n setActiveId(tab.plugin);\n },\n onKeyDown: (event: React.KeyboardEvent) => {\n onTabKeyDown(event, index);\n },\n title: light === undefined\n ? '这一家没装(或产物没注册页签槽)'\n : light.title + ':' + (light.hasChannel\n ? (light.status === null ? '还没体检' : '体检读数见本页签下方的表')\n : '没有配置体检出口'),\n },\n React.createElement('span', {\n 'data-ilife-health': 'tab-dot',\n style: {\n width: 6,\n height: 6,\n borderRadius: '50%',\n background: dotColor,\n display: 'inline-block',\n flex: '0 0 auto',\n },\n }),\n labelFor(tab),\n note === null\n ? null\n : React.createElement(\n 'span',\n {\n 'data-ilife-health': 'tab-note',\n // 红黄各自那一截带**自己**的档位色,不许一个色管两档(#706 复评逮过的缺陷)。\n style: { ...TAB_NOTE_STYLE, color: note.status === null ? undefined : STATUS_TEXT[note.status] },\n },\n note.text,\n ),\n );\n }),\n ),\n MANAGER_TABS.filter((tab) => tab.plugin === active || visitedIds.has(tab.plugin)).map((tab) => {\n const selected = tab.plugin === active;\n return React.createElement(\n 'div',\n {\n key: tab.plugin,\n id: tabsId + '-panel-' + tab.plugin,\n role: 'tabpanel',\n 'aria-labelledby': tabsId + '-tab-' + tab.plugin,\n // #933:这一格**不能**用 `hidden` 属性——UA 的 `[hidden]{display:none}` 会输给下面内联的\n // `display:flex`,六个预热页签会全露出来。显式写 display 才是唯一口径(未选中的整格不画)。\n style: selected ? undefined : { display: 'none' },\n },\n present.has(tab.plugin)\n ? React.createElement(\n // #933:配置卡与体检卡之间的空隙由**容器**给(原先两张卡边线直接相接、间距 0)。\n // 取 10=壳里块间距的既有口径(与 `S.head`/`S.tablist` 那几处同值)。\n 'div',\n { style: S.tabStack },\n props.renderSlot(CONFIG_TAB_SLOT, {}, { only: tab.plugin }) as React.ReactNode,\n // 那家的体检表:与总览那行读同一份快照(`health.rows`),不是各算一遍。\n // #1069:体检头只留去 `·` 的分层标题,**不带签**(签只留配置头;负责人 2026-10-03 定)。\n React.createElement(HealthTable, {\n title: labelFor(tab),\n phase: health.rows[tab.plugin]?.phase ?? 'idle',\n report: health.rows[tab.plugin]?.report ?? null,\n error: health.rows[tab.plugin]?.error ?? null,\n }),\n )\n : (() => {\n const reco = recoFor(tab);\n return React.createElement(\n 'div',\n { style: S.reco },\n reco.hint,\n React.createElement('span', { style: S.cmd }, reco.installCmd),\n );\n })(),\n );\n }),\n batchOpen\n ? React.createElement(\n 'div',\n {\n role: 'dialog',\n 'aria-modal': 'true',\n 'aria-label': '批量更新',\n style: S.updateBackdrop,\n onKeyDown: onDialogKeyDown,\n },\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n React.createElement(\n 'div',\n { style: S.updateBox },\n React.createElement(\n 'button',\n {\n type: 'button',\n 'data-ilife-press': '',\n style: S.updateClose,\n 'aria-label': '关闭更新面板(批量推进在宿主侧继续跑)',\n onClick: closeBatch,\n },\n '关闭',\n ),\n React.createElement('div', {\n ref: (element: HTMLDivElement | null) => {\n dialogRef.current = element;\n },\n }),\n ),\n )\n : null,\n React.createElement(MorePluginsCard, null),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器:每次取数时现取(connection 后到也不永久缺席),透传给面板组件。\n const getCall = (): RpcCallFace | null => (ctx.connection?.rpc?.call as RpcCallFace | undefined) ?? null;\n // ledger 观测源(settings-plugins:1744-1767 同形;无 locale 面声明,故只订 ledger)。\n let tabsVersion = -1;\n let tabs: ConfigTabRow[] = [];\n const sectionInjected = () => ({\n hooks: {\n tabs: {\n getSnapshot: () => {\n const version = ctx.slots.getVersion(CONFIG_TAB_SLOT);\n if (version !== tabsVersion) {\n tabsVersion = version;\n tabs = ctx.slots\n .entries(CONFIG_TAB_SLOT)\n .map((entry) => ({\n id: entry.options.id ?? '',\n order: entry.options.order ?? 0,\n label: resolveLabel(entry.options.label),\n // #735 配置体检的通道:取自导航表(账本那一格读不到)。表里没有这家 ⇒ 空串 ⇒\n // 这家没有体检出口(灯显缺席态、按钮不会为它取数)。\n channel: CHANNEL_BY_PLUGIN.get(entry.options.id ?? '') ?? '',\n }))\n .sort((a, b) => a.order - b.order);\n }\n return tabs;\n },\n subscribe: (listener: () => void) => ctx.slots.subscribe(CONFIG_TAB_SLOT, listener),\n },\n },\n });\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register(\n {\n name: 'settings.section',\n id: 'dsh-life-pack',\n order: 21,\n label: () => '爱生活',\n inject: sectionInjected,\n children: { 'ilife.config-tab': { kind: 'list', scope: 'root' } },\n },\n (props: LifePackSectionProps) => React.createElement(LifePackSection, { ...props, getCall }),\n ),\n );\n}\n"],"x_google_ignoreList":[2,3,4,5,6,7,8,9],"mappings":"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;IACL,QAAQ;KAAE,MAAM;KAAQ,OAAO;KAAgE,QAAQ;IAAG;IAC1G,gBAAgB;IAChB,cAAc;IACd,eAAe;IACf,YAAY;IACZ,WAAW;IACX;GACF;EACF;EACA,MAAM,mBAAmB;GACvB;GACA;GAGA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GAGA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;EACF,CAAC,CAAC,KAAK,IAAI;EACX,MAAM,sBAAsB;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAGA;GACA;GACA;GACA;GAKA;GACA;GACA;GACA;GACA;GACA;GAGA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAGA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GAEA;GACA;GAEA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC,CAAC,KAAK,IAAI;EACX,SAASC,aAAW,MAAM;GACxB,OAAO,OAAO,IAAI,CAAC,CAAC,QAAQ,MAAM,OAAO,CAAC,CAAC,QAAQ,MAAM,M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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["title","escapeHtml","g","escapeHtml","escapeHtml","isObject","S","React","React","S","S","React","S","S","React","React","React"],"sources":["../src/nav.ts","../src/update-contract.ts","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/config.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/redaction.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/service.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/changelog.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/log-events.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/panel.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/lang.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/bilingual.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/entry.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/batch.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/panel-batch.js","../../../node_modules/.pnpm/dsh-plugin-update@0.5.8/node_modules/dsh-plugin-update/dist/entry-batch.js","../src/install.ts","../src/update-targets.ts","../src/update-dialog.ts","../src/config-panel-contract.ts","../src/directory-browser-contract.ts","../src/directory-browser-state.ts","../src/directory-browser-parts.ts","../src/directory-browser-ui.ts","../src/title-seal.ts","../src/seal-paper-schemes.ts","../src/seal-scroll.ts","../src/seal-stamp.ts","../src/config-panel-view.ts","../src/health-contract.ts","../src/health-fetch.ts","../src/health-panel.ts","../src/health-view.ts","../src/directory-browser-roots.ts","../src/client.ts"],"sourcesContent":["/** dsh-life-pack 导航表(P10 脚手架,P3 #4 定案 order)。\n *\n * 总管只导航:6 tab 描述子仅做 openTab 路径导航,内容由各单品包自注册\n * slot 提供;总管不 import 任何单品页/单品包(见 boundaries 断言)。\n * 缺席纯条件渲染:present 缺谁就回谁的 reco(推荐安装+补装提示),不轮询。\n * 子页不占栏:卡路里 diet/exercise/goal 等 ilife:calorie:* 后缀只在单品\n * 主面板内导航,绝不在此表另起 tab 行。\n * 60 deck:map 为 matt 探针已占位,本表不动它。\n *\n * 面板外壳的两张文案表也在本文件(票 #679):右上角两个入口 PANEL_LINKS、\n * 底部「作者其他插件」卡 MORE_PLUGINS。文案与网址集中在这里,\n * 组件只负责画——改文案不碰组件(map #671 的 Q11 决议)。\n */\n\nexport interface ManagerTab {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n /** 页签名(屏上逐字印这个)=**产品名**(票 #738:作息管家/居家管家/私家大厨/饼干记账)。\n *\n * 它是各家自己那个 `SLOT_TITLE` 的镜像,两处必须同值:页签注册缺席时由这一格兜底,\n * 更新列表那一行的行首也用它(`update-targets.ts` 原样取 `title`)。\n * 对齐由 `test/plugin-p10-boundaries.test.mjs` 逐家咬住——真 import 两边的产物来比,不搜字符串。 */\n readonly title: string;\n /** 提供该 tab 的单品插件包名(仅文档级引用,不 import)。 */\n readonly plugin: string;\n /** 本家自己那条客户端通道(单段,例 `/ilife-calorie`):面板按它取配置体检(体检那个端点挂在上面)。\n *\n * 这条事实为什么住在这里(票 #735):各家注册页签槽时**也**会交一份(各包契约件的 `RPC_CHANNEL`),\n * 但装机上的槽位面**不透传自定义注册选项**——条目 options 只留 `{key,id,order,label,priority}`,\n * 总管从账本上读到的那一格恒是空串。后果是六家恒被判成「没有体检出口」,\n * 面板的体检目标表恒为空,「体检一次」按下去毫无反应(第 1 版的实现就是这么哑掉的)。\n * 这份镜像由 `test/plugin-p10-boundaries.test.mjs` 逐家与各包契约件的常量对齐守着。\n * 各家注册里那一格**留着不删**:上游哪天开始透传自定义选项,可以一行切回账本。 */\n readonly channel: string;\n}\n\nexport interface RecoTab extends ManagerTab {\n readonly kind: 'reco';\n /** 补装命令(单命令双包口径,总管必须已在位)。 */\n readonly installCmd: string;\n readonly hint: string;\n}\n\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\n/** 版本号**不在本文件**:它由构建期从本包 `package.json` 注入面板产物(票 #986,见\n * `tsdown.config.ts` 与 `client.ts` 的 `__LIFE_PACK_VERSION__`)——手写常量会与包版本\n * 无声漂开一整版(#737 实测过),而注入值由构造派生、并有一条构建门咬住\n * 「产物里的注入值 ≡ 包版本」;定版仍只剩改 `package.json` 一处。 */\n\nexport const DUAL_ADD_PREFIX = 'dsh plugin add dsh-life-pack' as const;\n\nexport function dualInstallCmd(singlePlugin: string): string {\n return DUAL_ADD_PREFIX + ' ' + singlePlugin;\n}\n\n/** P3 定案 6 tab(order 升序即 + 号菜单顺序;已打开条不受 order 控制)。 */\nexport const MANAGER_TABS: readonly ManagerTab[] = [\n { skill: 'bill', slotId: 'ilife:cookie', order: 70, title: '饼干记账', plugin: 'dsh-bill-ilife', channel: '/ilife-bill-ilife' },\n { skill: 'calorie', slotId: 'ilife:calorie', order: 75, title: '卡路里', plugin: 'dsh-calorie', channel: '/ilife-calorie' },\n { skill: 'memo', slotId: 'ilife:memo', order: 80, title: '备忘录', plugin: 'dsh-memo-ilife', channel: '/ilife-memo' },\n { skill: 'schedule', slotId: 'ilife:schedule', order: 85, title: '作息管家', plugin: 'dsh-schedule-ilife', channel: '/ilife-schedule-ilife' },\n { skill: 'home', slotId: 'ilife:home', order: 90, title: '居家管家', plugin: 'dsh-home-ilife', channel: '/ilife-home-ilife' },\n { skill: 'chef', slotId: 'ilife:chef', order: 95, title: '私家大厨', plugin: 'dsh-chef', channel: '/ilife-chef' },\n];\n\nexport function recoFor(tab: ManagerTab): RecoTab {\n const installCmd = dualInstallCmd(tab.plugin);\n return {\n ...tab,\n kind: 'reco',\n installCmd,\n hint: '未安装[' + tab.plugin + '],请补装后使用:' + installCmd,\n };\n}\n\nexport type Presence = ReadonlySet<string>;\n\nexport function tabsForPresence(present: Presence): readonly (ManagerTab | RecoTab)[] {\n return MANAGER_TABS.map((t) => (present.has(t.slotId) ? t : recoFor(t)));\n}\n\nexport interface OpenPort {\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\nexport interface HasTabsPort extends OpenPort {\n hasTabs(): boolean;\n}\n\n/** 总管导航:只做 openTab 路径导航(内容型打开),缺席由调用方渲染 reco。 */\nexport function openManagerTab(port: HasTabsPort, slotId: string, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: slotId, path: slotId }, sessionId ? { sessionId } : undefined);\n}\n\n/** 面板右上角一个入口:图标 + 悬停说明 + 目标网址。 */\nexport interface PanelLinkRow {\n readonly key: 'star' | 'feedback';\n /** 图标字形(照参照实现 dsh-mattpocock-skills-deck,星星只留 ⭐、反馈只留 💬)。 */\n readonly glyph: string;\n /** 鼠标悬停给出的说明文字。 */\n readonly tip: string;\n readonly url: string;\n}\n\n/** 面板右上角两个入口:星(去本仓点 star)+ 气泡(去本仓开 issue)。\n *\n * 两处都指本仓 FeatherHunter/ilife——这是 map #671 裁定过的(「星与气泡指向哪个仓」\n * 一问:反馈要落在本仓的需求池,「引流到作者其他技能」是底部那张卡的事)。\n * 三件并排里的第三件「检查更新」住隔壁票(#678),本表不管它。\n */\nexport const PANEL_LINKS: readonly PanelLinkRow[] = [\n { key: 'star', glyph: '⭐', tip: '你的🌟是我夜空中最亮的星。', url: 'https://github.com/FeatherHunter/ilife' },\n { key: 'feedback', glyph: '💬', tip: '反馈问题', url: 'https://github.com/FeatherHunter/ilife/issues/new' },\n];\n\n/** 底部「作者其他插件」卡里的一行:包名 + 一句说明 + 行尾外链图标指向的仓库。 */\nexport interface MorePluginRow {\n readonly pkg: string;\n readonly desc: string;\n readonly url: string;\n}\n\n/** 「作者其他插件」四行:包名 + 一句说明 + 仓库地址。\n *\n * 文案口径(用户 2026-09-18 拍板):**不写数字**——数字会过期(用户截图里的「34 款」实测已到 38 款),\n * 写错最伤推荐位的可信度;第一行也不用 deck 自己面板里的自指「本面板自己」,改成不自指的说法。\n * 只收真实存在的仓库,网址已逐个验真。第一行的说明描述的是 deck 那个包(工程技能面板),不是爱生活面板。\n */\nexport const MORE_PLUGINS: readonly MorePluginRow[] = [\n {\n pkg: 'dsh-mattpocock-skills-deck',\n desc: '工程技能面板:装好就能在右侧直接用',\n url: 'https://github.com/FeatherHunter/dsh-mattpocock-skills-deck',\n },\n {\n pkg: 'dsh-opencode-palette',\n desc: '多款长时间编程护眼配色,一键换上',\n url: 'https://github.com/FeatherHunter/dsh-opencode-palette',\n },\n {\n pkg: 'dsh-prompt',\n desc: '常用提示模板随手点,不用来回复制粘贴',\n url: 'https://github.com/FeatherHunter/dsh-prompt',\n },\n {\n pkg: 'dsh-im-companion',\n desc: '聊天机器人的伴侣插件:扫码或填凭据就把飞书、微信等聊天接进来',\n url: 'https://github.com/FeatherHunter/dsh-im-companion',\n },\n];\n","/** 总管与面板之间的共享常量:载体、槽名、电话名、版本降级字面量。\n *\n * 说明(票 1168 缺席态):本仓更新代码已全部移除,更新区藏入口。\n * 本文件仅保留仍被非更新代码取用的共享位,更新专用部分已随更新代码删除。\n * 0.5.1 新接线由 wire 票 1170 重建,此处不预留。\n */\n\nexport const CONFIG_TAB_SLOT = 'ilife.config-tab' as const;\n\nexport const CARRIER_BASE = '/api' as const;\n\nexport const MANAGER_RPC = {\n base: CARRIER_BASE,\n channel: '/ilife-manager',\n endpoint: 'ilife-manager',\n path: '/api/ilife-manager',\n} as const;\n\nexport const MANAGER_ACTIONS = {\n roots: 'ilife-manager.roots',\n} as const;\n\nexport const VERSION_UNKNOWN = 'unknown' as const;\n\nconst REASON_TEXT: Record<string, string> = {\n 'manager-unreachable': '面板连不上总管插件。重启 DSH 后重试。',\n 'bad-request': '面板与总管之间的请求出错了(属程序缺陷)。',\n internal: '面板与总管之间的请求出错了(属程序缺陷)。',\n};\n\nexport function reasonText(code: string): string {\n return REASON_TEXT[code] ?? '面板不认识这个原因码(' + code + ')。';\n}\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/config.ts\nconst DEFAULT_PREFIX = \"wf\";\nconst PHONE_ACTIONS = [\"updateStatus\", \"updateCheck\", \"updateInstall\"];\nconst CHANGELOG_PHONE_ACTION = \"updateChangelog\";\nconst DEFAULT_TARGET_PACKAGE = \"dsh-mattpocock-skills-deck\";\nconst DEFAULT_REGISTRY = \"https://registry.npmjs.org/\";\nconst LEGACY_PLUGIN_ID = \"dsh-mattpocock-skills-deck\";\nconst LEGACY_DIR_SEGMENT = \"updates\";\nconst STATE_FILE = \"state.json\";\nconst LOCK_FILE = \"install.lock\";\nconst BACKUP_FILE = \"before.json\";\nconst SKIPPED_FILE = \"skipped.json\";\nconst RELEASE_CHANNELS = [\"stable\", \"prerelease\"];\nconst DEFAULT_CHECK_TIMEOUT_MS = 1e4;\nconst DEFAULT_CONFIRMATION_TTL_MS = 10 * 6e4;\nconst DEFAULT_INSTALL_TIMEOUT_MS = 15 * 6e4;\nconst DEFAULT_PANEL_POLL_MS = 1e3;\nconst RECHECK_WINDOW_MS = 2e3;\nconst MIN_PANEL_POLL_MS = 250;\nfunction assertPluginId(value) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n if (value.includes(\"/\") || value.includes(\"\\\\\") || value.includes(\"\\0\")) {\n throw new Error(\"[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u542B\\u6709\\u8DEF\\u5F84\\u5206\\u9694\\u7B26\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction assertPrefix(value, role) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n if (value.includes(\".\") || value.includes(\"/\") || value.includes(\"\\\\\") || /\\s/.test(value)) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u542B\\u6709\\u70B9\\u3001\\u8DEF\\u5F84\\u5206\\u9694\\u7B26\\u6216\\u7A7A\\u767D\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction assertPositiveFinite(value, role) {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new Error(\"[dsh-plugin-update] \" + role + \" \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u6709\\u9650\\u5927\\u4E8E 0 \\u7684\\u6570\\uFF08\\u6536\\u5230 \" + JSON.stringify(value) + \"\\uFF09\");\n }\n return value;\n}\nfunction resolveUpdateConfig(input) {\n if (!input || typeof input !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u5EFA\\u66F4\\u65B0\\u80FD\\u529B\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = assertPluginId(input.pluginId);\n const prefix = input.prefix === void 0 ? DEFAULT_PREFIX : assertPrefix(input.prefix, \"\\u7535\\u8BDD\\u540D\\u524D\\u7F00 prefix\");\n const targetPackageName = input.targetPackageName === void 0 ? DEFAULT_TARGET_PACKAGE : String(input.targetPackageName);\n if (!targetPackageName) {\n throw new Error(\"[dsh-plugin-update] \\u76EE\\u6807\\u5305\\u540D targetPackageName \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\");\n }\n const registryUrl = input.registryUrl === void 0 ? DEFAULT_REGISTRY : String(input.registryUrl);\n if (!registryUrl) {\n throw new Error(\"[dsh-plugin-update] \\u5B98\\u65B9\\u6E90 registryUrl \\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\");\n }\n const homeDir = input.homeDir === void 0 ? null : String(input.homeDir);\n const checkTimeoutMs = input.checkTimeoutMs === void 0 ? DEFAULT_CHECK_TIMEOUT_MS : assertPositiveFinite(input.checkTimeoutMs, \"\\u8054\\u7F51\\u8D85\\u65F6 checkTimeoutMs\");\n const confirmationTtlMs = input.confirmationTtlMs === void 0 ? DEFAULT_CONFIRMATION_TTL_MS : assertPositiveFinite(input.confirmationTtlMs, \"\\u51ED\\u8BC1\\u6709\\u6548\\u671F confirmationTtlMs\");\n const installTimeoutMs = input.installTimeoutMs === void 0 ? DEFAULT_INSTALL_TIMEOUT_MS : assertPositiveFinite(input.installTimeoutMs, \"\\u5B89\\u88C5\\u65F6\\u9650 installTimeoutMs\");\n const panelPollMs = input.panelPollMs === void 0 ? DEFAULT_PANEL_POLL_MS : assertPositiveFinite(input.panelPollMs, \"\\u9762\\u677F\\u8F6E\\u8BE2 panelPollMs\");\n if (panelPollMs < MIN_PANEL_POLL_MS) {\n throw new Error(\"[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2 panelPollMs \\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 \" + JSON.stringify(input.panelPollMs) + \"\\uFF09\");\n }\n const releaseChannel = input.releaseChannel === void 0 ? \"stable\" : input.releaseChannel;\n if (releaseChannel !== \"stable\" && releaseChannel !== \"prerelease\") {\n throw new Error(\"[dsh-plugin-update] \\u7248\\u672C\\u901A\\u9053 releaseChannel \\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 stable \\u6216 prerelease\\uFF08\\u6536\\u5230 \" + JSON.stringify(input.releaseChannel) + \"\\uFF09\");\n }\n return { pluginId, prefix, targetPackageName, registryUrl, homeDir, checkTimeoutMs, confirmationTtlMs, installTimeoutMs, panelPollMs, releaseChannel };\n}\nfunction buildPhoneNames(prefix) {\n const checked = assertPrefix(prefix, \"\\u7535\\u8BDD\\u540D\\u524D\\u7F00 prefix\");\n return {\n updateStatus: checked + \".updateStatus\",\n updateCheck: checked + \".updateCheck\",\n updateInstall: checked + \".updateInstall\",\n updateChangelog: checked + \".updateChangelog\"\n };\n}\nfunction buildChangelogPhoneName(prefix) {\n return buildPhoneNames(prefix).updateChangelog;\n}\nfunction buildPhoneName(prefix, action) {\n return buildPhoneNames(prefix)[action];\n}\nexport {\n BACKUP_FILE,\n CHANGELOG_PHONE_ACTION,\n DEFAULT_CHECK_TIMEOUT_MS,\n DEFAULT_CONFIRMATION_TTL_MS,\n DEFAULT_INSTALL_TIMEOUT_MS,\n DEFAULT_PANEL_POLL_MS,\n DEFAULT_PREFIX,\n DEFAULT_REGISTRY,\n DEFAULT_TARGET_PACKAGE,\n LEGACY_DIR_SEGMENT,\n LEGACY_PLUGIN_ID,\n LOCK_FILE,\n MIN_PANEL_POLL_MS,\n PHONE_ACTIONS,\n RECHECK_WINDOW_MS,\n RELEASE_CHANNELS,\n SKIPPED_FILE,\n STATE_FILE,\n assertPluginId,\n assertPrefix,\n buildChangelogPhoneName,\n buildPhoneName,\n buildPhoneNames,\n resolveUpdateConfig\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/redaction.ts\nconst DETAIL_MAX_CHARS = 300;\nconst COPY_BUDGET_CHARS = 1500;\nconst DIAG_INLINE_BUDGET_BYTES = 1024;\nconst REDACTED_PATH = \"<\\u8DEF\\u5F84>\";\nconst REDACTED_SECRET = \"<\\u8131\\u654F>\";\nconst KNOWN_GAP_FILE_URL_PREFIX = \"file:///\";\nconst REDACTION_RULE_NAMES = [\n \"R_ABSOLUTE_PATH\",\n \"R_URL_USERINFO\",\n \"R_TOKEN\",\n \"R_CRED_PAIR\",\n \"R_EMAIL\"\n];\nconst ENDORSED_REGISTRY_HOSTS = [\"registry.npmjs.org\"];\nconst ABSOLUTE_PATH_RE = /[A-Za-z]:\\\\[^\\s\"']*|\\\\\\\\[^\\s\"'()\\[\\];]+|(^|[\\s\"'(\\[=,])\\/\\/[^\\s\"'()\\[\\];]+|(^|[\\s\"'(\\[=:,])\\/(?!\\/)[^\\s\"'()\\[\\];]+/g;\nconst URL_USERINFO_RE = /[A-Za-z][A-Za-z0-9+.-]*:\\/\\/[^\\s/]*@/;\nconst NPM_TOKEN_RE = /\\bnpm_[A-Za-z0-9_-]{8,}/g;\nconst GITHUB_TOKEN_RE = /\\b(?:gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{10,})/g;\nconst SK_TOKEN_RE = /\\bsk-[A-Za-z0-9_-]{6,}/g;\nconst BEARER_TOKEN_RE = /(\\bbearer\\s+)[A-Za-z0-9._~+/-=]{6,}/gi;\nconst CRED_PAIR_RE = /(\\b(?:token|password|passwd|pwd|api[_-]?key|access[_-]?key|auth[_-]?token|secret|cookie)\\s*[:=]\\s*)(\"[^\"]+\"|'[^']+'|[^\\s'\";,)\\]]+)/gi;\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\nfunction flattenWhitespace(text) {\n return String(text ?? \"\").replace(/\\s+/g, \" \").trim();\n}\nfunction applyPathRule(text) {\n ABSOLUTE_PATH_RE.lastIndex = 0;\n return text.replace(ABSOLUTE_PATH_RE, (_whole, g1, g2) => {\n const lead = g1 ?? g2 ?? \"\";\n return `${lead}${REDACTED_PATH}`;\n });\n}\nfunction applyTokenRule(text) {\n NPM_TOKEN_RE.lastIndex = 0;\n GITHUB_TOKEN_RE.lastIndex = 0;\n SK_TOKEN_RE.lastIndex = 0;\n BEARER_TOKEN_RE.lastIndex = 0;\n return text.replace(NPM_TOKEN_RE, REDACTED_SECRET).replace(GITHUB_TOKEN_RE, REDACTED_SECRET).replace(SK_TOKEN_RE, REDACTED_SECRET).replace(BEARER_TOKEN_RE, (_whole, prefix) => `${prefix}${REDACTED_SECRET}`);\n}\nfunction applyCredPairRule(text) {\n CRED_PAIR_RE.lastIndex = 0;\n return text.replace(CRED_PAIR_RE, (_whole, head) => `${head}${REDACTED_SECRET}`);\n}\nfunction applyEmailRule(text) {\n EMAIL_RE.lastIndex = 0;\n return text.replace(EMAIL_RE, REDACTED_SECRET);\n}\nfunction hasUserinfo(text) {\n URL_USERINFO_RE.lastIndex = 0;\n return URL_USERINFO_RE.test(text);\n}\nfunction truncateToWordBoundary(text, max) {\n const input = String(text ?? \"\");\n if (input.length <= max) return input;\n const slice = input.slice(0, max);\n const lastSpace = slice.lastIndexOf(\" \");\n let cut = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;\n const lastOpen = cut.lastIndexOf(\"<\");\n const lastClose = cut.lastIndexOf(\">\");\n if (lastOpen > lastClose) cut = cut.slice(0, lastOpen).trimEnd();\n if (!cut) cut = slice;\n return `${cut}\\u2026`;\n}\nfunction truncateDetail(text) {\n return truncateToWordBoundary(text, DETAIL_MAX_CHARS);\n}\nfunction truncateForCopy(text) {\n return truncateToWordBoundary(text, COPY_BUDGET_CHARS);\n}\nfunction scrubWithoutBudget(text) {\n return applyEmailRule(applyCredPairRule(applyTokenRule(applyPathRule(text))));\n}\nfunction sanitizeDetail(text) {\n const flat = flattenWhitespace(text);\n if (!flat) return \"\";\n if (hasUserinfo(flat)) return \"\";\n return truncateDetail(scrubWithoutBudget(flat));\n}\nfunction sanitizeForCopy(text) {\n const flat = flattenWhitespace(String(text ?? \"\"));\n if (!flat) return \"\";\n if (hasUserinfo(flat)) return \"\";\n return truncateForCopy(scrubWithoutBudget(flat));\n}\nfunction resolveRegistryHost(registryUrl) {\n try {\n const raw = String(registryUrl ?? \"\").trim();\n if (!raw) return null;\n const host = new URL(raw).hostname.toLowerCase();\n if (!host) return null;\n return ENDORSED_REGISTRY_HOSTS.includes(host) ? host : null;\n } catch {\n return null;\n }\n}\nfunction diagByteLength(value) {\n try {\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n const encoder = globalThis.TextEncoder;\n if (typeof encoder === \"function\") return new encoder().encode(text ?? \"\").length;\n return String(text ?? \"\").length;\n } catch {\n try {\n return String(JSON.stringify(value) ?? \"\").length;\n } catch {\n return 0;\n }\n }\n}\nconst DIAG_NEVER_DROP_KEYS = [\"v\", \"code\", \"error\", \"errorKind\", \"stage\", \"route\", \"method\", \"requestId\"];\nconst DIAG_DROP_ORDER = [\n \"registryHost\",\n \"action\",\n \"latestVersion\",\n \"runningVersion\",\n \"targetPackageName\",\n \"latencyMs\",\n \"exitCode\",\n \"environmentKind\",\n \"checkId\",\n \"httpStatus\"\n];\nfunction isRecord(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction enforceDiagBudget(input) {\n const base = isRecord(input) ? { ...input } : {};\n const sizeOf = (obj) => diagByteLength(obj);\n const convergeDetailTo300 = (obj) => {\n const current = obj[\"detail\"];\n if (typeof current !== \"string\" || !current) return false;\n const next = truncateToWordBoundary(current, DETAIL_MAX_CHARS);\n if (next !== current) {\n obj[\"detail\"] = next;\n return true;\n }\n return false;\n };\n const shrinkDetailToFit = (obj) => {\n const current = obj[\"detail\"];\n if (typeof current !== \"string\" || !current) return false;\n let next = current;\n let changed = false;\n while (next && sizeOf({ ...obj, detail: next, truncated: true }) > DIAG_INLINE_BUDGET_BYTES) {\n const shorter = next.length - 50;\n if (shorter <= 0) {\n next = \"\";\n break;\n }\n const cut = truncateToWordBoundary(next.slice(0, shorter), shorter) || next.slice(0, shorter) + \"\\u2026\";\n if (cut.length >= next.length) {\n next = next.slice(0, shorter) + \"\\u2026\";\n } else {\n next = cut;\n }\n changed = true;\n }\n if (next !== current) {\n obj[\"detail\"] = next;\n return true;\n }\n return changed;\n };\n if (sizeOf({ ...base, truncated: base[\"truncated\"] ?? false }) <= DIAG_INLINE_BUDGET_BYTES) {\n const already = base[\"truncated\"] === true;\n return { ...base, truncated: already };\n }\n const working = { ...base };\n convergeDetailTo300(working);\n for (const key of DIAG_DROP_ORDER) {\n if (sizeOf({ ...working, truncated: true }) <= DIAG_INLINE_BUDGET_BYTES) break;\n if (key in working) {\n delete working[key];\n }\n }\n if (sizeOf({ ...working, truncated: true }) > DIAG_INLINE_BUDGET_BYTES && typeof working[\"detail\"] === \"string\" && working[\"detail\"]) {\n shrinkDetailToFit(working);\n if (sizeOf({ ...working, truncated: true }) > DIAG_INLINE_BUDGET_BYTES && typeof working[\"detail\"] === \"string\" && working[\"detail\"]) {\n working[\"detail\"] = \"\";\n }\n }\n for (const key of DIAG_NEVER_DROP_KEYS) {\n if (key in base && !(key in working)) working[key] = base[key];\n }\n return { ...working, truncated: true };\n}\nexport {\n COPY_BUDGET_CHARS,\n DETAIL_MAX_CHARS,\n DIAG_DROP_ORDER,\n DIAG_INLINE_BUDGET_BYTES,\n DIAG_NEVER_DROP_KEYS,\n ENDORSED_REGISTRY_HOSTS,\n KNOWN_GAP_FILE_URL_PREFIX,\n REDACTED_PATH,\n REDACTED_SECRET,\n REDACTION_RULE_NAMES,\n diagByteLength,\n enforceDiagBudget,\n resolveRegistryHost,\n sanitizeDetail,\n sanitizeForCopy,\n truncateToWordBoundary\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/service.ts\nconst PACKAGE_NAME = \"dsh-mattpocock-skills-deck\";\nconst NPM_REGISTRY = \"https://registry.npmjs.org/\";\nconst CHECK_TIMEOUT_MS = 1e4;\nconst CONFIRMATION_TTL_MS = 10 * 6e4;\nconst RECHECK_WINDOW_MS = 2e3;\nconst MAX_METADATA_BYTES = 256 * 1024;\nconst INTEGRITY_PATTERN = \"^sha512-[A-Za-z0-9+/]{86}==$\";\nfunction validRequestId(v) {\n if (typeof v !== \"string\") return false;\n const id = v.trim();\n if (id.length < 1 || id.length > 128) return false;\n if (!/^[A-Za-z0-9._~-]+$/.test(id)) return false;\n if (/^(npm_|gh[pousr]_|github_pat_|sk-|bearer)/i.test(id)) return false;\n return true;\n}\nfunction updateError(code) {\n return Object.assign(new Error(code), { code });\n}\nfunction validVersion(v) {\n return typeof v === \"string\" && /^\\d+\\.\\d+\\.\\d+$/.test(v);\n}\nconst RELEASE_CHANNELS = [\"stable\", \"prerelease\"];\nfunction validReleaseChannel(v) {\n return v === \"stable\" || v === \"prerelease\";\n}\nfunction validPrereleaseIds(ids) {\n if (typeof ids !== \"string\" || !ids) return false;\n const parts = ids.split(\".\");\n if (parts.length === 0) return false;\n for (const p of parts) {\n if (!p || !/^[0-9A-Za-z-]+$/.test(p)) return false;\n if (/^\\d+$/.test(p) && p.length > 1 && p.startsWith(\"0\")) return false;\n }\n return true;\n}\nfunction validReleaseVersion(v) {\n if (typeof v !== \"string\" || !v) return false;\n const dash = v.indexOf(\"-\");\n if (dash < 0) return validVersion(v);\n const core = v.slice(0, dash);\n const ids = v.slice(dash + 1);\n if (!validVersion(core) || !validPrereleaseIds(ids)) return false;\n if (ids.includes(\"+\")) return false;\n return true;\n}\nfunction isPrereleaseVersion(v) {\n return validReleaseVersion(v) && !validVersion(v);\n}\nfunction isVersionAllowedInChannel(version, channel = \"stable\") {\n if (channel === \"prerelease\") return validReleaseVersion(version);\n return validVersion(version);\n}\nfunction parseTriple(v) {\n const parts = String(v).split(\".\");\n if (parts.length > 3) return null;\n const nums = [];\n for (const p of parts) {\n if (!/^\\d+$/.test(p)) return null;\n const n = Number(p);\n if (!Number.isSafeInteger(n)) return null;\n nums.push(n);\n }\n while (nums.length < 3) nums.push(0);\n return [nums[0], nums[1], nums[2]];\n}\nfunction compareVersions(a, b) {\n const pa = parseTriple(a);\n const pb = parseTriple(b);\n if (!pa || !pb) throw updateError(\"invalid-release\");\n for (let i = 0; i < 3; i++) {\n if (pa[i] < pb[i]) return -1;\n if (pa[i] > pb[i]) return 1;\n }\n return 0;\n}\nfunction parseReleaseIds(ids) {\n return ids.split(\".\").map((p) => /^\\d+$/.test(p) ? Number(p) : p);\n}\nfunction compareReleaseVersions(a, b) {\n if (!validReleaseVersion(a) || !validReleaseVersion(b)) throw updateError(\"invalid-release\");\n const dashA = String(a).indexOf(\"-\");\n const dashB = String(b).indexOf(\"-\");\n const coreA = dashA < 0 ? String(a) : String(a).slice(0, dashA);\n const coreB = dashB < 0 ? String(b) : String(b).slice(0, dashB);\n const order = compareVersions(coreA, coreB);\n if (order !== 0) return order;\n const preA = dashA < 0 ? null : parseReleaseIds(String(a).slice(dashA + 1));\n const preB = dashB < 0 ? null : parseReleaseIds(String(b).slice(dashB + 1));\n if (preA === null && preB === null) return 0;\n if (preA === null) return 1;\n if (preB === null) return -1;\n const width = Math.max(preA.length, preB.length);\n for (let i = 0; i < width; i++) {\n const x = preA[i];\n const y = preB[i];\n if (x === void 0) return -1;\n if (y === void 0) return 1;\n if (typeof x === \"number\" && typeof y === \"number\") {\n if (x < y) return -1;\n if (x > y) return 1;\n continue;\n }\n if (typeof x === \"number\") return -1;\n if (typeof y === \"number\") return 1;\n if (x < y) return -1;\n if (x > y) return 1;\n }\n return 0;\n}\nfunction compareOne(node, op, target) {\n const order = compareTriple(node, target);\n if (op === \"=\" || op === \"\") return order === 0;\n if (op === \">\") return order === 1;\n if (op === \">=\") return order >= 0;\n if (op === \"<\") return order === -1;\n if (op === \"<=\") return order <= 0;\n return true;\n}\nfunction compareTriple(a, b) {\n for (let i = 0; i < 3; i++) {\n if (a[i] < b[i]) return -1;\n if (a[i] > b[i]) return 1;\n }\n return 0;\n}\nfunction upperBound(t, kind, kept) {\n if (kind === \"^\") {\n if (kept >= 1) return [t[0] + 1, 0, 0];\n return [0, t[1] + 1, 0];\n }\n if (kept >= 2) return [t[0], t[1] + 1, 0];\n return [t[0] + 1, 0, 0];\n}\nfunction satisfiesNodeRange(nodeVersion, range) {\n if (range === void 0 || range === null) return true;\n const text = String(range).trim();\n if (text === \"\" || text === \"*\") return true;\n const node = parseTriple(String(nodeVersion).replace(/^v/, \"\"));\n if (!node) return true;\n if (text.includes(\"||\")) return true;\n const parts = text.split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return true;\n for (const part of parts) {\n const m = part.match(/^(\\^|~|>=|<=|>|<|=)?(.+)$/);\n if (!m) return true;\n const op = m[1] || \"\";\n const target = parseTriple(m[2]);\n if (!target) return true;\n if (op === \"^\" || op === \"~\") {\n const kept = m[2].split(\".\").length;\n if (compareTriple(node, target) < 0) return false;\n if (compareTriple(node, upperBound(target, op, kept)) >= 0) return false;\n continue;\n }\n if (!compareOne(node, op, target)) return false;\n }\n return true;\n}\nfunction byteLength(text) {\n try {\n return new TextEncoder().encode(text).length;\n } catch {\n return text.length;\n }\n}\nfunction timeoutSignal(ms) {\n try {\n const ctor = globalThis.AbortSignal;\n if (ctor && typeof ctor.timeout === \"function\") return ctor.timeout(ms);\n } catch {\n }\n return void 0;\n}\nfunction httpStatusOf(response) {\n const raw = response?.status;\n return typeof raw === \"number\" && Number.isInteger(raw) && raw >= 100 && raw <= 599 ? raw : null;\n}\nfunction checkFailedWithStatus(response) {\n const err = updateError(\"check-failed\");\n const status = httpStatusOf(response);\n if (status !== null) err.httpStatus = status;\n return err;\n}\nasync function fetchNpmRelease(fetchImpl, timeoutMs = CHECK_TIMEOUT_MS, opts) {\n const targetName = opts?.targetPackageName ?? PACKAGE_NAME;\n const registry = opts?.registryUrl ?? NPM_REGISTRY;\n const channel = opts?.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n let response = null;\n try {\n response = await fetchImpl(`${registry}${encodeURIComponent(targetName)}/latest`, {\n headers: { accept: \"application/json\" },\n redirect: \"error\",\n signal: timeoutSignal(timeoutMs)\n });\n } catch {\n throw updateError(\"check-failed\");\n }\n try {\n if (!response.ok) throw checkFailedWithStatus(response);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const text = await response.text();\n if (byteLength(text) > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const value = JSON.parse(text);\n if (value.name !== targetName || !isVersionAllowedInChannel(value.version, channel)) throw updateError(\"invalid-release\");\n const version = value.version;\n const nodeRange = value.engines?.node;\n if (nodeRange !== void 0 && typeof nodeRange !== \"string\") throw updateError(\"invalid-release\");\n const tarballText = value.dist?.tarball;\n const integrity = value.dist?.integrity;\n let tarball;\n try {\n tarball = new URL(String(tarballText));\n } catch {\n throw updateError(\"invalid-release\");\n }\n const registryOrigin = new URL(registry).origin;\n const shapeOk = tarball.origin === registryOrigin && !tarball.username && !tarball.password && !tarball.search && !tarball.hash && tarball.pathname === `/${targetName}/-/${targetName}-${version}.tgz` && typeof integrity === \"string\" && new RegExp(INTEGRITY_PATTERN).test(integrity);\n if (!shapeOk) throw updateError(\"invalid-release\");\n return { version, nodeRange: typeof nodeRange === \"string\" ? nodeRange : \"*\", integrity, tarball: tarball.href };\n } catch (error) {\n if (error?.code === \"check-failed\") throw error;\n if (error?.code === \"invalid-release\") throw error;\n if (response !== null && response.ok) throw updateError(\"invalid-release\");\n throw checkFailedWithStatus(response);\n }\n}\nasync function fetchNpmVersionRelease(fetchImpl, timeoutMs = CHECK_TIMEOUT_MS, opts) {\n const targetName = opts?.targetPackageName ?? PACKAGE_NAME;\n const registry = opts?.registryUrl ?? NPM_REGISTRY;\n const channel = opts?.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n const version = opts?.version;\n if (typeof version !== \"string\" || !validReleaseVersion(version) || !isVersionAllowedInChannel(version, channel)) {\n throw updateError(\"invalid-release\");\n }\n let response = null;\n try {\n response = await fetchImpl(`${registry}${encodeURIComponent(targetName)}/${encodeURIComponent(version)}`, {\n headers: { accept: \"application/json\" },\n redirect: \"error\",\n signal: timeoutSignal(timeoutMs)\n });\n } catch {\n throw updateError(\"check-failed\");\n }\n try {\n if (!response.ok) throw checkFailedWithStatus(response);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const text = await response.text();\n if (byteLength(text) > MAX_METADATA_BYTES) throw updateError(\"invalid-release\");\n const value = JSON.parse(text);\n if (value.name !== targetName || value.version !== version || !isVersionAllowedInChannel(value.version, channel)) {\n throw updateError(\"invalid-release\");\n }\n const nodeRange = value.engines?.node;\n if (nodeRange !== void 0 && typeof nodeRange !== \"string\") throw updateError(\"invalid-release\");\n const tarballText = value.dist?.tarball;\n const integrity = value.dist?.integrity;\n let tarball;\n try {\n tarball = new URL(String(tarballText));\n } catch {\n throw updateError(\"invalid-release\");\n }\n const registryOrigin = new URL(registry).origin;\n const shapeOk = tarball.origin === registryOrigin && !tarball.username && !tarball.password && !tarball.search && !tarball.hash && tarball.pathname === `/${targetName}/-/${targetName}-${version}.tgz` && typeof integrity === \"string\" && new RegExp(INTEGRITY_PATTERN).test(integrity);\n if (!shapeOk) throw updateError(\"invalid-release\");\n return { version, nodeRange: typeof nodeRange === \"string\" ? nodeRange : \"*\", integrity, tarball: tarball.href };\n } catch (error) {\n if (error?.code === \"check-failed\") throw error;\n if (error?.code === \"invalid-release\") throw error;\n if (response !== null && response.ok) throw updateError(\"invalid-release\");\n throw checkFailedWithStatus(response);\n }\n}\nfunction createUpdateCore(ports) {\n const checkTimeoutMs = ports.checkTimeoutMs ?? CHECK_TIMEOUT_MS;\n const confirmationTtlMs = ports.confirmationTtlMs ?? CONFIRMATION_TTL_MS;\n const targetPackageName = ports.targetPackageName ?? PACKAGE_NAME;\n const registryUrl = ports.registryUrl ?? NPM_REGISTRY;\n const releaseChannel = ports.releaseChannel === \"prerelease\" ? \"prerelease\" : \"stable\";\n function fetchRelease() {\n return fetchNpmRelease(ports.fetchImpl, checkTimeoutMs, { targetPackageName, registryUrl, releaseChannel });\n }\n let checked = null;\n let checking = null;\n let lastCheckAt = -Infinity;\n let activeJobId = null;\n let memJob = null;\n let memLock = null;\n async function loadJob() {\n try {\n return ports.readJob ? await ports.readJob() ?? null : memJob;\n } catch {\n throw updateError(\"install-failed\");\n }\n }\n async function saveJob(job) {\n try {\n if (ports.writeJob) await ports.writeJob(job);\n else memJob = job;\n } catch {\n throw updateError(\"install-failed\");\n }\n }\n async function acquire(lockId) {\n try {\n if (ports.tryAcquireLock) return await ports.tryAcquireLock(lockId);\n } catch {\n return false;\n }\n if (memLock !== null) return false;\n memLock = lockId;\n return true;\n }\n async function release(lockId) {\n try {\n if (ports.releaseLock) await ports.releaseLock(lockId);\n else if (memLock === lockId) memLock = null;\n } catch {\n }\n }\n function healJob(job, env) {\n if (!job) return null;\n if (job.state === \"installing\" || job.state === \"verifying\") {\n return job.id === activeJobId ? job : { ...job, state: \"interrupted\", message: \"recovery-required\", requestId: job.requestId ?? null };\n }\n if (job.state === \"restart-required\" || job.state === \"completed\") {\n if (!job.targetVersion || env.installedVersion !== job.targetVersion) {\n return { ...job, state: \"interrupted\", message: \"installation-changed\", requestId: job.requestId ?? null };\n }\n const runningVersion = ports.readRunningVersion();\n return { ...job, state: runningVersion === job.targetVersion ? \"completed\" : \"restart-required\", requestId: job.requestId ?? null };\n }\n return job;\n }\n function buildSnapshot(env, job) {\n let blockedReason = env.blockedReason ?? checked?.blockedReason ?? null;\n if (job?.state === \"interrupted\" || job?.state === \"failed\") {\n if (env.installedVersion !== ports.readRunningVersion()) blockedReason = \"recovery-required\";\n } else if (job?.state === \"restart-required\" || env.installedVersion && env.installedVersion !== ports.readRunningVersion()) {\n blockedReason = \"pending-restart\";\n }\n const busy = job?.state === \"installing\" || job?.state === \"verifying\";\n const fresh = checked !== null && checked.checkId !== null && ports.now() < checked.expiresAt;\n const runningVersion = ports.readRunningVersion();\n let newer = false;\n try {\n newer = isVersionAllowedInChannel(runningVersion, releaseChannel) && !!checked?.release && isVersionAllowedInChannel(checked.release.version, releaseChannel) && compareReleaseVersions(checked.release.version, runningVersion) === 1;\n } catch {\n newer = false;\n }\n const canInstall = Boolean(\n env.eligible && !blockedReason && !busy && fresh && checked?.installationKey === env.installationKey && newer\n );\n return {\n runningVersion,\n installedVersion: env.installedVersion ?? null,\n latestVersion: checked?.release.version ?? null,\n canInstall,\n blockedReason,\n job\n };\n }\n async function status() {\n const env = await ports.readInstalled();\n const job = healJob(await loadJob(), env);\n return buildSnapshot(env, job);\n }\n async function check() {\n if (checking) return checking;\n if (checked?.checkId && ports.now() - lastCheckAt < RECHECK_WINDOW_MS) {\n const env = await ports.readInstalled();\n const snapshot = buildSnapshot(env, healJob(await loadJob(), env));\n return { snapshot, receipt: snapshot.canInstall ? toReceipt() : null };\n }\n lastCheckAt = ports.now();\n checking = (async () => {\n try {\n const env = await ports.readInstalled();\n const release2 = await fetchRelease();\n checked = {\n release: release2,\n checkId: ports.randomId(),\n checkedAt: ports.now(),\n expiresAt: ports.now() + confirmationTtlMs,\n installationKey: env.installationKey,\n blockedReason: satisfiesNodeRange(ports.nodeVersion, release2.nodeRange) ? null : \"incompatible-node\"\n };\n const snapshot = buildSnapshot(env, healJob(await loadJob(), env));\n return { snapshot, receipt: snapshot.canInstall ? toReceipt() : null };\n } catch (error) {\n if (checked) checked = { ...checked, checkId: null, expiresAt: 0 };\n throw error;\n } finally {\n checking = null;\n }\n })();\n return checking;\n }\n function toReceipt() {\n if (!checked?.checkId) return null;\n return { checkId: checked.checkId, checkedAt: checked.checkedAt, expiresAt: checked.expiresAt };\n }\n async function runBackground(job, envAtStart) {\n try {\n if (ports.runInstall) await ports.runInstall({ version: job.targetVersion, profileName: envAtStart.profileName, environmentKind: envAtStart.environmentKind });\n else throw updateError(\"unsupported\");\n const doing = { ...job, state: \"verifying\", message: null };\n activeJobId = doing.id;\n await saveJob(doing);\n const env = await ports.readInstalled();\n if (env.installedVersion !== job.targetVersion) {\n throw Object.assign(updateError(\"install-failed\"), {\n detail: `\\u88C5\\u5B8C\\u6821\\u9A8C\\u6CA1\\u8FC7\\uFF1A\\u78C1\\u76D8\\u4E0A\\u662F ${env.installedVersion ?? \"\\u672A\\u77E5\"}\\uFF0C\\u76EE\\u6807\\u662F ${job.targetVersion ?? \"\\u672A\\u77E5\"}`\n });\n }\n if (env.blockedReason && env.blockedReason !== \"pending-restart\") {\n throw Object.assign(updateError(\"install-failed\"), { detail: `\\u88C5\\u5B8C\\u6821\\u9A8C\\u6CA1\\u8FC7\\uFF1A\\u73AF\\u5883\\u62A5\\u544A ${env.blockedReason}` });\n }\n const done = { ...doing, state: \"restart-required\", message: null };\n await saveJob(done);\n } catch (error) {\n const code = error?.code;\n const base = code === \"installation-changed\" || code === \"registry-conflict\" || code === \"install-failed\" ? String(code) : \"install-failed\";\n const detail = error?.detail;\n const message = typeof detail === \"string\" && detail ? `${base}: ${detail}` : base;\n const failedJob = { ...job, state: \"failed\", message, requestId: job.requestId ?? null };\n try {\n await saveJob(failedJob);\n } catch {\n memJob = failedJob;\n }\n } finally {\n await release(job.id);\n if (activeJobId === job.id) activeJobId = null;\n }\n }\n async function install(args) {\n const checkId = typeof args?.checkId === \"string\" ? args.checkId : \"\";\n const requestId = typeof args?.requestId === \"string\" ? args.requestId : \"\";\n if (!checkId || !validRequestId(requestId)) throw updateError(\"check-expired\");\n let env = await ports.readInstalled();\n let previous = healJob(await loadJob(), env);\n if (previous?.requestId === requestId) return buildSnapshot(env, previous);\n if (previous?.state === \"installing\" || previous?.state === \"verifying\" || previous?.state === \"restart-required\") {\n throw updateError(\"update-busy\");\n }\n if (!checked || !checked.checkId || checked.checkId !== checkId || ports.now() >= checked.expiresAt) {\n throw updateError(\"check-expired\");\n }\n if (env.installationKey !== checked.installationKey) throw updateError(\"installation-changed\");\n if (!buildSnapshot(env, previous).canInstall) {\n throw updateError(env.blockedReason ?? checked.blockedReason ?? \"update-busy\");\n }\n const job = {\n id: ports.randomId(),\n state: \"installing\",\n targetVersion: checked.release.version,\n message: null,\n requestId\n };\n if (!await acquire(job.id)) throw updateError(\"update-busy\");\n try {\n const current = await fetchRelease();\n if (JSON.stringify(current) !== JSON.stringify(checked.release)) throw updateError(\"check-expired\");\n env = await ports.readInstalled();\n if (env.installationKey !== checked.installationKey) throw updateError(\"installation-changed\");\n if (!env.eligible || env.blockedReason) throw updateError(env.blockedReason ?? \"update-busy\");\n if (ports.backupJob) await ports.backupJob(job);\n await saveJob(job);\n activeJobId = job.id;\n } catch (error) {\n await release(job.id);\n throw error;\n }\n void runBackground(job, env).catch(() => {\n });\n return buildSnapshot(env, job);\n }\n return { status, check, install };\n}\nexport {\n CHECK_TIMEOUT_MS,\n CONFIRMATION_TTL_MS,\n INTEGRITY_PATTERN,\n MAX_METADATA_BYTES,\n NPM_REGISTRY,\n PACKAGE_NAME,\n RECHECK_WINDOW_MS,\n RELEASE_CHANNELS,\n compareReleaseVersions,\n compareVersions,\n createUpdateCore,\n fetchNpmRelease,\n fetchNpmVersionRelease,\n isPrereleaseVersion,\n isVersionAllowedInChannel,\n satisfiesNodeRange,\n updateError,\n validReleaseChannel,\n validReleaseVersion,\n validRequestId,\n validVersion\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/changelog.ts\nimport { compareReleaseVersions, validReleaseVersion } from \"./service.js\";\nconst CHANGELOG_FILENAME = \"CHANGELOG.md\";\nconst CHANGELOG_NEUTRAL_HINT = \"\\u4F5C\\u8005\\u672A\\u63D0\\u4F9B\\u66F4\\u65B0\\u8BF4\\u660E\";\nconst CHANGELOG_NEUTRAL_LINE = \"\\u4F5C\\u8005\\u672A\\u63D0\\u4F9B\\u66F4\\u65B0\\u8BF4\\u660E\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\u3002\";\nconst CHANGELOG_MAX_CHARS = 64 * 1024;\nconst CHANGELOG_MAX_ENTRIES = 100;\nconst CHANGELOG_MAX_BULLETS_PER_SECTION = 200;\nconst CHANGELOG_MAX_BULLET_CHARS = 500;\nconst CHANGELOG_ALL_CATEGORIES = [\n \"Added\",\n \"Fixed\",\n \"Changed\",\n \"Deprecated\",\n \"Removed\",\n \"Security\"\n];\nconst CHANGELOG_MUST_SHOW = [\"Added\", \"Fixed\", \"Changed\", \"Security\"];\nconst CHANGELOG_FOLDED = [\"Deprecated\", \"Removed\"];\nconst CHANGELOG_CATEGORY_ZH = {\n Added: \"\\u65B0\\u589E\",\n Fixed: \"\\u4FEE\\u590D\",\n Changed: \"\\u53D8\\u66F4\",\n Deprecated: \"\\u5F03\\u7528\\u9884\\u544A\",\n Removed: \"\\u79FB\\u9664\",\n Security: \"\\u5B89\\u5168\"\n};\nfunction emptySections() {\n return { Added: [], Fixed: [], Changed: [], Deprecated: [], Removed: [], Security: [] };\n}\nfunction emptyCounts() {\n return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n}\nfunction isCategoryName(v) {\n return CHANGELOG_ALL_CATEGORIES.includes(v);\n}\nfunction normalizeCategory(raw) {\n const t = String(raw || \"\").trim().toLowerCase();\n if (t === \"added\") return \"Added\";\n if (t === \"fixed\") return \"Fixed\";\n if (t === \"changed\") return \"Changed\";\n if (t === \"deprecated\") return \"Deprecated\";\n if (t === \"removed\") return \"Removed\";\n if (t === \"security\") return \"Security\";\n return null;\n}\nfunction isUnreleasedVersion(v) {\n return typeof v === \"string\" && v.trim().toLowerCase() === \"unreleased\";\n}\nfunction extractVersionDateAndYanked(title) {\n const t = String(title || \"\").trim();\n if (!t) return { version: null, date: null, yanked: false };\n const yanked = /\\[YANKED\\]/i.test(t);\n if (/unreleased/i.test(t)) return { version: \"Unreleased\", date: null, yanked };\n const vm = t.match(/(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)/);\n if (!vm) return { version: null, date: null, yanked: false };\n const dm = t.match(/(\\d{4}-\\d{2}-\\d{2})/);\n return { version: vm[1], date: dm ? dm[1] : null, yanked };\n}\nfunction truncateBullet(text) {\n const t = String(text || \"\").replace(/\\s+/g, \" \").trim();\n if (!t) return \"\";\n if (t.length <= CHANGELOG_MAX_BULLET_CHARS) return t;\n return `${t.slice(0, CHANGELOG_MAX_BULLET_CHARS - 1)}\\u2026`;\n}\nconst RE_VERSION_HEADING = /^##(?!#)\\s*(.+?)\\s*$/;\nconst RE_CATEGORY_HEADING = /^###\\s*(.+?)\\s*$/;\nconst RE_BULLET = /^\\s*[-*+]\\s+(.+?)\\s*$/;\nconst RE_FENCE_TOGGLE = new RegExp(\"^\\\\s*(\" + String.fromCharCode(96).repeat(3) + \"|~~~)\");\nconst RE_VERSION_NUM = /(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)/;\nconst RE_VERSION_DATE = /(\\d{4}-\\d{2}-\\d{2})/;\nconst RE_YANKED = /\\[YANKED\\]/i;\nconst RE_UNRELEASED = /unreleased/i;\nfunction matchVersionHeading(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_VERSION_HEADING);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\").trim();\n } catch {\n return null;\n }\n}\nfunction matchCategoryHeading(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_CATEGORY_HEADING);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\").trim();\n } catch {\n return null;\n }\n}\nfunction matchBulletBody(line) {\n try {\n const s = String(line !== null && line !== void 0 ? line : \"\");\n const m = s.match(RE_BULLET);\n if (!m) return null;\n return String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\");\n } catch {\n return null;\n }\n}\nfunction isFenceToggle(line) {\n try {\n return RE_FENCE_TOGGLE.test(String(line !== null && line !== void 0 ? line : \"\"));\n } catch {\n return false;\n }\n}\nconst BREAKING_BADGE_TEXT = \"\\u4E0D\\u517C\\u5BB9\";\nfunction stripBreakingDecorations(s) {\n try {\n let t = String(s !== null && s !== void 0 ? s : \"\").trim();\n let changed = true;\n while (changed) {\n changed = false;\n if (t.charAt(0) === \">\") {\n t = t.slice(1).trim();\n changed = true;\n continue;\n }\n if (t.slice(0, 2) === \"**\" || t.slice(0, 2) === \"__\") {\n t = t.slice(2).trim();\n changed = true;\n continue;\n }\n if (t.charAt(0) === \"*\" || t.charAt(0) === \"_\") {\n t = t.slice(1).trim();\n changed = true;\n continue;\n }\n }\n return t;\n } catch {\n return \"\";\n }\n}\nfunction isBreakingChangelogItem(item) {\n try {\n if (typeof item !== \"string\") return false;\n const t = stripBreakingDecorations(item);\n if (!t) return false;\n return /^(breaking|不兼容)\\s*[::]/i.test(t);\n } catch {\n return false;\n }\n}\nfunction splitBreakingPrefix(item) {\n try {\n const s = String(item !== null && item !== void 0 ? item : \"\");\n const m = s.match(/^(\\s*(?:>\\s*|\\*\\*\\s*|__\\s*|\\*\\s*|_\\s*)*)(breaking|不兼容)(\\s*[::])/i);\n if (!m) return null;\n const head = String(m[1] !== void 0 && m[1] !== null ? m[1] : \"\");\n const core = String(m[2] !== void 0 && m[2] !== null ? m[2] : \"\");\n const colon = String(m[3] !== void 0 && m[3] !== null ? m[3] : \"\");\n const prefix = core + colon;\n const rest = s.slice(m[0].length);\n return { head, prefix, rest };\n } catch {\n return null;\n }\n}\nconst CHANGELOG_POLL_BACKOFF_MS = 30 * 1e3;\nfunction shouldFetchChangelog(opts) {\n try {\n const o = opts;\n if (o.hasCache === true) return false;\n if (o.isManual === true) return true;\n const now = typeof o.now === \"number\" && Number.isFinite(o.now) ? o.now : Date.now();\n const failedAt = typeof o.failedAt === \"number\" && Number.isFinite(o.failedAt) ? o.failedAt : null;\n if (failedAt === null) return true;\n const backoff = typeof o.backoffMs === \"number\" && Number.isFinite(o.backoffMs) && o.backoffMs > 0 ? Math.floor(o.backoffMs) : CHANGELOG_POLL_BACKOFF_MS;\n return now - failedAt >= backoff;\n } catch {\n return true;\n }\n}\nfunction parseChangelog(markdown) {\n try {\n if (typeof markdown !== \"string\" || !markdown.trim()) return [];\n let text = markdown.replace(/\\r\\n/g, \"\\n\");\n if (text.length > CHANGELOG_MAX_CHARS) text = text.slice(0, CHANGELOG_MAX_CHARS);\n const lines = text.split(\"\\n\");\n const entries = [];\n let cur = null;\n let curCat = null;\n let inFence = false;\n const pushCurrent = () => {\n if (!cur) return;\n const hasAny = CHANGELOG_ALL_CATEGORIES.some(\n (c) => cur.sections[c].length > 0\n );\n if (!hasAny) return;\n for (const c of CHANGELOG_ALL_CATEGORIES) {\n const list = cur.sections[c];\n if (list.length > CHANGELOG_MAX_BULLETS_PER_SECTION) {\n cur.sections[c] = list.slice(0, CHANGELOG_MAX_BULLETS_PER_SECTION);\n }\n }\n entries.push(cur);\n };\n for (const rawLine of lines) {\n const line = String(rawLine ?? \"\");\n if (isFenceToggle(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const versionTitle = matchVersionHeading(line);\n if (versionTitle !== null) {\n pushCurrent();\n cur = null;\n curCat = null;\n const title = versionTitle;\n const { version, date, yanked } = extractVersionDateAndYanked(title);\n if (!version) continue;\n cur = { version, date, yanked, sections: emptySections(), counts: emptyCounts() };\n continue;\n }\n const catTitle = matchCategoryHeading(line);\n if (catTitle !== null) {\n const cat = normalizeCategory(catTitle);\n curCat = cur && cat && isCategoryName(cat) ? cat : null;\n continue;\n }\n const bulletBody = matchBulletBody(line);\n if (bulletBody !== null && cur && curCat) {\n const item = truncateBullet(bulletBody);\n if (item) {\n cur.sections[curCat].push(item);\n try {\n if (cur.counts) cur.counts[curCat] += 1;\n } catch {\n }\n }\n continue;\n }\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"#\") && /^\\s/.test(line) && cur && curCat) {\n const list = cur.sections[curCat];\n if (list.length > 0) {\n const merged = truncateBullet(`${list[list.length - 1]} ${trimmed}`);\n if (merged) list[list.length - 1] = merged;\n }\n continue;\n }\n }\n pushCurrent();\n return entries.slice(0, CHANGELOG_MAX_ENTRIES);\n } catch {\n return [];\n }\n}\nfunction hasVisibleSections(entry) {\n if (!entry || typeof entry !== \"object\") return false;\n try {\n return CHANGELOG_ALL_CATEGORIES.some((c) => Array.isArray(entry.sections?.[c]) && entry.sections[c].length > 0);\n } catch {\n return false;\n }\n}\nfunction compareReleaseSafe(a, b) {\n try {\n return compareReleaseVersions(a, b);\n } catch {\n return null;\n }\n}\nfunction selectChangelogEntries(entries, fromExclusive, toInclusive) {\n try {\n if (!Array.isArray(entries) || entries.length === 0) return [];\n const to = typeof toInclusive === \"string\" ? toInclusive.trim() : \"\";\n if (!to || !validReleaseVersion(to)) return [];\n const from = typeof fromExclusive === \"string\" ? fromExclusive.trim() : \"\";\n const fromValid = from && validReleaseVersion(from) ? from : null;\n const out = [];\n for (const e of entries) {\n if (!e || typeof e !== \"object\") continue;\n const v = typeof e.version === \"string\" ? String(e.version) : \"\";\n if (!v || isUnreleasedVersion(v) || !validReleaseVersion(v)) continue;\n if (!hasVisibleSections(e)) continue;\n const leTo = compareReleaseSafe(v, to);\n if (leTo === null || leTo > 0) continue;\n if (fromValid) {\n const gtFrom = compareReleaseSafe(v, fromValid);\n if (gtFrom === null || gtFrom <= 0) continue;\n }\n out.push(e);\n }\n return out;\n } catch {\n return [];\n }\n}\nfunction changelogForUpdate(entries, runningVersion, latestVersion, installedVersion) {\n try {\n const to = typeof latestVersion === \"string\" ? latestVersion.trim() : \"\";\n if (!to || !validReleaseVersion(to)) return [];\n const run = typeof runningVersion === \"string\" ? runningVersion.trim() : \"\";\n const inst = typeof installedVersion === \"string\" ? installedVersion.trim() : \"\";\n const from = run && validReleaseVersion(run) ? run : inst && validReleaseVersion(inst) ? inst : \"\";\n if (!from) return [];\n const order = compareReleaseSafe(to, from);\n if (order === null || order <= 0) return [];\n return selectChangelogEntries(entries, from, to);\n } catch {\n return [];\n }\n}\nfunction escapeChangelogHtml(text) {\n return String(text ?? \"\").replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction countsOf(entry) {\n try {\n const out = { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n if (!entry || typeof entry !== \"object\") return out;\n for (const c of CHANGELOG_ALL_CATEGORIES) {\n const n = entry.counts;\n const v = n && typeof n[c] === \"number\" && Number.isFinite(n[c]) ? Math.floor(n[c]) : null;\n if (v !== null && v >= 0) {\n out[c] = v;\n continue;\n }\n const list = entry.sections;\n const arr = list ? list[c] : null;\n out[c] = Array.isArray(arr) ? arr.length : 0;\n }\n return out;\n } catch {\n return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };\n }\n}\nfunction truncatedNoteHTML(total, shown) {\n try {\n const m = Math.floor(total);\n const n = Math.floor(shown);\n return '<div class=\"dsh-upd-changelog-count\">\\u5171 ' + String(m) + \" \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D \" + String(n) + \" \\u6761</div>\";\n } catch {\n return \"\";\n }\n}\nfunction yankedBannerHTML(version) {\n try {\n const v = typeof version === \"string\" ? version.trim() : \"\";\n if (!v) return \"\";\n return '<div class=\"dsh-upd-changelog-yanked\" role=\"alert\">\\u76EE\\u6807\\u7248\\u672C ' + escapeChangelogHtml(v) + \" \\u5DF2\\u88AB\\u4F5C\\u8005\\u64A4\\u56DE\\uFF08yanked\\uFF09\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\uFF0C\\u7EE7\\u7EED\\u524D\\u8BF7\\u786E\\u8BA4\\u3002</div>\";\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogItem(item) {\n try {\n const s = String(item !== null && item !== void 0 ? item : \"\");\n if (!s) return \"\";\n const split = splitBreakingPrefix(s);\n if (!split) return \"<li>\" + escapeChangelogHtml(s) + \"</li>\";\n const head = escapeChangelogHtml(split.head);\n const prefix = escapeChangelogHtml(split.prefix);\n const rest = escapeChangelogHtml(split.rest);\n const badge = '<span class=\"dsh-upd-breaking-badge\" role=\"img\" aria-label=\"\\u7834\\u574F\\u6027\\u53D8\\u66F4\">' + BREAKING_BADGE_TEXT + \"</span> \";\n return \"<li>\" + head + badge + \"<strong>\" + prefix + \"</strong>\" + rest + \"</li>\";\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogSection(entry) {\n try {\n if (!entry || typeof entry !== \"object\") return \"\";\n const version = String(entry.version !== void 0 && entry.version !== null ? String(entry.version) : \"\").trim();\n if (!version || isUnreleasedVersion(version)) return \"\";\n if (!hasVisibleSections(entry)) return \"\";\n const dateRaw = entry.date;\n const date = typeof dateRaw === \"string\" ? dateRaw : \"\";\n const yankedFlag = entry.yanked === true;\n const yankedSuffix = yankedFlag ? \" \\xB7 \\u5DF2\\u64A4\\u56DE\" : \"\";\n const title = (date ? version + \" \\xB7 \" + date : version) + yankedSuffix;\n const counts = countsOf(entry);\n const parts = [];\n parts.push('<div class=\"dsh-upd-changelog-version\" data-version=\"' + escapeChangelogHtml(version) + '\">');\n parts.push('<div class=\"dsh-upd-changelog-title\">' + escapeChangelogHtml(title) + \"</div>\");\n for (const cat of CHANGELOG_MUST_SHOW) {\n const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];\n if (!items || items.length === 0) continue;\n const label = cat + \" \\xB7 \" + CHANGELOG_CATEGORY_ZH[cat];\n parts.push('<div class=\"dsh-upd-changelog-cat\" data-cat=\"' + cat + '\">');\n parts.push('<div class=\"dsh-upd-changelog-catname\">' + escapeChangelogHtml(label) + \"</div>\");\n const total = counts[cat];\n const shown = items.length;\n if (total > shown) {\n parts.push(truncatedNoteHTML(total, shown));\n }\n parts.push(\"<ul>\");\n for (const item of items) {\n if (!item) continue;\n const li = renderChangelogItem(item);\n if (li) parts.push(li);\n }\n parts.push(\"</ul>\");\n if (cat === \"Security\" && total > shown) {\n const restCount = total - shown;\n parts.push('<details class=\"dsh-upd-changelog-security-more\"><summary>\\u5176\\u4F59 ' + String(restCount) + ' \\u6761</summary><div class=\"dsh-upd-changelog-more-note\">\\u4E3A\\u4FDD\\u6301\\u9762\\u677F\\u6027\\u80FD\\uFF0C\\u5176\\u4F59\\u6761\\u76EE\\u5DF2\\u6298\\u53E0\\uFF0C\\u53EF\\u67E5\\u770B\\u539F\\u6587\\u3002</div></details>');\n }\n parts.push(\"</div>\");\n }\n for (const cat of CHANGELOG_FOLDED) {\n const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];\n if (!items || items.length === 0) continue;\n const label = cat + \" \\xB7 \" + CHANGELOG_CATEGORY_ZH[cat] + \"\\uFF08\" + String(items.length) + \"\\uFF09\";\n parts.push('<details class=\"dsh-upd-changelog-fold\" data-cat=\"' + cat + '\">');\n parts.push(\"<summary>\" + escapeChangelogHtml(label) + \"</summary>\");\n const total = counts[cat];\n const shown = items.length;\n if (total > shown) {\n parts.push(truncatedNoteHTML(total, shown));\n }\n parts.push(\"<ul>\");\n for (const item of items) {\n if (!item) continue;\n const li = renderChangelogItem(item);\n if (li) parts.push(li);\n }\n parts.push(\"</ul></details>\");\n }\n parts.push(\"</div>\");\n return parts.join(String.fromCharCode(10));\n } catch {\n return \"\";\n }\n}\nfunction renderChangelogNeutral() {\n return `<div class=\"dsh-upd-changelog-neutral\">${escapeChangelogHtml(CHANGELOG_NEUTRAL_LINE)}</div>`;\n}\nfunction renderChangelogHTML(entries, opts) {\n try {\n if (!Array.isArray(entries) || entries.length === 0) return renderChangelogNeutral();\n const from = opts && typeof opts.from === \"string\" ? opts.from : null;\n const to = opts && typeof opts.to === \"string\" ? opts.to : null;\n let ranged;\n if (from !== null || to !== null) {\n ranged = selectChangelogEntries(entries, from, to);\n } else {\n ranged = entries.filter((e) => {\n try {\n const v = String(e.version ?? \"\");\n return !!v && !isUnreleasedVersion(v) && hasVisibleSections(e);\n } catch {\n return false;\n }\n });\n }\n if (ranged.length === 0) return renderChangelogNeutral();\n const blocks = ranged.map((e) => renderChangelogSection(e)).filter((s) => !!s);\n if (blocks.length === 0) return renderChangelogNeutral();\n return `<div class=\"dsh-upd-changelog\">\n${blocks.join(\"\\n\")}\n</div>`;\n } catch {\n return renderChangelogNeutral();\n }\n}\nconst CHANGELOG_VALIDATE_HINTS = {\n E_VERSION_TITLE: \"\\u8BE5\\u6807\\u9898\\u4E0D\\u662F\\u5408\\u6CD5\\u7248\\u672C\\u53F7\\uFF0C\\u5176\\u4E0B\\u6761\\u76EE\\u5DF2\\u88AB\\u5FFD\\u7565\\uFF0C\\u8BF7\\u6539\\u6210 ## [x.y.z] \\u5F62\\u6001\",\n E_CATEGORY: \"\\u672A\\u77E5\\u5206\\u7C7B\\uFF0C\\u5176\\u4E0B\\u6761\\u76EE\\u5DF2\\u88AB\\u5FFD\\u7565\\uFF0C\\u4EC5\\u6536 Added/Fixed/Changed/Deprecated/Removed/Security\",\n E_BULLET_ORPHAN: \"\\u8BE5\\u6761\\u76EE\\u4E0D\\u5728\\u7248\\u672C\\u8282\\u4E0E\\u5206\\u7C7B\\u4E0B\\uFF0C\\u5DF2\\u88AB\\u5FFD\\u7565\",\n W_BREAKING_MAYBE: \"\\u7591\\u4F3C\\u7834\\u574F\\u6807\\u8BB0\\u8BEF\\u5199\\uFF08\\u7F3A\\u5192\\u53F7/\\u62FC\\u5199\\u63A5\\u8FD1/\\u4F4D\\u7F6E\\u7591\\u4F3C\\u653E\\u9519\\uFF09\\uFF0C\\u8BF7\\u68C0\\u67E5\\u662F\\u5426\\u60F3\\u5199 BREAKING:/\\u4E0D\\u517C\\u5BB9:\",\n W_YANKED: \"\\u8BE5\\u7248\\u672C\\u5DF2\\u88AB\\u6807\\u8BB0\\u64A4\\u56DE\\uFF08yanked\\uFF09\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\uFF0C\\u7EE7\\u7EED\\u524D\\u8BF7\\u786E\\u8BA4\",\n W_TRUNCATED: \"\\u8BE5\\u7C7B\\u5171 M \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D N \\u6761\",\n W_TRUNCATED_FILE: \"\\u6587\\u4EF6\\u8D85 64K\\uFF0C\\u4EC5\\u6821\\u9A8C\\u524D 64K\",\n W_EMPTY: \"\\u672A\\u89E3\\u6790\\u5230\\u4EFB\\u4F55\\u7248\\u672C\\u8282\\uFF0C\\u9762\\u677F\\u5C06\\u663E\\u793A\\u4E2D\\u6027\\u63D0\\u793A\"\n};\nfunction isMaybeBreakingMisuse(bulletBody) {\n try {\n if (typeof bulletBody !== \"string\") return false;\n const t = stripBreakingDecorations(bulletBody);\n if (!t) return false;\n if (isBreakingChangelogItem(bulletBody)) return false;\n if (/^breaking\\s*$/i.test(t)) return true;\n if (/^breaking\\s+[^::]/i.test(t)) return true;\n if (/^breaking[^::\\s\\w]/i.test(t)) return true;\n if (/^不兼容\\s*$/i.test(t)) return true;\n if (/^不兼容\\s*[^::]/i.test(t)) return true;\n if (/^(braking|breking|breakign|brekaing|braeking|breakingg)\\b/i.test(t)) return true;\n if (/^break[^i::\\s]/i.test(t)) return true;\n return false;\n } catch {\n return false;\n }\n}\nfunction validateChangelog(markdown) {\n try {\n if (typeof markdown !== \"string\" || !markdown.trim()) {\n return { ok: true, diagnostics: [{ line: 1, code: \"W_EMPTY\", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY }] };\n }\n let raw = String(markdown).replace(/\\r\\n/g, String.fromCharCode(10));\n let fileTruncated = false;\n if (raw.length > CHANGELOG_MAX_CHARS) {\n fileTruncated = true;\n raw = raw.slice(0, CHANGELOG_MAX_CHARS);\n }\n const lines = raw.split(String.fromCharCode(10));\n const diags = [];\n let inFence = false;\n let curValid = false;\n let curCat = null;\n let catLineByCat = {};\n let countByCat = {};\n let hasVisibleVersion = false;\n const flushCounts = function() {\n try {\n for (const k of Object.keys(countByCat)) {\n const n = countByCat[k];\n if (n > CHANGELOG_MAX_BULLETS_PER_SECTION) {\n const lineNo = catLineByCat[k] ? catLineByCat[k] : lines.length;\n const hint = \"\\u5206\\u7C7B \" + k + \" \\u5171 \" + String(n) + \" \\u6761\\uFF0C\\u4EC5\\u663E\\u793A\\u524D \" + String(CHANGELOG_MAX_BULLETS_PER_SECTION) + \" \\u6761\";\n diags.push({ line: lineNo, code: \"W_TRUNCATED\", hint });\n }\n }\n } catch {\n }\n };\n for (let i = 0; i < lines.length; i++) {\n const lineNo = i + 1;\n const line = String(lines[i] !== void 0 && lines[i] !== null ? lines[i] : \"\");\n if (isFenceToggle(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const vTitle = matchVersionHeading(line);\n if (vTitle !== null) {\n flushCounts();\n catLineByCat = {};\n countByCat = {};\n curValid = false;\n curCat = null;\n const info = extractVersionDateAndYanked(vTitle);\n if (!info.version) {\n diags.push({ line: lineNo, code: \"E_VERSION_TITLE\", hint: CHANGELOG_VALIDATE_HINTS.E_VERSION_TITLE });\n continue;\n }\n if (isUnreleasedVersion(info.version)) {\n curValid = false;\n continue;\n }\n curValid = true;\n hasVisibleVersion = true;\n if (info.yanked) {\n diags.push({ line: lineNo, code: \"W_YANKED\", hint: CHANGELOG_VALIDATE_HINTS.W_YANKED });\n }\n continue;\n }\n const cTitle = matchCategoryHeading(line);\n if (cTitle !== null) {\n const cat = normalizeCategory(cTitle);\n if (!cat) {\n curCat = null;\n diags.push({ line: lineNo, code: \"E_CATEGORY\", hint: CHANGELOG_VALIDATE_HINTS.E_CATEGORY });\n continue;\n }\n if (!curValid) {\n curCat = null;\n continue;\n }\n curCat = cat;\n catLineByCat[cat] = lineNo;\n if (countByCat[cat] === void 0) countByCat[cat] = 0;\n continue;\n }\n const body = matchBulletBody(line);\n if (body !== null) {\n if (!curValid || !curCat) {\n diags.push({ line: lineNo, code: \"E_BULLET_ORPHAN\", hint: CHANGELOG_VALIDATE_HINTS.E_BULLET_ORPHAN });\n continue;\n }\n countByCat[curCat] = (countByCat[curCat] ? countByCat[curCat] : 0) + 1;\n if (isMaybeBreakingMisuse(body)) {\n diags.push({ line: lineNo, code: \"W_BREAKING_MAYBE\", hint: CHANGELOG_VALIDATE_HINTS.W_BREAKING_MAYBE });\n }\n continue;\n }\n }\n flushCounts();\n if (fileTruncated) {\n diags.push({ line: lines.length, code: \"W_TRUNCATED_FILE\", hint: CHANGELOG_VALIDATE_HINTS.W_TRUNCATED_FILE });\n }\n if (!hasVisibleVersion) {\n let anyVisible = false;\n try {\n const parsed = parseChangelog(markdown);\n for (const e of parsed) {\n if (e && !isUnreleasedVersion(e.version) && hasVisibleSections(e)) {\n anyVisible = true;\n break;\n }\n }\n } catch {\n anyVisible = false;\n }\n if (!anyVisible) {\n diags.push({ line: lines.length, code: \"W_EMPTY\", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY });\n }\n }\n diags.sort(function(a, b) {\n return a.line - b.line;\n });\n let ok = true;\n for (const d of diags) {\n if (d.code.charAt(0) === \"E\") {\n ok = false;\n break;\n }\n }\n return { ok, diagnostics: diags };\n } catch {\n return { ok: false, diagnostics: [] };\n }\n}\nexport {\n BREAKING_BADGE_TEXT,\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_CATEGORY_ZH,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n CHANGELOG_POLL_BACKOFF_MS,\n CHANGELOG_VALIDATE_HINTS,\n RE_BULLET,\n RE_CATEGORY_HEADING,\n RE_FENCE_TOGGLE,\n RE_UNRELEASED,\n RE_VERSION_DATE,\n RE_VERSION_HEADING,\n RE_VERSION_NUM,\n RE_YANKED,\n changelogForUpdate,\n countsOf,\n hasVisibleSections,\n isBreakingChangelogItem,\n isFenceToggle,\n isMaybeBreakingMisuse,\n isUnreleasedVersion,\n matchBulletBody,\n matchCategoryHeading,\n matchVersionHeading,\n parseChangelog,\n renderChangelogHTML,\n renderChangelogItem,\n renderChangelogNeutral,\n renderChangelogSection,\n selectChangelogEntries,\n shouldFetchChangelog,\n splitBreakingPrefix,\n stripBreakingDecorations,\n truncatedNoteHTML,\n validateChangelog,\n yankedBannerHTML\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/log-events.ts\nconst LOG_EVENT_CALL = \"host.call\";\nconst LOG_EVENT_CALL_FAIL = \"host.call.fail\";\nconst LOG_EVENT_INSTALL_EXEC = \"update.install.exec\";\nexport {\n LOG_EVENT_CALL,\n LOG_EVENT_CALL_FAIL,\n LOG_EVENT_INSTALL_EXEC\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel.ts\nimport { buildChangelogPhoneName, buildPhoneNames, DEFAULT_PANEL_POLL_MS, MIN_PANEL_POLL_MS } from \"./config.js\";\nimport { COPY_BUDGET_CHARS, sanitizeForCopy } from \"./redaction.js\";\nimport { validReleaseVersion } from \"./service.js\";\nimport {\n changelogForUpdate,\n parseChangelog,\n renderChangelogHTML,\n shouldFetchChangelog,\n yankedBannerHTML\n} from \"./changelog.js\";\nimport { LOG_EVENT_CALL, LOG_EVENT_CALL_FAIL, LOG_EVENT_INSTALL_EXEC } from \"./log-events.js\";\nfunction normalizePanelTheme(value) {\n return value === \"archive\" || value === \"d5-paper\" ? \"archive\" : \"default\";\n}\nconst BLOCKED_COPY = {\n \"unknown-profile\": {\n title: \"\\u4F7F\\u7528\\u8303\\u56F4\\u6216\\u63D2\\u4EF6\\u4F4D\\u7F6E\\u8BA4\\u4E0D\\u51FA\",\n action: \"\\u91CD\\u5F00\\u5BBF\\u4E3B\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u7248\\u672C\\u53F7\\u4E0E\\u65E5\\u5FD7\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u8FD9\\u79CD\\u60C5\\u5F62\\u4E0D\\u7ED9\\u624B\\u5DE5\\u547D\\u4EE4\"\n },\n \"source-install\": {\n title: \"\\u5F53\\u524D\\u662F\\u4ECE\\u6E90\\u7801\\u88C5\\u7684\\uFF0C\\u4E0D\\u662F\\u6309\\u7248\\u672C\\u53F7\\u88C5\\u7684\",\n action: \"\\u8FD9\\u79CD\\u60C5\\u5F62\\u4E0D\\u7ED9\\u624B\\u5DE5\\u547D\\u4EE4\\uFF1B\\u60F3\\u8D70\\u66F4\\u65B0\\u5148\\u6309\\u7248\\u672C\\u53F7\\u91CD\\u88C5\\u4E00\\u6B21\"\n },\n \"invalid-installation\": {\n title: \"\\u5DF2\\u88C5\\u7684\\u5305\\u4E0D\\u5B8C\\u6574\\uFF08\\u540D\\u5B57\\u5BF9\\u4E0D\\u4E0A\\u3001\\u7248\\u672C\\u975E\\u6CD5\\u3001\\u5165\\u53E3\\u6587\\u4EF6\\u7F3A\\u5931\\uFF09\",\n action: \"\\u91CD\\u88C5\\u5F53\\u524D\\u7248\\u672C\\uFF0C\\u4FEE\\u597D\\u5DF2\\u88C5\\u76EE\\u5F55\\u518D\\u67E5\\u66F4\\u65B0\"\n },\n \"installation-changed\": {\n title: \"\\u5B89\\u88C5\\u4F4D\\u7F6E\\u5728\\u4F7F\\u7528\\u4E2D\\u9014\\u53D8\\u4E86\\uFF08\\u6362\\u4E86\\u76EE\\u5F55\\u6216\\u6362\\u4E86\\u5305\\uFF09\",\n action: \"\\u91CD\\u65B0\\u6253\\u5F00\\u5BBF\\u4E3B\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u8FD8\\u51FA\\u73B0\\u5C31\\u91CD\\u88C5\"\n },\n \"pending-restart\": {\n title: \"\\u65B0\\u7248\\u5DF2\\u88C5\\u5230\\u78C1\\u76D8\\uFF0C\\u6B63\\u5728\\u8DD1\\u7684\\u8FD8\\u662F\\u65E7\\u7248\",\n action: \"\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u8BA9\\u65B0\\u7248\\u8DD1\\u8D77\\u6765\\uFF1B\\u8FD9\\u662F\\u6B63\\u5E38\\u7EC8\\u6001\\uFF0C\\u4E0D\\u662F\\u5931\\u8D25\"\n },\n \"registry-conflict\": {\n title: \"\\u672C\\u5730\\u58F0\\u660E\\u7684\\u7248\\u672C\\u4E0E\\u78C1\\u76D8\\u5B9E\\u9645\\u7248\\u672C\\u4E92\\u76F8\\u77DB\\u76FE\",\n action: \"\\u6253\\u5F00\\u4F7F\\u7528\\u8303\\u56F4\\u7684\\u6E05\\u5355\\u6587\\u4EF6\\uFF0C\\u628A\\u76EE\\u6807\\u5305\\u540D\\u90A3\\u4E00\\u884C\\u6539\\u6210\\u7248\\u672C\\u53F7\\u518D\\u8BD5\"\n },\n \"incompatible-node\": {\n title: \"\\u65B0\\u7248\\u8981\\u6C42\\u7684 Node \\u4E0E\\u5F53\\u524D\\u8FD0\\u884C\\u7684\\u5BF9\\u4E0D\\u4E0A\",\n action: \"\\u5148\\u5347\\u7EA7 Node \\u5230 22 \\u6216\\u66F4\\u9AD8\\uFF0C\\u518D\\u67E5\\u66F4\\u65B0\"\n },\n \"recovery-required\": {\n title: \"\\u4E0A\\u6B21\\u5B89\\u88C5\\u88AB\\u6253\\u65AD\\uFF0C\\u7559\\u4E0B\\u4E00\\u4E2A\\u534A\\u622A\\u4EFB\\u52A1\",\n action: \"\\u91CD\\u65B0\\u70B9\\u4E00\\u6B21\\u5B89\\u88C5\\uFF1B\\u4E00\\u76F4\\u51FA\\u73B0\\u5C31\\u6309\\u7B2C 6 \\u8282\\u6392\\u9519\"\n }\n};\nfunction blockedCopy(reason) {\n if (reason === null || reason === void 0) return null;\n return BLOCKED_COPY[reason] ?? null;\n}\nconst PHONE_FAILURE_COPY = {\n \"check-failed\": {\n zh: \"\\u67E5\\u65B0\\u7248\\u6CA1\\u6210\\u529F\\uFF08\\u8054\\u7F51\\u3001\\u6E90\\u3001\\u9650\\u6D41\\u90FD\\u53EF\\u80FD\\uFF09\",\n act: \"\\u8FC7\\u4E00\\u4F1A\\u513F\\u518D\\u67E5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u5931\\u8D25\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n },\n \"invalid-release\": {\n zh: \"\\u62FF\\u5230\\u7684\\u53D1\\u5E03\\u4FE1\\u606F\\u4E0D\\u5408\\u6CD5\\uFF08\\u7248\\u672C\\u53F7\\u975E\\u6CD5\\u6216\\u5185\\u5BB9\\u5BF9\\u4E0D\\u4E0A\\uFF09\",\n act: \"\\u68C0\\u67E5\\u6E05\\u5355\\u6587\\u4EF6\\u91CC\\u7684\\u5305\\u540D\\u4E0E\\u7248\\u672C\\u5199\\u6CD5\\uFF0C\\u518D\\u67E5\\u4E00\\u6B21\"\n },\n \"check-expired\": {\n zh: \"\\u51ED\\u8BC1\\u8FC7\\u671F\\u4E86\\uFF0C\\u5B89\\u88C5\\u8BF7\\u6C42\\u88AB\\u62D2\",\n act: \"\\u91CD\\u65B0\\u67E5\\u4E00\\u6B21\\u65B0\\u7248\\u518D\\u70B9\\u5B89\\u88C5\\uFF0C\\u4E0D\\u8981\\u91CD\\u8BD5\\u65E7\\u7F16\\u53F7\"\n },\n \"update-busy\": {\n zh: \"\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u6B63\\u5728\\u88C5\\u53E6\\u4E00\\u4E2A\",\n act: \"\\u7B49\\u5F53\\u524D\\u4EFB\\u52A1\\u79BB\\u5F00 installing/verifying \\u518D\\u70B9\\uFF1B\\u6392\\u961F\\u4E2D\\u53BB\\u67E5\\u72B6\\u6001\\u770B\\u4F4D\\u7F6E\"\n },\n \"install-failed\": {\n zh: \"\\u88C5\\u4E0D\\u4E0A\\uFF08\\u8BE6\\u89C1\\u8BCA\\u65AD\\u6458\\u8981\\uFF09\",\n act: \"\\u5148\\u770B\\u590D\\u5236\\u8BCA\\u65AD\\uFF1B\\u5B98\\u65B9\\u684C\\u9762\\u7248\\u628A\\u8FD9\\u6BB5\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n },\n internal: {\n zh: \"\\u51FA\\u4E86\\u70B9\\u95EE\\u9898\\uFF0C\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\",\n act: \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\"\n }\n};\nconst UNKNOWN_FAILURE_COPY = {\n zh: \"\\u51FA\\u4E86\\u70B9\\u95EE\\u9898\\uFF0C\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\",\n act: \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF08\\u5E26\\u4E0A\\u4F60\\u770B\\u5230\\u7684\\u7801\\uFF09\"\n};\nfunction isKnownFailureCode(code) {\n if (typeof code !== \"string\" || !code) return false;\n const c = code.trim();\n if (!c) return false;\n return c in BLOCKED_COPY || c in PHONE_FAILURE_COPY;\n}\nfunction failureCopy(code) {\n if (typeof code !== \"string\") return null;\n const c = code.trim();\n if (!c) return null;\n const blocked = BLOCKED_COPY[c];\n if (blocked) return { zh: blocked.title, act: blocked.action };\n const phone = PHONE_FAILURE_COPY[c];\n if (phone) return phone;\n return UNKNOWN_FAILURE_COPY;\n}\nfunction failureCodeOf(reply, fallback) {\n const kind = reply && typeof reply.errorKind === \"string\" ? reply.errorKind.trim() : \"\";\n if (kind) return kind;\n const err = reply && typeof reply.error === \"string\" ? reply.error.trim() : \"\";\n if (err) return err;\n if (typeof fallback === \"string\" && fallback.trim()) return fallback.trim();\n return \"internal\";\n}\nconst PANEL_DIAG_MAX_CHARS = COPY_BUDGET_CHARS;\nfunction redactForCopy(text) {\n return sanitizeForCopy(text);\n}\nfunction buildDiagnosticText(input) {\n const code = String(input.code || \"check-failed\");\n const lines = [`[\\u66F4\\u65B0\\u8BCA\\u65AD] ${String(input.pluginId)} \\u7A33\\u5B9A\\u7801\\uFF1A${code}`];\n const detail = redactForCopy(input.detail ?? \"\");\n lines.push(`\\u4EBA\\u8BDD\\uFF1A${detail || failureCopy(code)?.act || code}`);\n const versions = [\n `\\u8FD0\\u884C\\u7248\\uFF1A${input.runningVersion ?? \"\\u672A\\u77E5\"}`,\n `\\u5DF2\\u88C5\\uFF1A${input.installedVersion ?? \"\\u672A\\u77E5\"}`,\n `\\u8FDC\\u7AEF\\uFF1A${input.latestVersion ?? \"\\u672A\\u67E5\\u8FC7\"}`\n ].join(\" / \");\n lines.push(versions);\n const queue = input.queuePosition === 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5\" : typeof input.queuePosition === \"number\" ? `\\u6392\\u961F\\u7B2C ${input.queuePosition} \\u4F4D` : \"\\u4E0D\\u5728\\u961F\\u5217\\u91CC\";\n const hostLine = [`\\u5BBF\\u4E3B\\uFF1A${input.hostKind ?? \"\\u672A\\u77E5\"}`];\n if (typeof input.profileName === \"string\" && input.profileName) hostLine.push(`\\u4F7F\\u7528\\u8303\\u56F4\\uFF1A${input.profileName}`);\n hostLine.push(`\\u961F\\u5217\\uFF1A${queue}`);\n lines.push(hostLine.join(\" / \") + (input.requestId ? ` / \\u8BF7\\u6C42\\u7F16\\u53F7\\uFF1A${input.requestId}` : \"\"));\n const manual = redactForCopy(input.manual ?? \"\");\n if (manual) lines.push(`\\u624B\\u5DE5\\u547D\\u4EE4\\uFF1A${manual}`);\n return redactForCopy(lines.join(\"\\n\"));\n}\nconst DIAG_KNOWN_TYPES = {\n v: \"number\",\n stage: \"string\",\n route: \"string\",\n method: \"string\",\n httpStatus: \"number\",\n exitCode: \"number\",\n latencyMs: \"number\",\n detail: \"string\",\n targetPackageName: \"string\",\n runningVersion: \"string\",\n latestVersion: \"string\",\n environmentKind: \"string\",\n requestId: \"string\",\n checkId: \"string\",\n registryHost: \"string\",\n action: \"string\"\n};\nfunction readDiagTolerant(diag) {\n const empty = {\n stage: null,\n route: null,\n method: null,\n httpStatus: null,\n exitCode: null,\n latencyMs: null,\n detail: null,\n targetPackageName: null,\n runningVersion: null,\n latestVersion: null,\n environmentKind: null,\n requestId: null,\n checkId: null,\n registryHost: null,\n action: null,\n unknownKeys: []\n };\n if (!diag || typeof diag !== \"object\" || Array.isArray(diag)) return empty;\n const raw = diag;\n const out = { ...empty, unknownKeys: [] };\n for (const key of Object.keys(DIAG_KNOWN_TYPES)) {\n if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;\n const v = raw[key];\n if (v === void 0 || v === null) continue;\n if (typeof v !== DIAG_KNOWN_TYPES[key]) continue;\n out[key] = v;\n }\n for (const key of Object.keys(raw)) {\n if (!Object.prototype.hasOwnProperty.call(DIAG_KNOWN_TYPES, key)) out.unknownKeys.push(key);\n }\n return out;\n}\nfunction pickText(value, fallback) {\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return fallback;\n}\nfunction queueTextOf(queuePosition, _diagQueuePos) {\n void _diagQueuePos;\n const pos = typeof queuePosition === \"number\" ? queuePosition : null;\n if (pos === 0) return \"\\u6B63\\u5728\\u5B89\\u88C5\";\n if (typeof pos === \"number\") return `\\u6392\\u961F\\u7B2C ${pos} \\u4F4D`;\n return \"\\u4E0D\\u5728\\u961F\\u5217\\u91CC\";\n}\nfunction buildUpdateDiagCopy(input) {\n const rawCode = String(input.code ?? \"\").trim() || \"internal\";\n const copy = failureCopy(rawCode) ?? UNKNOWN_FAILURE_COPY;\n const diag = readDiagTolerant(input.diag);\n const detailRaw = diag.detail ?? input.detail ?? \"\";\n const detail = redactForCopy(detailRaw) || \"\\uFF08\\u672C\\u56DE\\u5305\\u6CA1\\u6709\\u5E26\\u8BCA\\u65AD\\u6458\\u8981\\uFF0C\\u7B49\\u7535\\u8BDD\\u4FA7 diag \\u843D\\u5B9A\\u540E\\u8865\\u9F50\\uFF09\";\n const pluginName = pickText(diag.targetPackageName ?? input.pluginId, \"(\\u672A\\u77E5\\u5305)\");\n const runVer = pickText(diag.runningVersion ?? input.runningVersion, \"?\");\n const instVer = pickText(input.installedVersion ?? diag.latestVersion, \"?\");\n const host = pickText(diag.environmentKind ?? input.hostKind, \"\\u672A\\u77E5\");\n const routeRaw = diag.route ?? input.route;\n const requestRaw = diag.requestId ?? input.requestId;\n const checkRaw = diag.checkId ?? input.checkId;\n const queue = queueTextOf(input.queuePosition ?? null);\n const prov = [`\\u63D2\\u4EF6=${pluginName}`, `\\u7248\\u672C=${runVer}\\u2192${instVer}`, `\\u5BBF\\u4E3B=${host}`];\n prov.push(`\\u4F7F\\u7528\\u8303\\u56F4=${pickText(input.profileName, \"\\u672A\\u77E5\")}`);\n if (typeof routeRaw === \"string\" && routeRaw.trim()) prov.push(`\\u8DEF\\u7531=${routeRaw.trim()}`);\n if (diag.stage) prov.push(`\\u9636\\u6BB5=${diag.stage}`);\n if (diag.method) prov.push(`\\u65B9\\u6CD5=${diag.method}`);\n if (typeof diag.httpStatus === \"number\") prov.push(`HTTP=${diag.httpStatus}`);\n if (typeof diag.exitCode === \"number\") prov.push(`exit=${diag.exitCode}`);\n if (typeof diag.latencyMs === \"number\") prov.push(`\\u8017\\u65F6=${diag.latencyMs}ms`);\n if (diag.registryHost) prov.push(`\\u6E90=${diag.registryHost}`);\n else prov.push(\"\\u6E90=\\u672A\\u77E5\\uFF08\\u975E\\u5B98\\u65B9\\u6E90\\u65F6\\u7701\\u7565\\u672C\\u8EAB\\u5373\\u4FE1\\u606F\\uFF09\");\n if (diag.action) prov.push(`\\u5EFA\\u8BAE=${diag.action}`);\n if (typeof requestRaw === \"string\" && requestRaw.trim()) prov.push(`\\u8BF7\\u6C42=${requestRaw.trim()}`);\n if (typeof checkRaw === \"string\" && checkRaw.trim()) prov.push(`\\u68C0\\u67E5=${checkRaw.trim()}`);\n prov.push(`\\u961F\\u5217=${queue}`);\n const format = input.format === \"line\" ? \"line\" : \"block\";\n const provLine = redactForCopy(prov.join(\" \\xB7 \"));\n if (format === \"line\") {\n const line = `[update-diag] code=${rawCode} \\xB7 ${copy.zh} \\xB7 \\u6458\\u8981=${detail} \\xB7 ${provLine} \\xB7 \\u600E\\u4E48\\u529E=${copy.act}`;\n return redactForCopy(line);\n }\n const block = [\n `[update-diag] ${rawCode} \\u2014 ${copy.zh}`,\n ` \\u6458\\u8981\\uFF1A${detail}`,\n ` \\u6765\\u6E90\\uFF1A${provLine}`,\n ` \\u600E\\u4E48\\u529E\\uFF1A${copy.act}`\n ].join(\"\\n\");\n return block;\n}\nconst PANEL_SKIPPED_MAX = 50;\nfunction panelSkipKey(pluginId) {\n return `dsh-upd-skipped/${String(pluginId)}`;\n}\nfunction normalizePanelSkipped(raw) {\n if (!raw || typeof raw !== \"object\") return [];\n const list = raw.skipped;\n if (!Array.isArray(list)) return [];\n const seen = /* @__PURE__ */ new Set();\n const out = [];\n for (const item of list) {\n if (!item || typeof item !== \"object\") continue;\n const version = item.version;\n if (!validReleaseVersion(version) || seen.has(version)) continue;\n seen.add(version);\n const at = item.skippedAt;\n out.push({ version, skippedAt: typeof at === \"number\" && Number.isFinite(at) ? at : 0 });\n }\n return out.slice(0, PANEL_SKIPPED_MAX);\n}\nfunction createMemorySkipStore(now = Date.now) {\n let skipped = [];\n function assertVersion(version) {\n if (!validReleaseVersion(version)) {\n throw new Error(`[dsh-plugin-update] \\u8DF3\\u8FC7\\u7248\\u672C\\u53F7\\u975E\\u6CD5\\uFF1A\\u987B\\u4E3A\\u53D1\\u884C\\u7248\\u672C\\u53F7\\uFF08\\u6536\\u5230 ${JSON.stringify(version)}\\uFF09`);\n }\n return version;\n }\n return {\n list: () => skipped.map((e) => e.version),\n has: (version) => validReleaseVersion(version) ? skipped.some((e) => e.version === version) : false,\n skip: (version) => {\n const v = assertVersion(version);\n const at = now();\n skipped = [{ version: v, skippedAt: typeof at === \"number\" && Number.isFinite(at) ? at : 0 }].concat(skipped.filter((e) => e.version !== v)).slice(0, PANEL_SKIPPED_MAX);\n },\n reset: (version) => {\n if (version === void 0) skipped = [];\n else skipped = skipped.filter((e) => e.version !== version);\n }\n };\n}\nfunction probeStorage(candidate) {\n try {\n if (!candidate || typeof candidate !== \"object\") return null;\n const s = candidate;\n if (typeof s.getItem !== \"function\" || typeof s.setItem !== \"function\" || typeof s.removeItem !== \"function\") return null;\n return s;\n } catch {\n return null;\n }\n}\nfunction createBrowserSkipStore(pluginId, storage, now = Date.now) {\n const memory = createMemorySkipStore(now);\n const store = probeStorage(storage) ?? probeStorage(globalThis[\"localStorage\"] ?? null) ?? null;\n if (!store) return memory;\n const active = store;\n const key = panelSkipKey(pluginId);\n function read() {\n try {\n const raw = active.getItem(key);\n if (!raw) return [];\n return normalizePanelSkipped(JSON.parse(raw));\n } catch {\n return [];\n }\n }\n function write(entries) {\n try {\n active.setItem(key, JSON.stringify({ skipped: entries }));\n } catch {\n }\n }\n try {\n for (const e of read()) memory.skip(e.version);\n } catch {\n }\n return {\n list: () => memory.list(),\n has: (version) => memory.has(version),\n skip: (version) => {\n memory.skip(version);\n write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));\n },\n reset: (version) => {\n memory.reset(version);\n if (version === void 0) {\n try {\n active.removeItem(key);\n } catch {\n }\n } else {\n write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));\n }\n }\n };\n}\nconst SEAL_BY_KIND = {\n loading: { text: \"\\u5F85\\u67E5\", mini: \"\\u67E5\", tone: \"ink\" },\n idle: { text: \"\\u5F85\\u67E5\", mini: \"\\u67E5\", tone: \"ink\" },\n update: { text: \"\\u53EF\\u88C5\", mini: \"\\u88C5\", tone: \"green\" },\n busy: { text: \"\\u5B89\\u88C5\\u4E2D\", mini: \"\\u88C5\", tone: \"yellow\" },\n restart: { text: \"\\u5F85\\u91CD\\u542F\", mini: \"\\u542F\", tone: \"yellow\" },\n blocked: { text: \"\\u53D7\\u963B\", mini: \"\\u963B\", tone: \"red\" },\n failed: { text: \"\\u53D7\\u963B\", mini: \"\\u963B\", tone: \"red\" },\n done: { text: \"\\u5DF2\\u6700\\u65B0\", mini: \"\\u5B9A\", tone: \"green\" }\n};\nfunction messageCodeOf(message) {\n if (typeof message !== \"string\" || !message) return \"\";\n const at = message.indexOf(\":\");\n return (at < 0 ? message : message.slice(0, at)).trim();\n}\nfunction panelViewModel(input) {\n const view = panelViewModelCore(input);\n return { ...view, seal: SEAL_BY_KIND[view.banner.kind] ?? SEAL_BY_KIND.idle };\n}\nfunction panelViewModelCore(input) {\n const { snapshot, manual, queue, skippedLatest, lastError } = input;\n const errorKind = input.errorKind;\n if (!snapshot) {\n const earlyCode = typeof errorKind === \"string\" && errorKind.trim() || lastError || \"\";\n if (earlyCode) {\n const copy = failureCopy(earlyCode);\n return {\n banner: {\n kind: \"failed\",\n title: `\\u66F4\\u65B0\\u5931\\u8D25\\uFF08${earlyCode}\\uFF09\\uFF1A${copy?.zh ?? earlyCode}\\u3002`,\n action: copy?.act || \"\\u590D\\u5236\\u8BCA\\u65AD\\u53D1\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\u901A\\u9053\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u91CD\\u8BD5\\u5B89\\u88C5\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote: null\n };\n }\n return {\n banner: { kind: \"loading\", title: \"\\u6B63\\u5728\\u8BFB\\u53D6\\u66F4\\u65B0\\u72B6\\u6001\\u2026\", action: \"\" },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote: null\n };\n }\n const job = snapshot.job ?? null;\n const jobState = job?.state ?? null;\n const queueNote = queue && queue.busy ? queue.position === 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5\\uFF08\\u672C\\u63D2\\u4EF6\\u5728\\u88C5\\uFF09\\u3002\" : typeof queue.position === \"number\" ? `\\u524D\\u65B9\\u6709\\u5B89\\u88C5\\u5728\\u8FDB\\u884C\\uFF0C\\u672C\\u63D2\\u4EF6\\u6392\\u7B2C ${queue.position} \\u4F4D\\uFF0C\\u5230\\u961F\\u9996\\u518D\\u70B9\\u5B89\\u88C5\\u3002` : \"\\u524D\\u65B9\\u6709\\u5176\\u4ED6\\u63D2\\u4EF6\\u5728\\u5B89\\u88C5\\uFF0C\\u7A0D\\u540E\\u91CD\\u8BD5\\u3002\" : null;\n if (skippedLatest && snapshot.latestVersion) {\n return {\n banner: {\n kind: \"idle\",\n title: `\\u5DF2\\u8DF3\\u8FC7 ${snapshot.latestVersion}`,\n action: \"\\u70B9\\u201C\\u6062\\u590D\\u201D\\u53EF\\u91CD\\u65B0\\u63D0\\u9192\\u8BE5\\u7248\\u672C\\uFF1B\\u6709\\u66F4\\u65B0\\u7684\\u65B0\\u7248\\u672C\\u4F1A\\u7167\\u5E38\\u63D0\\u9192\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: true,\n showManual: false,\n showReset: true,\n queueNote\n };\n }\n if (snapshot.blockedReason === \"pending-restart\") {\n const latest = snapshot.latestVersion ?? snapshot.installedVersion ?? \"\";\n return {\n banner: {\n kind: \"restart\",\n // 文案照原型(d5-paper.html:329):不带 emoji——警示由横幅左侧的手绘 SVG 标承担,\n // 印章在状态一侧,两者各司其职,不再三重标记。\n title: `\\u65B0\\u7248 ${latest} \\u5DF2\\u5B89\\u88C5\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002`,\n action: BLOCKED_COPY[\"pending-restart\"].action\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (jobState === \"installing\" || jobState === \"verifying\") {\n return {\n banner: {\n kind: \"busy\",\n title: `\\u6B63\\u5728\\u5B89\\u88C5${job?.targetVersion ? ` ${job.targetVersion}` : \"\"}\\u2026\\u5173\\u95ED\\u9762\\u677F\\u4E0D\\u4F1A\\u4E2D\\u65AD\\u3002`,\n action: \"\\u8FDB\\u5EA6\\u6309\\u8F6E\\u8BE2\\u81EA\\u52A8\\u5237\\u65B0\\uFF1B\\u91CD\\u5F00\\u9762\\u677F 1 \\u79D2\\u5185\\u6062\\u590D\\u663E\\u793A\\u3002\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u4E2D\\u2026\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote\n };\n }\n const jobCode = jobState === \"failed\" ? messageCodeOf(job?.message) || \"install-failed\" : \"\";\n const failedCode = typeof errorKind === \"string\" && errorKind.trim() || lastError || jobCode || \"\";\n if (failedCode || jobState === \"failed\" || jobState === \"interrupted\") {\n const code = failedCode || \"install-failed\";\n const copy = failureCopy(code);\n return {\n banner: {\n kind: \"failed\",\n title: `\\u66F4\\u65B0\\u5931\\u8D25\\uFF08${code}\\uFF09\\uFF1A${copy?.zh ?? code}\\u3002`,\n action: copy?.act || \"\\u590D\\u5236\\u8BCA\\u65AD\\u53D1\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\uFF1B\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\u901A\\u9053\\u3002\"\n },\n installEnabled: snapshot.canInstall,\n installLabel: \"\\u91CD\\u8BD5\\u5B89\\u88C5\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (snapshot.blockedReason) {\n const copy = blockedCopy(snapshot.blockedReason);\n return {\n banner: {\n kind: \"blocked\",\n title: copy ? `${copy.title}\\u3002` : `${snapshot.blockedReason}\\u3002`,\n action: copy?.action || \"\"\n },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n if (snapshot.canInstall && snapshot.latestVersion) {\n return {\n banner: {\n kind: \"update\",\n title: `\\u6709\\u65B0\\u7248 ${snapshot.latestVersion} \\u53EF\\u88C5\\uFF08\\u5F53\\u524D ${snapshot.runningVersion}\\uFF09\\u3002`,\n action: \"\\u70B9\\u5B89\\u88C5\\u5373\\u8D70\\u7CBE\\u786E\\u7248\\u672C\\u5B89\\u88C5\\uFF1B\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u540C\\u65F6\\u53EA\\u88C5\\u4E00\\u4E2A\\u3002\"\n },\n installEnabled: true,\n installLabel: `\\u5B89\\u88C5 ${snapshot.latestVersion}`,\n skippedLatest: false,\n showManual: manual ? true : false,\n showReset: false,\n queueNote\n };\n }\n return {\n banner: { kind: \"done\", title: \"\\u5DF2\\u662F\\u6700\\u65B0\\uFF0C\\u65E0\\u9700\\u66F4\\u65B0\\u3002\", action: \"\" },\n installEnabled: false,\n installLabel: \"\\u5B89\\u88C5\\u66F4\\u65B0\",\n skippedLatest: false,\n showManual: false,\n showReset: false,\n queueNote\n };\n}\nconst UPDATE_PANEL_CSS = [\n '.dsh-upd{font:14px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937);',\n \"background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:8px;padding:12px 14px;max-width:560px;\",\n // 横幅配色走变量(浅色默认 + 深色覆盖,见下方 dark 媒体块):硬编码浅色会让深色下\n // 「浅底 + 浅字」读不出来(现场回归:默认主题深色模式更新横幅白底浅字)。\n \"--dsh-upd-ok-bg:#ecfdf5;--dsh-upd-ok-line:#059669;--dsh-upd-warn-bg:#fffbeb;--dsh-upd-warn-line:#d97706;\",\n \"--dsh-upd-bad-bg:#fef2f2;--dsh-upd-bad-line:#dc2626;--dsh-upd-busy-bg:#eff6ff;--dsh-upd-busy-line:#2563eb}\",\n \".dsh-upd *{box-sizing:border-box}\",\n \".dsh-upd button{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;background:var(--dsh-upd-btn,#f9fafb);\",\n \"color:inherit;padding:4px 12px;cursor:pointer;margin:2px 6px 2px 0}\",\n \".dsh-upd button:disabled{opacity:.45;cursor:not-allowed}\",\n \".dsh-upd button:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}\",\n '.dsh-upd button[data-primary=\"1\"]{background:var(--dsh-upd-primary,#2563eb);border-color:var(--dsh-upd-primary,#2563eb);color:#fff}',\n \".dsh-upd-banner{border-left:4px solid var(--dsh-upd-line,#9ca3af);padding:6px 10px;margin:0 0 8px;background:var(--dsh-upd-soft,#f3f4f6)}\",\n '.dsh-upd-banner[data-kind=\"restart\"]{border-color:var(--dsh-upd-warn-line);background:var(--dsh-upd-warn-bg)}',\n '.dsh-upd-banner[data-kind=\"failed\"],.dsh-upd-banner[data-kind=\"blocked\"]{border-color:var(--dsh-upd-bad-line);background:var(--dsh-upd-bad-bg)}',\n '.dsh-upd-banner[data-kind=\"update\"]{border-color:var(--dsh-upd-ok-line);background:var(--dsh-upd-ok-bg)}',\n '.dsh-upd-banner[data-kind=\"busy\"]{border-color:var(--dsh-upd-busy-line);background:var(--dsh-upd-busy-bg)}',\n \".dsh-upd code{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}\",\n \".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:4px 0;font-size:13px}\",\n \".dsh-upd-changelog-wrap{margin:4px 0 0;font-size:13px}\",\n \".dsh-upd-changelog-title{font-weight:700;margin:0 0 4px}\",\n \".dsh-upd-changelog-version{margin:6px 0}\",\n \".dsh-upd-changelog-catname{font-weight:600;margin:6px 0 2px}\",\n \".dsh-upd-changelog ul{margin:2px 0 6px 20px;padding:0}\",\n \".dsh-upd-changelog li{margin:2px 0}\",\n \".dsh-upd-changelog-fold{margin:4px 0}\",\n \".dsh-upd-changelog-fold>summary{cursor:pointer}\",\n \".dsh-upd-changelog-count{font-size:12px;opacity:.7;margin:2px 0 4px}\",\n \".dsh-upd-changelog-yanked{border-left:4px solid var(--dsh-upd-warn-line,#d97706);background:var(--dsh-upd-warn-bg,#fffbeb);padding:6px 10px;margin:6px 0;font-size:13px}\",\n \".dsh-upd-breaking-badge{display:inline-block;font-size:11px;font-weight:700;border:1px solid currentColor;border-radius:3px;padding:0 5px;margin-right:6px;vertical-align:baseline}\",\n \".dsh-upd-changelog-security-more{margin:4px 0 6px}\",\n \".dsh-upd-changelog-security-more>summary{cursor:pointer;font-size:12px;opacity:.8}\",\n \".dsh-upd-changelog-more-note{font-size:12px;opacity:.7;margin:2px 0 4px}\",\n \".dsh-upd-changelog-neutral{color:inherit;opacity:.8}\",\n \".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}\",\n \".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}\",\n // —— 弹窗分栏滚动:头(抬头/档案头/横幅/版本条)与尾固定,只有 01–05 章节区滚动 ——\n \".dsh-upd-body{min-height:0}\",\n \".dsh-upd-overlay .dsh-upd-body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsh-upd-line,#e5e7eb) transparent}\",\n \".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar{width:8px}\",\n \".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--dsh-upd-line,#e5e7eb);border-radius:4px}\",\n \".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-track{background:transparent}\",\n // —— 章节标题磁吸:滚动时节标题贴顶(纯 CSS sticky;背景跟随主题,D5 另覆)——\n \".dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{position:sticky;top:0;z-index:1;background:var(--dsh-upd-bg,#ffffff);padding-top:2px}\",\n // —— 档案头 / 版本条 / 章节 / 进度条 / 跳过行(原型 :208-245 的新结构,默认主题给最小可用样式)——\n \".dsh-upd-head{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}\",\n // 卷宗抬头「插件更新 / 更新档案 卷」:D5 档案卷才画,默认(最小)主题不画。\n // 两个主题共用同一份内核 HTML(见 renderUpdatePanelHTML 的注释),画不画是皮肤决定的事。\n \".dsh-upd-masthead{display:none}\",\n \".dsh-upd-name{font-weight:700}\",\n \".dsh-upd-meta{font-size:12.5px;opacity:.75}\",\n \".dsh-upd-proftag{font-family:Consolas,Menlo,monospace;font-size:11px;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:3px;padding:0 5px;margin-left:6px;letter-spacing:.06em}\",\n \".dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;font-size:12.5px}\",\n \".dsh-upd-strip>div{flex:1 1 110px;padding:6px 10px;border-left:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-strip>div:first-child{border-left:0}\",\n \".dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.14em;opacity:.7}\",\n \".dsh-upd-strip-v{font-family:Consolas,Menlo,monospace;font-size:12.5px}\",\n \".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n // 右下角独立 footer 区(#47 定案 A):与第一章 actions 脱钩,右对齐,一次找到。\n \".dsh-upd-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-foot-note{margin-right:auto;font-size:12px;opacity:.7}\",\n \".dsh-upd-footer button{margin:0}\",\n \".dsh-upd-chap-head{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}\",\n \".dsh-upd-chap-no{font-size:13px;font-style:italic;opacity:.6}\",\n \".dsh-upd-chap-title{font-size:14px;margin:0}\",\n \".dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-upd-line,#e5e7eb);transform:translateY(-3px)}\",\n // 03 章标题行右端的开关(问题 3 定案:按钮形态、挪到标题行)。\n \".dsh-upd-chap-note{flex:none;font-size:12px;opacity:.75}\",\n \".dsh-upd-chap-note button{margin:0}\",\n // 更新队列两行键值(用户定案的设计):结构两主题共用,皮肤各自收敛。\n \".dsh-upd-qrow{display:flex;align-items:baseline;gap:10px;padding:6px 0}\",\n \".dsh-upd-qrow+.dsh-upd-qrow{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-qdot{width:8px;height:8px;border-radius:50%;flex:none;align-self:center;background:currentColor;opacity:.5}\",\n '.dsh-upd-qdot[data-tone=\"busy\"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',\n '.dsh-upd-qdot[data-tone=\"you\"]{background:var(--dsh-upd-primary,#2563eb);opacity:1}',\n \".dsh-upd-qk{flex:none;width:5.5em;font-size:12px;opacity:.7}\",\n \".dsh-upd-qv{font-weight:600}\",\n \".dsh-upd-qn{margin-left:auto;font-size:12px;opacity:.7}\",\n \".dsh-upd-qseq{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}\",\n \".dsh-upd-prog{height:8px;background:var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;margin:10px 0 4px}\",\n \".dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-upd-primary,#2563eb);transition:width .3s}\",\n \".dsh-upd-progtxt{font-size:12.5px;opacity:.75}\",\n \".dsh-upd-skipline{font-size:13px;margin-top:8px}\",\n // 骨架微光:只在首帧 loading 出现;reduced-motion 下静止占位,不断语义。\n \".dsh-upd-skv{display:inline-block;min-width:64px;border-radius:3px;color:transparent !important;user-select:none;\",\n \"background:linear-gradient(90deg,var(--dsh-upd-line,#e5e7eb) 25%,var(--dsh-upd-soft,#f3f4f6) 50%,var(--dsh-upd-line,#e5e7eb) 75%);\",\n \"background-size:200% 100%;animation:dsh-upd-shimmer 1.2s linear infinite}\",\n \"@keyframes dsh-upd-shimmer{to{background-position:-200% 0}}\",\n \".dsh-upd-tag{display:inline-block;border:1px dashed currentColor;border-radius:3px;padding:1px 8px;margin-right:8px;font-family:Consolas,Menlo,monospace;font-size:12px}\",\n \".dsh-upd-err{font-size:13px;margin:0 0 6px}\",\n // —— 全按钮交互反馈(#36:悬停/按下/过渡/在途忙态;浅深双主题通用写法,不碰上面的既有串)——\n \".dsh-upd button{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}\",\n \".dsh-upd button:hover:not(:disabled){border-color:var(--dsh-upd-focus,#2563eb)}\",\n '.dsh-upd button[data-primary=\"1\"]:hover:not(:disabled){filter:brightness(.93)}',\n \".dsh-upd button:active:not(:disabled){transform:translateY(1px)}\",\n '.dsh-upd button[aria-busy=\"true\"]{cursor:wait;animation:dsh-upd-pulse 1s ease-in-out infinite}',\n \"@keyframes dsh-upd-pulse{0%,100%{opacity:1}50%{opacity:.55}}\",\n // —— 状态横幅淡入 + 日志折叠格动画(纯 CSS;重绘只发生在真变时;reduced-motion 下静止)——\n \".dsh-upd-banner{animation:dsh-upd-fadein .22s ease}\",\n \"@keyframes dsh-upd-fadein{from{opacity:.35;transform:translateY(2px)}}\",\n \".dsh-upd-changelog-foldbox{display:grid;grid-template-rows:1fr;opacity:1;transition:grid-template-rows .22s ease,opacity .18s ease}\",\n \".dsh-upd-changelog-foldbox-inner{min-height:0;overflow:hidden}\",\n '.dsh-upd-changelog-foldbox[data-open=\"0\"]{grid-template-rows:0fr;opacity:0}',\n // 在途转圈:纯 CSS ::after,不加 DOM 节点(内核 DOM 冻结);转的是边框缺口,不是 emoji。\n '.dsh-upd button[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:11px;height:11px;margin-left:8px;vertical-align:-1px;',\n \"border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-spin .8s linear infinite}\",\n \"@keyframes dsh-upd-spin{to{transform:rotate(360deg)}}\",\n '@media (prefers-reduced-motion: reduce){.dsh-upd button{transition:none}.dsh-upd button:active:not(:disabled){transform:none}.dsh-upd button[aria-busy=\"true\"]{animation:none}.dsh-upd-skv{animation:none}.dsh-upd-banner{animation:none}.dsh-upd-changelog-foldbox{transition:none}}',\n \"@media (forced-colors: active){.dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{background:Canvas}}\",\n \"@media (prefers-color-scheme: dark){.dsh-upd{--dsh-upd-fg:#e5e7eb;--dsh-upd-bg:#111827;--dsh-upd-line:#374151;\",\n \"--dsh-upd-btn:#1f2937;--dsh-upd-soft:#1f2937;--dsh-upd-primary:#3b82f6;--dsh-upd-focus:#93c5fd;\",\n // 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。\n \"--dsh-upd-ok-bg:rgba(16,185,129,.14);--dsh-upd-ok-line:#34d399;\",\n \"--dsh-upd-warn-bg:rgba(245,158,11,.16);--dsh-upd-warn-line:#fbbf24;\",\n \"--dsh-upd-bad-bg:rgba(239,68,68,.16);--dsh-upd-bad-line:#f87171;\",\n \"--dsh-upd-busy-bg:rgba(59,130,246,.16);--dsh-upd-busy-line:#60a5fa}}\"\n].join(\"\\n\");\nconst UPDATE_PANEL_D5_CSS = [\n \"/* D5 \\u6863\\u6848\\u5377\\u53EF\\u9009\\u4E3B\\u9898\\uFF1A\\u53EA\\u6362\\u989C\\u8272/\\u5B57\\u4F53/\\u95F4\\u8DDD\\uFF1B\\u5185\\u6838 DOM \\u987A\\u5E8F\\u4E00\\u5B57\\u4E0D\\u52A8\\uFF0C\\u4E0D\\u65AD\\u590D\\u5236\\u8BCA\\u65AD\\u3002 */\",\n '.dsh-upd[data-theme=\"archive\"]{--d5-bg:#f7f3ea;--d5-card:#fffdf6;--d5-ink:#1a1a1a;--d5-muted:#6f675a;',\n \"--d5-line:#e3d9c4;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-accent-deep:#9c2e1d;\",\n \"--d5-ok:#1a7f37;--d5-ok-bg:#e9f4ea;--d5-warn:#8a5a00;--d5-warn-bg:#fbf0d0;\",\n \"--d5-bad:#b3261e;--d5-bad-bg:#fbe9e5;\",\n '--d5-serif:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",\"Noto Serif CJK SC\",\"Source Han Serif SC\",serif;',\n '--d5-sans:system-ui,\"PingFang SC\",\"Hiragino Sans GB\",\"Microsoft YaHei\",sans-serif;',\n '--d5-mono:ui-monospace,\"SF Mono\",SFMono-Regular,Consolas,\"Noto Sans Mono\",monospace;',\n \"--d5-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);\",\n \"font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);\",\n \"border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}\",\n // 按钮脸自己不透明(宿主底色未知时也读得出;卡片上渲染与 transparent 逐字同色)。\n '.dsh-upd[data-theme=\"archive\"] button{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',\n '.dsh-upd[data-theme=\"archive\"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"archive\"] button[data-primary=\"1\"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',\n '.dsh-upd[data-theme=\"archive\"] button[data-primary=\"1\"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',\n '.dsh-upd[data-theme=\"archive\"] button:focus-visible{outline:2px solid var(--d5-accent);outline-offset:2px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner{background:var(--d5-bg);border-color:var(--d5-line-strong)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"update\"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"busy\"]{border-color:var(--d5-warn);background:var(--d5-warn-bg)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"restart\"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"failed\"],.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"blocked\"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"done\"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-yanked{border-color:var(--d5-warn);background:var(--d5-warn-bg);color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-breaking-badge{color:var(--d5-accent);border-color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-count{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-more-note{color:var(--d5-muted)}',\n // —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——\n '.dsh-upd[data-theme=\"archive\"]{position:relative;padding:16px 20px 14px}',\n '.dsh-upd[data-theme=\"archive\"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',\n \"display:flex;align-items:center;justify-content:center;text-align:center;letter-spacing:.18em;text-indent:.18em;line-height:1.35;\",\n \"border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--d5-serif);font-weight:700;font-size:21px;\",\n \"background:color-mix(in srgb,currentColor 8%,transparent);user-select:none;pointer-events:none;\",\n \"box-shadow:inset 0 0 0 5px var(--d5-card),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}\",\n '.dsh-upd[data-theme=\"archive\"][data-seal-tone=\"ink\"]::before{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"][data-seal-tone=\"green\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"archive\"][data-seal-tone=\"yellow\"]::before{color:var(--d5-warn)}',\n '.dsh-upd[data-theme=\"archive\"][data-seal-tone=\"red\"]::before{color:var(--d5-bad)}',\n // 印章占位:首行(横幅/状态行)右侧留出 120px,文字不许压到印章上(原型 .filehead padding-right:120px)\n // —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 D5 档案卷才显示 ——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead{display:block;padding:0 0 8px;margin:0 0 8px;border-bottom:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--d5-muted);margin-bottom:3px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead-title{font-family:var(--d5-serif);font-size:26px;font-weight:700;line-height:1.2}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead-title i{color:var(--d5-accent);font-style:normal}',\n // 横幅不再给大印章留 124px:实测(headless 量盒子)印章盒底边 y=109,横幅正文顶边 y=108、\n // 状态行那句在 y=159——印章只压到横幅顶部的留白带,压不到正文。留着反而把 27px 那句话挤成两行\n // (27px 单行需 428px,留白后只剩 366px)。档案头那 120px 保留:那里是真的重叠。\n // —— 更新队列(03 章)D5 皮肤 ——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-qrow{padding:6px 0}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-qk{width:66px;letter-spacing:.18em}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-qv{font-family:var(--d5-serif);font-size:16px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-qseq{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chap-note{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-body .dsh-upd-chap-head{background:var(--d5-card)}',\n '.dsh-upd-overlay .dsh-upd[data-theme=\"archive\"] .dsh-upd-body{scrollbar-color:var(--d5-line-strong) transparent}',\n '.dsh-upd-overlay .dsh-upd[data-theme=\"archive\"] .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--d5-line-strong)}',\n // —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——\n // 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"loading\"]::before,.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"idle\"]::before,',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"update\"]::before,.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"busy\"]::before,',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"blocked\"]::before,.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"failed\"]::before,',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"done\"]::before{content:attr(data-mini);display:inline-flex;align-items:center;justify-content:center;',\n \"width:30px;height:30px;margin-right:10px;vertical-align:middle;border:2px solid currentColor;border-radius:7px;\",\n \"font-family:var(--d5-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--d5-muted)}\",\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"update\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"busy\"]::before{color:var(--d5-warn)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"blocked\"]::before,.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"failed\"]::before{color:var(--d5-bad)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"done\"]::before{color:var(--d5-ok)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-log code{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;letter-spacing:.06em}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-manual code{display:block;background:var(--d5-ink);color:var(--d5-bg);font-family:var(--d5-mono);font-size:12.5px;padding:12px 14px;border-radius:4px;white-space:pre-wrap;word-break:break-all}',\n // 待重启标记:手绘 SVG 当**独立 flex 标记**放在文字块左侧(原型 :446 `.mark` 是独立节点),\n // 不能用行内背景——那样换行时三角会落在句子中间把话劈开(现场回归)。\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child{display:flex;gap:10px;align-items:flex-start}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{content:\"\";flex:none;width:20px;height:20px;margin-top:3px;',\n 'background:url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%238a5a00%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E\") no-repeat center/20px 20px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-actions{flex-wrap:wrap}',\n // footer 只换肤(#47 定案 A + D5 约束:不换 DOM 顺序;复制诊断永不隐藏,本串不动它)。\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-footer{margin-top:14px;padding-top:10px;border-top:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-foot-note{color:var(--d5-muted)}',\n // —— 档案头(原型 :208-211 `.filehead`:serif 插件名 22px + 使用范围 + profile 牌;右侧留章位)——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-meta b{color:var(--d5-ink);font-weight:600}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-proftag{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;margin-left:8px;letter-spacing:.06em}',\n // —— 版本条(原型 :216 `.strip`:三格,格间一线,左上小写标签 + 等宽值)——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--d5-line);border-radius:4px;overflow:hidden;font-size:12.5px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-strip>div{flex:1 1 120px;padding:6px 10px;border-left:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-strip>div:first-child{border-left:0}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-strip-v{font-family:var(--d5-mono);font-size:13px}',\n // —— 章节(原型 :218-245:01–05 编号 + 衬线标题 + 细线)——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chapter{margin-top:16px;padding-top:10px;border-top:1px solid var(--d5-line)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chap-head{display:flex;align-items:baseline;gap:12px;margin-bottom:6px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chap-no{font-family:var(--d5-serif);font-style:italic;font-size:15px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chap-title{font-family:var(--d5-serif);font-size:17px;margin:0;letter-spacing:.1em}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--d5-line);transform:translateY(-4px)}',\n // —— 横幅即状态行 / 待重启横幅(原型 :81-85 `.restart-banner`:2px 边框、圆角 4、内边距 12/16、衬线;右侧留章位)——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner{border:2px solid var(--d5-line-strong);border-radius:4px;padding:10px 12px;font-size:14.5px;font-family:var(--d5-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>div:first-child{flex:1 1 auto;min-width:0}',\n // 状态行字号照原型 .status-line=27px(实测去掉横幅右侧占位后可写 486px > 428px,一行放得下)\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>div:first-child strong{font-family:var(--d5-serif);font-size:27px;font-weight:700;line-height:1.25}',\n // —— 进度条 / 跳过行(原型 :132-133 `.prog`、:129-131 `.skipline .tag`)——\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-prog{height:8px;background:var(--d5-line);border-radius:4px;overflow:hidden;margin:10px 0 4px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--d5-accent);transition:width .3s}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-progtxt{font-size:12.5px;color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-skipline{font-size:13px;margin-top:8px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-tag{display:inline-block;border:1px dashed var(--d5-line-strong);border-radius:3px;padding:1px 8px;margin-right:8px;font-family:var(--d5-mono);font-size:12px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--d5-muted)}',\n '@media (max-width:640px){.dsh-upd[data-theme=\"archive\"]{padding:10px 12px}.dsh-upd[data-theme=\"archive\"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--d5-card),inset 0 0 0 5px currentColor}.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>div:first-child{white-space:normal}}',\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"archive\"]{--d5-bg:#141210;--d5-card:#1e1a15;--d5-ink:#ece5d3;--d5-muted:#a89c83;',\n \"--d5-line:#3a3226;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-accent-deep:#f0866b;\",\n \"--d5-ok:#8fd6a4;--d5-ok-bg:rgba(80,180,120,.12);--d5-warn:#e8c15a;--d5-warn-bg:rgba(232,193,90,.12);\",\n \"--d5-bad:#ef8a7d;--d5-bad-bg:rgba(239,138,125,.12);--d5-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.45)}}\",\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"archive\"] button[data-primary=\"1\"]{color:#141210}.dsh-upd[data-theme=\"archive\"] button:focus-visible{outline-color:var(--d5-accent-deep)}}',\n '@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23e8c15a%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E\")}}',\n '@media (forced-colors: active){.dsh-upd[data-theme=\"archive\"]{box-shadow:none}.dsh-upd[data-theme=\"archive\"]::before{background:none;box-shadow:none;border-color:CanvasText;color:CanvasText}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner{border:1px solid CanvasText}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner::before{border-color:CanvasText;color:CanvasText;background:Canvas}.dsh-upd[data-theme=\"archive\"] button{border:1px solid ButtonText}.dsh-upd[data-theme=\"archive\"] button[data-primary=\"1\"]{background:ButtonFace;color:ButtonText;border-color:ButtonText}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner[data-kind=\"restart\"]>div:first-child::before{background-image:none;content:\"\\u26A0\"}}',\n '@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme=\"archive\"] *{transition:none !important;animation:none !important}}'\n].join(\"\\n\");\nfunction escapeHtml(text) {\n return String(text).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nconst CHAPTER_TITLES = [\"\\u68C0\\u67E5\\u4E0E\\u5B89\\u88C5\", \"\\u66F4\\u65B0\\u65E5\\u5FD7\", \"\\u66F4\\u65B0\\u961F\\u5217\", \"\\u9519\\u8BEF\\u4FE1\\u606F\", \"\\u624B\\u5DE5\\u547D\\u4EE4\"];\nfunction formatLatchTime(atMs) {\n if (typeof atMs !== \"number\" || !Number.isFinite(atMs) || atMs <= 0) return null;\n try {\n const s = new Date(atMs).toLocaleTimeString(\"zh-CN\", { hour12: false });\n return s ? s : null;\n } catch {\n return null;\n }\n}\nfunction chapterOf(index, inner, note = \"\") {\n const no = String(index).padStart(2, \"0\");\n const title = CHAPTER_TITLES[index - 1];\n return `<section class=\"dsh-upd-chapter\" data-chapter=\"${no}\"><div class=\"dsh-upd-chap-head\"><span class=\"dsh-upd-chap-no\">${no}</span><h3 class=\"dsh-upd-chap-title\">${escapeHtml(title)}</h3><span class=\"dsh-upd-chap-rule\"></span>${note}</div>${inner}</section>`;\n}\nfunction skeletonStrip(loading) {\n if (!loading) return \"\";\n const cell = (k) => `<div><span class=\"dsh-upd-strip-k\">${escapeHtml(k)}</span><span class=\"dsh-upd-strip-v dsh-upd-skv\" aria-hidden=\"true\">\\u2026</span></div>`;\n return `<div class=\"dsh-upd-strip\" aria-hidden=\"true\">` + cell(\"\\u8FD0\\u884C\") + cell(\"\\u78C1\\u76D8\") + cell(\"\\u8FDC\\u7AEF\") + `</div>`;\n}\nfunction versionStrip(snapshot) {\n const cell = (k, v) => `<div><span class=\"dsh-upd-strip-k\">${escapeHtml(k)}</span><span class=\"dsh-upd-strip-v\">${escapeHtml(v ?? \"\\u672A\\u77E5\")}</span></div>`;\n if (!snapshot) return \"\";\n return `<div class=\"dsh-upd-strip\">` + cell(\"\\u8FD0\\u884C\", snapshot.runningVersion) + cell(\"\\u78C1\\u76D8\", snapshot.installedVersion) + cell(\"\\u8FDC\\u7AEF\", snapshot.latestVersion) + `</div>`;\n}\nfunction progressBar(snapshot) {\n const job = snapshot?.job;\n if (!job) return \"\";\n const state = String(job.state);\n if (state !== \"installing\" && state !== \"verifying\") return \"\";\n const width = state === \"installing\" ? 60 : 90;\n const text = state === \"installing\" ? \"\\u6B63\\u5728\\u5B89\\u88C5\\u65B0\\u7248\\u2026\" : \"\\u6B63\\u5728\\u6821\\u9A8C\\u5B89\\u88C5\\u7ED3\\u679C\\u2026\";\n return `<div class=\"dsh-upd-prog\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"${width}\"><i class=\"dsh-upd-prog-bar\" style=\"width:${width}%\"></i></div><div class=\"dsh-upd-progtxt\">${escapeHtml(text)}</div>`;\n}\nfunction renderUpdatePanelKernel(input, view) {\n const { snapshot, manual, queue, mode, showOthers, pluginId, copyNotice } = input;\n const changelogMarkdown = input.changelogMarkdown ?? null;\n const profileName = typeof input.profileName === \"string\" && input.profileName ? input.profileName : null;\n const b = view.banner;\n const seal = view.seal;\n const parts = [];\n parts.push(\n '<div class=\"dsh-upd-masthead\"><span class=\"dsh-upd-masthead-kicker\">\\u63D2\\u4EF6\\u66F4\\u65B0</span><span class=\"dsh-upd-masthead-title\">\\u66F4\\u65B0\\u6863\\u6848 <i>\\u5377</i></span></div>'\n );\n parts.push(\n `<div class=\"dsh-upd-head\"><span class=\"dsh-upd-name\">${escapeHtml(pluginId)}</span><span class=\"dsh-upd-meta\">\\u4F7F\\u7528\\u8303\\u56F4 <b>${escapeHtml(profileName ?? \"\\u672A\\u77E5\")}</b><span class=\"dsh-upd-proftag\">profile</span></span></div>`\n );\n parts.push(`<div class=\"dsh-upd-banner\" data-kind=\"${b.kind}\" data-mini=\"${escapeHtml(seal.mini)}\" role=\"status\" aria-live=\"polite\">`);\n parts.push(`<div><strong>${escapeHtml(b.title)}</strong></div>`);\n if (b.action) parts.push(`<div>${escapeHtml(b.action)}</div>`);\n parts.push(\"</div>\");\n parts.push(snapshot ? versionStrip(snapshot) : skeletonStrip(view.banner.kind === \"loading\"));\n const showActions = input.actions !== \"none\";\n const actions = [];\n if (showActions) {\n const busyAct = input.busyAct;\n const checkBusy = busyAct === \"check\";\n const installBusy = busyAct === \"install\";\n actions.push('<div class=\"dsh-upd-actions\">');\n actions.push(\n (checkBusy ? `<button type=\"button\" data-action=\"check\" disabled aria-busy=\"true\" title=\"\\u6B63\\u5728\\u5411\\u5B98\\u65B9\\u6E90\\u67E5\\u8BE2\\uFF0C\\u8BF7\\u7A0D\\u5019\">\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026</button>` : `<button type=\"button\" data-action=\"check\" title=\"\\u91CD\\u65B0\\u5411\\u5B98\\u65B9\\u6E90\\u67E5\\u4E00\\u6B21\\u65B0\\u7248\\uFF08\\u53EA\\u8BFB\\uFF0C\\u4E0D\\u5B89\\u88C5\\uFF09\">\\u67E5\\u65B0\\u7248</button>`) + (installBusy ? `<button type=\"button\" data-action=\"install\" data-primary=\"1\" disabled aria-busy=\"true\" title=\"\\u6B63\\u5728\\u5B89\\u88C5\\uFF0C\\u8BF7\\u7A0D\\u5019\">\\u6B63\\u5728\\u5B89\\u88C5\\u2026</button>` : `<button type=\"button\" data-action=\"install\" data-primary=\"1\" title=\"\\u7528\\u7CBE\\u786E\\u7248\\u672C\\u5B89\\u88C5\\uFF1B\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u540C\\u65F6\\u53EA\\u88C5\\u4E00\\u4E2A\"${view.installEnabled ? \"\" : \" disabled\"}>${escapeHtml(view.installLabel)}</button>`)\n );\n if (snapshot?.latestVersion && !view.skippedLatest && view.banner.kind === \"update\") {\n actions.push(`<button type=\"button\" data-action=\"skip\" title=\"\\u8BE5\\u7248\\u672C\\u4E0D\\u518D\\u63D0\\u9192\\uFF1B\\u6709\\u66F4\\u65B0\\u7684\\u65B0\\u7248\\u672C\\u7167\\u5E38\\u63D0\\u9192\">\\u8DF3\\u8FC7\\u8BE5\\u7248\\u672C</button>`);\n }\n if (view.showReset && snapshot?.latestVersion) {\n actions.push(`<button type=\"button\" data-action=\"reset-skip\" title=\"\\u64A4\\u9500\\u8DF3\\u8FC7\\uFF0C\\u8BE5\\u7248\\u672C\\u91CD\\u65B0\\u63D0\\u9192\">\\u6062\\u590D\\uFF08${escapeHtml(snapshot.latestVersion)}\\uFF09</button>`);\n }\n if (view.showManual && manual) {\n actions.push(`<button type=\"button\" data-action=\"copy-manual\" title=\"\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\">\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4</button>`);\n }\n if (b.kind === \"restart\") {\n actions.push(`<button type=\"button\" data-action=\"restart-hint\" data-primary=\"1\" title=\"\\u5BBF\\u4E3B\\u6CA1\\u6709\\u81EA\\u91CD\\u542F\\u7535\\u8BDD\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\">\\u91CD\\u542F\\u5BBF\\u4E3B</button>`);\n }\n if (b.kind === \"failed\") {\n actions.push(`<button type=\"button\" data-action=\"dismiss-failure\" title=\"\\u786E\\u8BA4\\u5DF2\\u77E5\\u6653\\u8BE5\\u5931\\u8D25\\uFF1A\\u56DE\\u5230\\u53EF\\u88C5\\u9875\\uFF0C\\u4E0B\\u6B21\\u67E5/\\u88C5\\u5C06\\u91CD\\u65B0\\u8BC4\\u4F30\">\\u77E5\\u9053\\u4E86</button>`);\n }\n if (snapshot) {\n actions.push(`<button type=\"button\" data-action=\"copy-diag\" title=\"\\u590D\\u5236\\u5DF2\\u8131\\u654F\\u8BCA\\u65AD\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\">\\u590D\\u5236\\u8BCA\\u65AD</button>`);\n }\n actions.push(\"</div>\");\n }\n actions.push(progressBar(snapshot));\n if (view.skippedLatest && snapshot?.latestVersion) {\n actions.push(\n `<div class=\"dsh-upd-skipline\"><span class=\"dsh-upd-tag\">\\u5DF2\\u8DF3\\u8FC7 ${escapeHtml(snapshot.latestVersion)}</span>\\u70B9\\u300C\\u6062\\u590D\\u300D\\u53EF\\u64A4\\u9500\\uFF0C\\u4E4B\\u540E\\u8FD9\\u4E00\\u7248\\u8FD8\\u4F1A\\u518D\\u63D0\\u9192\\u3002</div>`\n );\n }\n const chapters = [];\n chapters.push(chapterOf(1, actions.join(\"\")));\n {\n let inner = \"\";\n let hasLog = false;\n if (snapshot && snapshot.latestVersion) {\n try {\n const mdText = typeof changelogMarkdown === \"string\" ? changelogMarkdown : \"\";\n const entries = parseChangelog(mdText);\n const ranged = changelogForUpdate(\n entries,\n snapshot.runningVersion,\n snapshot.latestVersion,\n snapshot.installedVersion\n );\n const changelogHTML = renderChangelogHTML(ranged);\n const fromText = String(snapshot.runningVersion ?? \"\");\n const toText = String(snapshot.latestVersion ?? \"\");\n const rangeTitle = fromText && toText ? `\\u66F4\\u65B0\\u8BF4\\u660E\\uFF08${fromText} \\u2192 ${toText}\\uFF09\\uFF1A` : \"\\u66F4\\u65B0\\u8BF4\\u660E\\uFF1A\";\n let yankedBanner = \"\";\n try {\n const toEntry = Array.isArray(ranged) ? ranged.find(function(e) {\n try {\n return e && e.version === toText;\n } catch {\n return false;\n }\n }) : null;\n if (toEntry && toEntry.yanked === true && toText) {\n yankedBanner = yankedBannerHTML(toText);\n }\n } catch {\n yankedBanner = \"\";\n }\n const logOpen = input.changelogCollapsed !== true;\n inner = `<div class=\"dsh-upd-changelog-wrap\"><div>${escapeHtml(rangeTitle)}</div><div class=\"dsh-upd-changelog-foldbox\" data-open=\"${logOpen ? \"1\" : \"0\"}\"><div class=\"dsh-upd-changelog-foldbox-inner\">${yankedBanner}\n${changelogHTML}\n</div></div></div>`;\n hasLog = Array.isArray(ranged) && ranged.length > 0;\n } catch {\n inner = \"\";\n }\n }\n if (!inner) {\n inner = `<div class=\"dsh-upd-changelog-wrap\"><div class=\"dsh-upd-changelog-neutral\">${escapeHtml(\n snapshot && snapshot.latestVersion ? \"\\u65E5\\u5FD7\\u8BFB\\u4E0D\\u51FA\\u6765\\uFF0C\\u5B89\\u88C5\\u4E0D\\u53D7\\u5F71\\u54CD\\u3002\" : \"\\u8FD8\\u6CA1\\u67E5\\u5230\\u65B0\\u7248\\uFF1B\\u67E5\\u5230\\u540E\\u518D\\u663E\\u793A\\u65E5\\u5FD7\\u3002\"\n )}</div></div>`;\n }\n const logCollapsed = input.changelogCollapsed === true;\n const logNote = showActions && hasLog ? `<span class=\"dsh-upd-chap-note\"><button type=\"button\" data-action=\"toggle-changelog\" title=\"\\u5C55\\u5F00\\u6216\\u6536\\u8D77\\u66F4\\u65B0\\u65E5\\u5FD7\">${logCollapsed ? \"\\u5C55\\u5F00\\u66F4\\u65B0\\u65E5\\u5FD7\" : \"\\u6536\\u8D77\\u66F4\\u65B0\\u65E5\\u5FD7\"}</button></span>` : \"\";\n chapters.push(chapterOf(2, inner, logNote));\n }\n {\n const queued = !!queue && (queue.busy || queue.waiting.length > 0);\n if (queued && queue) {\n const busy = queue.busy;\n const ownerRaw = queue.owner;\n const named = ownerRaw && \"pluginId\" in ownerRaw && ownerRaw.pluginId ? String(ownerRaw.pluginId) : null;\n const version = ownerRaw && \"targetVersion\" in ownerRaw && ownerRaw.targetVersion ? `@${String(ownerRaw.targetVersion)}` : \"\";\n const aboutSelf = named !== null && named === pluginId;\n const reveal = showOthers === true;\n const ownerShown = !busy ? \"\\u7A7A\\u95F2\" : named === null ? \"\\u5176\\u4ED6\\u63D2\\u4EF6\" : aboutSelf ? \"\\u672C\\u63D2\\u4EF6\" : reveal ? named : \"\\u5176\\u4ED6\\u63D2\\u4EF6\";\n const ownerVer = busy && named !== null && (aboutSelf || reveal) ? version : \"\";\n const pos = typeof queue.position === \"number\" ? queue.position : null;\n const posText = pos === null ? \"\\u672A\\u6392\\u961F\" : `\\u7B2C ${pos} \\u4F4D`;\n const posNote = pos === null ? \"\" : pos === 1 ? \"\\u4E0B\\u4E00\\u4E2A\\u5C31\\u662F\\u4F60\" : `\\u524D\\u65B9 ${pos - 1} \\u4E2A`;\n const rows = [\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\" data-tone=\"${busy ? \"busy\" : \"idle\"}\"></span><span class=\"dsh-upd-qk\">\\u6B63\\u5728\\u5B89\\u88C5</span><span class=\"dsh-upd-qv\">${escapeHtml(ownerShown + ownerVer)}</span><span class=\"dsh-upd-qn\">${busy ? \"\\u88C5\\u5B8C\\u81EA\\u52A8\\u8F6E\\u5230\\u4F60\" : \"\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u4E00\\u6B21\\u53EA\\u88C5\\u4E00\\u4E2A\"}</span></div>`,\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\" data-tone=\"you\"></span><span class=\"dsh-upd-qk\">\\u4F60\\u7684\\u987A\\u4F4D</span><span class=\"dsh-upd-qv\">${escapeHtml(posText)}</span><span class=\"dsh-upd-qn\">${escapeHtml(posNote)}</span></div>`\n ];\n if (reveal && queue.waiting.length > 0) {\n const seq = queue.waiting.map((e) => `${e.pluginId}${e.targetVersion ? `@${e.targetVersion}` : \"\"}`).join(\" \\u2192 \");\n rows.push(\n `<div class=\"dsh-upd-qrow\"><span class=\"dsh-upd-qdot\"></span><span class=\"dsh-upd-qk\">\\u6392\\u961F\\u987A\\u5E8F</span><span class=\"dsh-upd-qseq\">${escapeHtml(seq)}</span></div>`\n );\n }\n const note = showActions ? `<span class=\"dsh-upd-chap-note\"><button type=\"button\" data-action=\"toggle-queue\">${reveal ? \"\\u9690\\u85CF\\u4ED6\\u4EBA\\u660E\\u7EC6\" : \"\\u663E\\u793A\\u5176\\u4ED6\\u63D2\\u4EF6\"}</button></span>` : \"\";\n chapters.push(chapterOf(3, `<div class=\"dsh-upd-queue\">${rows.join(\"\")}</div>`, note));\n } else {\n chapters.push(\n chapterOf(\n 3,\n `<div class=\"dsh-upd-queue\"><div class=\"dsh-upd-changelog-neutral\">${escapeHtml(\n view.queueNote ?? \"\\u5F53\\u524D\\u6CA1\\u6709\\u6392\\u961F\\u4EFB\\u52A1\\uFF0C\\u540C\\u4E00\\u4F7F\\u7528\\u8303\\u56F4\\u4E00\\u6B21\\u53EA\\u88C5\\u4E00\\u4E2A\\u3002\"\n )}</div></div>`\n )\n );\n }\n }\n {\n const errLines = [];\n const failedNow = b.kind === \"failed\" || b.kind === \"blocked\";\n const failRef = input.failure;\n if (failedNow) {\n const shownCode = b.kind === \"blocked\" ? snapshot?.blockedReason ?? b.kind : typeof input.lastError === \"string\" && input.lastError.trim() ? input.lastError : \"install-failed\";\n errLines.push(\n `<div class=\"dsh-upd-err\">\\u7A33\\u5B9A\\u7801 <code>${escapeHtml(String(shownCode))}</code>\\uFF1A\\u4E0A\\u4E00\\u6761\\u4E2D\\u6587\\u8BF4\\u660E\\u5C31\\u662F\\u8981\\u7528\\u6237\\u505A\\u7684\\u4E8B\\uFF1B\\u8981\\u5F80\\u4E0A\\u6E38\\u62A5\\uFF0C\\u7528\\u300C\\u590D\\u5236\\u8BCA\\u65AD\\u300D\\u6574\\u6BB5\\u7C98\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002</div>`\n );\n if (b.kind === \"failed\" && failRef) {\n const keys = [];\n if (typeof failRef.requestId === \"string\" && failRef.requestId) keys.push(`\\u8BF7\\u6C42 <code>${escapeHtml(failRef.requestId)}</code>`);\n if (typeof failRef.checkId === \"string\" && failRef.checkId) keys.push(`\\u68C0\\u67E5 <code>${escapeHtml(failRef.checkId)}</code>`);\n const at = formatLatchTime(failRef.atMs);\n if (at) keys.push(`\\u5931\\u8D25\\u4E8E ${escapeHtml(at)}`);\n if (keys.length > 0) {\n errLines.push(`<div class=\"dsh-upd-err\">\\u672C\\u6B21\\u67E5\\u8BE2\\u952E\\uFF1A${keys.join(\" \\xB7 \")}\\uFF08\\u62FF\\u7740\\u5B83\\u4EEC\\u53BB\\u65E5\\u5FD7\\u91CC\\u5BF9\\uFF09\\u3002</div>`);\n }\n if (!failRef.volatile) {\n errLines.push(\n `<div class=\"dsh-upd-err\">\\u8BC1\\u636E\\u5DF2\\u51BB\\u7ED3\\uFF1A\\u590D\\u5236\\u8BCA\\u65AD\\u91CC\\u7684\\u7801\\u3001\\u7248\\u672C\\u3001\\u7F16\\u53F7\\u90FD\\u53D6\\u81EA\\u5931\\u8D25\\u65F6\\u523B\\uFF0C\\u4E0D\\u968F\\u8F6E\\u8BE2\\u5237\\u65B0\\uFF1B\\u4E0B\\u4E00\\u6B21\\u67E5\\u65B0\\u7248\\u6216\\u5B89\\u88C5\\u4F1A\\u66F4\\u65B0\\u5B83\\u3002</div>`\n );\n } else {\n errLines.push(\n `<div class=\"dsh-upd-err\">\\u8BFB\\u6570\\u77AC\\u6001\\u5931\\u8D25\\uFF1A\\u4E0B\\u4E00\\u6B21\\u6210\\u529F\\u8BFB\\u6570\\u4F1A\\u81EA\\u52A8\\u89E3\\u9664\\uFF1B\\u4E00\\u76F4\\u51FA\\u73B0\\u518D\\u6309\\u7A33\\u5B9A\\u7801\\u6392\\u67E5\\u3002</div>`\n );\n }\n }\n } else {\n errLines.push(`<div class=\"dsh-upd-changelog-neutral\">\\u6682\\u65E0\\u5931\\u8D25\\uFF1A\\u6B64\\u65F6\\u590D\\u5236\\u8BCA\\u65AD\\u7ED9\\u51FA\\u7684\\u662F\\u5F53\\u524D\\u72B6\\u6001\\u5FEB\\u7167\\u3002</div>`);\n }\n if (input.showLogHint !== false) {\n errLines.push(\n `<div class=\"dsh-upd-log\">\\u6DF1\\u6316\\u770B\\u65E5\\u5FD7\\uFF1A\\u6309\\u63D2\\u4EF6\\u6807\\u8BC6 <code>${escapeHtml(pluginId)}</code> \\u8FC7\\u6EE4 <code>${LOG_EVENT_CALL}</code>\\u3001<code>${LOG_EVENT_CALL_FAIL}</code>\\u3001<code>${LOG_EVENT_INSTALL_EXEC}</code> \\u4E09\\u4E2A\\u4E8B\\u4EF6\\u3002</div>`\n );\n if (b.kind === \"failed\" && failRef && (failRef.requestId || failRef.checkId)) {\n errLines.push(\n `<div class=\"dsh-upd-log\">\\u51ED\\u4E0A\\u9762\\u7684\\u8BF7\\u6C42\\uFF0F\\u68C0\\u67E5\\u7F16\\u53F7\\u5728 <code>${LOG_EVENT_CALL_FAIL}</code> \\u91CC\\u5BF9\\u4E0A\\uFF1B\\u57FA\\u7EBF\\u8017\\u65F6\\u770B <code>${LOG_EVENT_CALL}</code>\\uFF0C\\u6267\\u884C\\u7ED3\\u679C\\u770B <code>${LOG_EVENT_INSTALL_EXEC}</code>\\u3002</div>`\n );\n }\n }\n if (copyNotice) errLines.push(`<div class=\"dsh-upd-copy\" role=\"status\">${escapeHtml(copyNotice)}</div>`);\n chapters.push(chapterOf(4, errLines.join(\"\")));\n }\n chapters.push(\n chapterOf(\n 5,\n view.showManual && manual ? `<div class=\"dsh-upd-manual\"><div>\\u624B\\u5DE5\\u515C\\u5E95\\u547D\\u4EE4\\uFF08\\u590D\\u5236\\u6574\\u884C\\u6267\\u884C\\uFF09\\uFF1A</div><code>${escapeHtml(manual)}</code></div>` : `<div class=\"dsh-upd-manual\"><div class=\"dsh-upd-changelog-neutral\">\\u5F53\\u524D\\u6CA1\\u6709\\u53EF\\u7528\\u7684\\u624B\\u5DE5\\u547D\\u4EE4\\uFF08\\u8BA4\\u4E0D\\u51FA\\u4F7F\\u7528\\u8303\\u56F4\\u6216\\u5C5E\\u6E90\\u7801\\u5B89\\u88C5\\u65F6\\u4E0D\\u7ED9\\uFF09\\u3002</div></div>`\n )\n );\n parts.push('<div class=\"dsh-upd-body\">' + chapters.join(\"\\n\") + \"</div>\");\n if (showActions && mode === \"dialog\") {\n parts.push(\n `<div class=\"dsh-upd-footer\"><span class=\"dsh-upd-foot-note\">\\u5173\\u95ED\\u4E0D\\u5F71\\u54CD\\u66F4\\u65B0\\uFF0C\\u53EF\\u968F\\u65F6\\u56DE\\u6765\\u67E5\\u770B</span><button type=\"button\" data-action=\"close-view\" title=\"\\u5173\\u95ED\\u7A97\\u53E3\\uFF0C\\u66F4\\u65B0\\u4E0D\\u53D7\\u5F71\\u54CD\">\\u5173\\u95ED</button></div>`\n );\n }\n return parts.join(\"\\n\");\n}\nfunction renderUpdatePanelHTML(input) {\n const view = panelViewModel(input);\n const kernel = renderUpdatePanelKernel(input, view);\n const d5 = normalizePanelTheme(input.theme) === \"archive\";\n const attr = d5 ? ' data-theme=\"archive\"' : \"\";\n const sealAttr = ` data-seal=\"${escapeHtml(view.seal.text)}\" data-seal-tone=\"${view.seal.tone}\"`;\n const body = input.mode === \"dialog\" ? `<div class=\"dsh-upd-overlay\" data-mode=\"dialog\"><div class=\"dsh-upd\" data-mode=\"dialog\" data-plugin=\"${escapeHtml(input.pluginId)}\"${sealAttr}${attr}>\n${kernel}\n</div></div>` : `<div class=\"dsh-upd\" data-mode=\"embedded\" data-plugin=\"${escapeHtml(input.pluginId)}\"${sealAttr}${attr}>\n${kernel}\n</div>`;\n const css = d5 ? `${UPDATE_PANEL_CSS}\n${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS;\n return `<style>${css}</style>\n${body}`;\n}\nconst PANEL_FAILURE_LATCHES = /* @__PURE__ */ new Map();\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction asSnapshot(value) {\n if (!isObject(value)) return null;\n const s = value;\n if (typeof s[\"runningVersion\"] !== \"string\") return null;\n if (typeof s[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction asQueue(value) {\n if (!isObject(value) || typeof value[\"busy\"] !== \"boolean\") return null;\n return value;\n}\nfunction randomRequestId() {\n try {\n const rand = Math.floor(Math.random() * 16777215).toString(36);\n return `r${Date.now().toString(36)}${rand}`.slice(0, 64);\n } catch {\n return `r${Date.now()}`;\n }\n}\nfunction getTimer() {\n const g = globalThis;\n const set = g[\"setInterval\"];\n const clear = g[\"clearInterval\"];\n if (typeof set === \"function\" && typeof clear === \"function\") {\n return {\n set: (fn, ms) => set(fn, ms),\n clear: (h) => clear(h)\n };\n }\n const setT = g[\"setTimeout\"];\n const clearT = g[\"clearTimeout\"];\n return {\n set: (fn, ms) => setT(fn, ms),\n clear: (h) => clearT(h)\n };\n}\nasync function defaultCopyText(text) {\n try {\n const holder = globalThis[\"navigator\"];\n const clip = holder?.clipboard;\n const write = clip?.writeText;\n if (clip && typeof write === \"function\") {\n await write.call(clip, text);\n return;\n }\n } catch {\n }\n}\nfunction pendingAutoCheck(snapshot, busyAct) {\n if (busyAct === \"check\" || busyAct === \"install\") return false;\n if (!snapshot) return false;\n const st = snapshot?.job?.state;\n if (st === \"installing\" || st === \"verifying\") return false;\n return true;\n}\nfunction mountUpdatePanel(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = options.pluginId;\n if (typeof pluginId !== \"string\" || !pluginId) {\n throw new Error(`[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 ${JSON.stringify(pluginId)}\\uFF09`);\n }\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u9762\\u677F\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u9762\\u677F\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const prefix = options.prefix === void 0 ? \"wf\" : options.prefix;\n const phoneNames = buildPhoneNames(prefix);\n const changelogPhone = buildChangelogPhoneName(prefix);\n const autoChangelogEnabled = typeof options.changelogMarkdown !== \"string\" && options.autoChangelog !== false;\n let manualChangelogOverride = typeof options.changelogMarkdown === \"string\";\n const changelogCache = /* @__PURE__ */ new Map();\n const changelogInflight = /* @__PURE__ */ new Set();\n const changelogFailedAt = /* @__PURE__ */ new Map();\n let autoSeq = 0;\n const pollMs = options.pollMs === void 0 ? DEFAULT_PANEL_POLL_MS : options.pollMs;\n if (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS) {\n throw new Error(`[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 ${JSON.stringify(options.pollMs)}\\uFF09`);\n }\n let mode = options.mode ?? \"embedded\";\n if (mode !== \"embedded\" && mode !== \"dialog\") {\n throw new Error(`[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 ${JSON.stringify(options.mode)}\\uFF09`);\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(options.theme)}\\uFF09`);\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n let showOthers = options.showOthers === true;\n let changelogCollapsed = false;\n const call = options.call;\n const onRestartRequested = options.onRestartRequested;\n const onCloseRequested = typeof options.onCloseRequested === \"function\" ? options.onCloseRequested : null;\n let lastHTML = \"\";\n const copyText = options.copyText ?? defaultCopyText;\n const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);\n const hostKind = typeof options.hostKind === \"string\" && options.hostKind ? options.hostKind : null;\n const profileNameOption = typeof options.profileName === \"string\" && options.profileName ? options.profileName : null;\n const showLogHintOption = options.showLogHint !== false;\n const diagCopyFormat = options.diagCopyFormat === \"line\" ? \"line\" : \"block\";\n let changelogMarkdown = typeof options.changelogMarkdown === \"string\" ? options.changelogMarkdown : null;\n let snapshot = null;\n let manual = null;\n let queue = null;\n let receipt = null;\n let requestId = null;\n const latchKey = `${pluginId}${profileNameOption ?? \"\"}`;\n let latch = null;\n try {\n latch = PANEL_FAILURE_LATCHES.get(latchKey) ?? null;\n if (latch?.volatile) latch = null;\n } catch {\n latch = null;\n }\n function saveLatch() {\n try {\n if (latch && !latch.volatile) {\n if (!PANEL_FAILURE_LATCHES.has(latchKey) && PANEL_FAILURE_LATCHES.size >= 100) {\n const oldest = PANEL_FAILURE_LATCHES.keys().next();\n if (!oldest.done) PANEL_FAILURE_LATCHES.delete(oldest.value);\n }\n PANEL_FAILURE_LATCHES.set(latchKey, latch);\n } else PANEL_FAILURE_LATCHES.delete(latchKey);\n } catch {\n }\n }\n function latchNow() {\n try {\n const n = Date.now();\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n }\n function setLatch(next) {\n latch = next;\n saveLatch();\n }\n function clearLatch() {\n if (!latch) return;\n latch = null;\n saveLatch();\n }\n function currentEnvPair() {\n return { hostKind: hostKind ?? envHostKind, profileName: profileNameOption ?? envProfileName };\n }\n function latchFromReply(reply, source) {\n const env = currentEnvPair();\n return {\n code: failureCodeOf(\n { error: reply[\"error\"], errorKind: reply[\"errorKind\"] },\n source === \"install\" ? \"install-failed\" : \"check-failed\"\n ),\n kind: typeof reply[\"errorKind\"] === \"string\" && reply[\"errorKind\"].trim() ? reply[\"errorKind\"].trim() : null,\n detail: null,\n diag: Object.prototype.hasOwnProperty.call(reply, \"diag\") ? reply[\"diag\"] : null,\n requestId: source === \"install\" ? requestId : null,\n checkId: source === \"install\" ? receipt?.checkId ?? null : null,\n runningVersion: snapshot?.runningVersion ?? null,\n installedVersion: snapshot?.installedVersion ?? null,\n latestVersion: snapshot?.latestVersion ?? null,\n hostKind: env.hostKind,\n profileName: env.profileName,\n source,\n volatile: false,\n jobId: null,\n atMs: latchNow()\n };\n }\n function latchFromJob(job, snap) {\n const env = currentEnvPair();\n const message = typeof job.message === \"string\" ? job.message : null;\n const detail = message && message.includes(\":\") ? message.slice(message.indexOf(\":\") + 1).trim() || null : message;\n return {\n code: messageCodeOf(message) || \"install-failed\",\n kind: null,\n detail,\n // 快照里没有 diag:保留锁存里既有的(多半是致命安装回包自带的),没有即缺省说人话\n diag: latch?.diag ?? null,\n requestId: job.requestId ?? latch?.requestId ?? requestId,\n checkId: receipt?.checkId ?? latch?.checkId ?? null,\n runningVersion: snap?.runningVersion ?? snapshot?.runningVersion ?? null,\n installedVersion: snap?.installedVersion ?? snapshot?.installedVersion ?? null,\n latestVersion: snap?.latestVersion ?? snapshot?.latestVersion ?? null,\n hostKind: env.hostKind ?? latch?.hostKind ?? null,\n profileName: env.profileName ?? latch?.profileName ?? null,\n source: \"install\",\n volatile: false,\n jobId: typeof job.id === \"string\" ? job.id : null,\n atMs: latchNow()\n };\n }\n function thrownDetail(err) {\n try {\n const raw = err instanceof Error ? err.message : typeof err === \"string\" ? err : null;\n if (typeof raw !== \"string\") return null;\n const t = raw.trim();\n if (!t) return null;\n if (/^[a-z][a-z-]*$/.test(t) && t.length <= 32) return null;\n return redactForCopy(t) || null;\n } catch {\n return null;\n }\n }\n function userThrowLatch(source, err) {\n const env = currentEnvPair();\n return {\n code: source === \"install\" ? \"install-failed\" : \"check-failed\",\n kind: null,\n detail: thrownDetail(err),\n diag: null,\n requestId: source === \"install\" ? requestId : null,\n checkId: source === \"install\" ? receipt?.checkId ?? null : null,\n runningVersion: snapshot?.runningVersion ?? null,\n installedVersion: snapshot?.installedVersion ?? null,\n latestVersion: snapshot?.latestVersion ?? null,\n hostKind: env.hostKind,\n profileName: env.profileName,\n source,\n volatile: false,\n jobId: null,\n atMs: latchNow()\n };\n }\n function volatileLatch(source) {\n const env = currentEnvPair();\n return {\n code: \"check-failed\",\n kind: null,\n detail: null,\n diag: null,\n requestId,\n checkId: receipt?.checkId ?? null,\n runningVersion: snapshot?.runningVersion ?? null,\n installedVersion: snapshot?.installedVersion ?? null,\n latestVersion: snapshot?.latestVersion ?? null,\n hostKind: env.hostKind,\n profileName: env.profileName,\n source,\n volatile: true,\n jobId: null,\n atMs: latchNow()\n };\n }\n function backfillLatch(s) {\n if (!latch) return;\n let touched = false;\n if (latch.runningVersion === null && s.runningVersion) {\n latch.runningVersion = s.runningVersion;\n touched = true;\n }\n if (latch.installedVersion === null && s.installedVersion) {\n latch.installedVersion = s.installedVersion;\n touched = true;\n }\n if (latch.latestVersion === null && s.latestVersion) {\n latch.latestVersion = s.latestVersion;\n touched = true;\n }\n if (touched) saveLatch();\n }\n function tripleChanged(s) {\n if (!latch) return false;\n backfillLatch(s);\n const pairs = [\n [latch.runningVersion, s.runningVersion ?? null],\n [latch.installedVersion, s.installedVersion ?? null],\n [latch.latestVersion, s.latestVersion ?? null]\n ];\n return pairs.some(([a, b]) => a !== null && b !== null && a !== b);\n }\n let copyNotice = null;\n let noticeExpiresAt = 0;\n let busyAct = null;\n let mounted = true;\n let envProfileName = null;\n let envHostKind = null;\n function sayCopy(text) {\n copyNotice = text;\n try {\n noticeExpiresAt = Date.now() + 5e3;\n } catch {\n noticeExpiresAt = 0;\n }\n }\n function queueArgs() {\n return { includeQueue: true, includeEnv: true, showOthers, ...requestId ? { requestId } : {} };\n }\n function render() {\n if (!mounted) return;\n if (!copyNotice) noticeExpiresAt = 0;\n else if (noticeExpiresAt && Date.now() > noticeExpiresAt) {\n copyNotice = null;\n noticeExpiresAt = 0;\n }\n const latest = snapshot?.latestVersion ?? null;\n const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);\n const nextHTML = renderUpdatePanelHTML({\n snapshot,\n manual,\n queue,\n busyAct,\n skippedLatest,\n lastError: latch?.code ?? null,\n errorKind: latch?.kind ?? null,\n failure: latch ? { requestId: latch.requestId, checkId: latch.checkId, atMs: latch.atMs, source: latch.source, volatile: latch.volatile } : null,\n showLogHint: showLogHintOption,\n changelogMarkdown,\n mode,\n showOthers,\n changelogCollapsed,\n pluginId,\n copyNotice,\n theme,\n // 使用范围与宿主种类:调用方显式传的优先,否则用宿主回的真值。\n profileName: profileNameOption ?? envProfileName,\n hostKind: hostKind ?? envHostKind\n });\n if (nextHTML !== lastHTML) {\n lastHTML = nextHTML;\n container.innerHTML = nextHTML;\n }\n }\n function applyStatusReply(reply, via) {\n if (!isObject(reply)) return;\n if (reply[\"ok\"] === true) {\n const s = asSnapshot(reply[\"snapshot\"]);\n if (s) snapshot = s;\n manual = typeof reply[\"manual\"] === \"string\" ? reply[\"manual\"] : null;\n if (reply[\"receipt\"] && isObject(reply[\"receipt\"]) && typeof reply[\"receipt\"][\"checkId\"] === \"string\") {\n receipt = { checkId: reply[\"receipt\"][\"checkId\"] };\n }\n const q = asQueue(reply[\"queue\"]);\n if (q) queue = q;\n const env = reply[\"env\"];\n if (isObject(env)) {\n const pn = env[\"profileName\"];\n const hk = env[\"environmentKind\"];\n if (typeof pn === \"string\" && pn.trim()) envProfileName = pn.trim();\n if (typeof hk === \"string\" && hk.trim()) envHostKind = hk.trim();\n }\n const jobState = snapshot?.job?.state ?? null;\n if (jobState === \"installing\" || jobState === \"verifying\") {\n clearLatch();\n } else if (jobState === \"completed\" || jobState === \"restart-required\") {\n clearLatch();\n } else if ((jobState === \"failed\" || jobState === \"interrupted\") && snapshot?.job) {\n setLatch(latchFromJob(snapshot.job, snapshot));\n } else if (via === \"install\") {\n clearLatch();\n } else if (snapshot && tripleChanged(snapshot)) {\n clearLatch();\n } else if (via === \"check\" && latch?.source === \"check\") {\n clearLatch();\n } else if (latch?.volatile) {\n clearLatch();\n }\n } else {\n const errText = typeof reply[\"error\"] === \"string\" ? reply[\"error\"].trim() : \"\";\n const kindText = typeof reply[\"errorKind\"] === \"string\" ? reply[\"errorKind\"].trim() : \"\";\n if ((kindText || errText) === \"update-busy\") {\n } else {\n setLatch(latchFromReply(reply, via === \"install\" ? \"install\" : \"check\"));\n }\n try {\n const env = reply[\"env\"];\n if (isObject(env)) {\n const pn = env[\"profileName\"];\n const hk = env[\"environmentKind\"];\n if (typeof pn === \"string\" && pn.trim()) envProfileName = pn.trim();\n if (typeof hk === \"string\" && hk.trim()) envHostKind = hk.trim();\n }\n } catch {\n }\n try {\n const q = asQueue(reply[\"queue\"]);\n if (q) queue = q;\n } catch {\n }\n }\n }\n function pendingAutoChangelogVersion() {\n if (!autoChangelogEnabled || manualChangelogOverride || !mounted) return null;\n const v = snapshot?.latestVersion ?? null;\n if (typeof v !== \"string\" || !validReleaseVersion(v)) return null;\n if (!snapshot || snapshot.canInstall !== true) return null;\n try {\n if (skipStore.has(v)) return null;\n } catch {\n }\n if (changelogInflight.has(v)) return null;\n const hasCache = changelogCache.has(v);\n const failedAt = changelogFailedAt.has(v) ? changelogFailedAt.get(v) : null;\n let nowMs = 0;\n try {\n nowMs = Date.now();\n } catch {\n nowMs = 0;\n }\n if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return null;\n return v;\n }\n function maybeAutoChangelog() {\n const v = pendingAutoChangelogVersion();\n if (v === null) return;\n changelogInflight.add(v);\n const seq = autoSeq;\n void Promise.resolve().then(() => call(changelogPhone, { version: v })).then(\n (reply) => {\n changelogInflight.delete(v);\n if (!mounted || seq !== autoSeq) return;\n if (!reply || typeof reply !== \"object\" || reply[\"ok\"] !== true) {\n try {\n changelogFailedAt.set(v, Date.now());\n } catch {\n try {\n changelogFailedAt.set(v, 0);\n } catch {\n }\n }\n return;\n }\n const md = reply[\"markdown\"];\n changelogCache.set(v, typeof md === \"string\" ? md : null);\n if (typeof md === \"string\") {\n changelogMarkdown = md;\n render();\n }\n },\n () => {\n changelogInflight.delete(v);\n if (mounted && seq === autoSeq) {\n try {\n changelogFailedAt.set(v, Date.now());\n } catch {\n try {\n changelogFailedAt.set(v, 0);\n } catch {\n }\n }\n }\n }\n );\n }\n async function refresh() {\n if (!mounted) return;\n try {\n const reply = await call(phoneNames.updateStatus, queueArgs());\n if (!mounted) return;\n applyStatusReply(reply, \"refresh\");\n } catch {\n if (!mounted) return;\n setLatch(volatileLatch(\"check\"));\n }\n render();\n maybeAutoChangelog();\n }\n async function act(kind, arg) {\n if (!mounted) return;\n copyNotice = null;\n switch (kind) {\n case \"check\": {\n if (busyAct) return;\n busyAct = \"check\";\n copyNotice = \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\";\n render();\n try {\n const reply = await call(phoneNames.updateCheck, queueArgs());\n if (!mounted) return;\n applyStatusReply(reply, \"check\");\n changelogFailedAt.clear();\n } catch (err) {\n if (!mounted) return;\n setLatch(userThrowLatch(\"check\", err));\n } finally {\n busyAct = null;\n if (copyNotice === \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\") copyNotice = null;\n }\n render();\n maybeAutoChangelog();\n return;\n }\n case \"install\": {\n if (busyAct) return;\n busyAct = \"install\";\n copyNotice = \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\";\n render();\n try {\n if (!receipt) {\n try {\n const checked = await call(phoneNames.updateCheck, queueArgs());\n if (!mounted) return;\n applyStatusReply(checked, \"check\");\n } catch (err) {\n if (!mounted) return;\n setLatch(userThrowLatch(\"check\", err));\n render();\n return;\n }\n }\n if (!mounted) return;\n if (!receipt) {\n const expiredEnv = currentEnvPair();\n setLatch({\n code: \"check-expired\",\n kind: \"check-expired\",\n detail: null,\n diag: null,\n requestId,\n checkId: null,\n runningVersion: snapshot?.runningVersion ?? null,\n installedVersion: snapshot?.installedVersion ?? null,\n latestVersion: snapshot?.latestVersion ?? null,\n hostKind: expiredEnv.hostKind,\n profileName: expiredEnv.profileName,\n source: \"install\",\n volatile: false,\n jobId: null,\n atMs: latchNow()\n });\n render();\n return;\n }\n requestId = randomRequestId();\n const reply = await call(phoneNames.updateInstall, {\n checkId: receipt.checkId,\n requestId,\n ...queueArgs()\n });\n if (!mounted) return;\n applyStatusReply(reply, \"install\");\n } catch (err) {\n if (!mounted) return;\n setLatch(userThrowLatch(\"install\", err));\n } finally {\n busyAct = null;\n if (copyNotice === \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\") copyNotice = null;\n }\n render();\n maybeAutoChangelog();\n return;\n }\n case \"skip\": {\n const v = snapshot?.latestVersion ?? null;\n if (v && validReleaseVersion(v)) {\n try {\n skipStore.skip(v);\n } catch {\n }\n }\n render();\n return;\n }\n case \"reset-skip\": {\n try {\n skipStore.reset(arg ?? snapshot?.latestVersion ?? void 0);\n } catch {\n }\n await refresh();\n return;\n }\n case \"copy-manual\": {\n if (manual) {\n try {\n await copyText(manual);\n sayCopy(\"\\u624B\\u5DE5\\u547D\\u4EE4\\u5DF2\\u590D\\u5236\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\\u5373\\u53EF\\u3002\");\n } catch {\n sayCopy(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u547D\\u4EE4\\u3002\");\n }\n }\n render();\n return;\n }\n case \"copy-diag\": {\n const jobCode = snapshot?.job?.state === \"failed\" ? messageCodeOf(snapshot.job.message) || \"install-failed\" : \"\";\n if (!latch && !jobCode && !snapshot?.blockedReason && snapshot) {\n const stateLine = snapshot.canInstall && snapshot.latestVersion ? `\\u6709\\u65B0\\u7248 ${snapshot.latestVersion} \\u53EF\\u88C5\\uFF08\\u5F53\\u524D ${snapshot.runningVersion}\\uFF09` : \"\\u5DF2\\u662F\\u6700\\u65B0\\uFF0C\\u65E0\\u9700\\u66F4\\u65B0\\u3002\";\n const queueName = queueTextOf(queue?.position ?? null);\n const envBits = [\n `\\u63D2\\u4EF6=${pluginId}`,\n `\\u5BBF\\u4E3B=${hostKind ?? envHostKind ?? \"\\u672A\\u77E5\"}`,\n `\\u4F7F\\u7528\\u8303\\u56F4=${profileNameOption ?? envProfileName ?? \"\\u672A\\u77E5\"}`,\n `\\u961F\\u5217=${queueName}`\n ];\n if (requestId) envBits.push(`\\u8BF7\\u6C42=${requestId}`);\n if (receipt?.checkId) envBits.push(`\\u68C0\\u67E5=${receipt.checkId}`);\n const segs = [\n `[update-diag] \\u5F53\\u524D\\u65E0\\u5931\\u8D25\\uFF1A${stateLine}`,\n `\\u7248\\u672C\\uFF1A\\u8FD0\\u884C ${snapshot.runningVersion}\\uFF0F\\u78C1\\u76D8 ${snapshot.installedVersion ?? \"\\u672A\\u77E5\"}\\uFF0F\\u8FDC\\u7AEF ${snapshot.latestVersion ?? \"\\u672A\\u67E5\\u8FC7\"}`,\n `\\u6765\\u6E90\\uFF1A${envBits.join(\" \\xB7 \")}`\n ].map((s) => redactForCopy(s));\n const text2 = diagCopyFormat === \"line\" ? segs.join(\" \\xB7 \") : segs.join(\"\\n\");\n try {\n await copyText(text2);\n sayCopy(\"\\u5DF2\\u590D\\u5236\\u5F53\\u524D\\u72B6\\u6001\\uFF08\\u65E0\\u5931\\u8D25\\uFF09\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u5373\\u53EF\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002\");\n } catch {\n sayCopy(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u4FE1\\u606F\\u3002\");\n }\n render();\n return;\n }\n const frozen = latch;\n const code = frozen?.code ?? failureCodeOf({ error: null, errorKind: null }, jobCode || snapshot?.blockedReason || \"check-failed\");\n const detail = frozen?.detail ?? (snapshot?.job?.message && snapshot.job.message.includes(\":\") ? snapshot.job.message.slice(snapshot.job.message.indexOf(\":\") + 1) : snapshot?.job?.message);\n const text = buildUpdateDiagCopy({\n pluginId,\n code,\n detail,\n runningVersion: frozen?.runningVersion ?? snapshot?.runningVersion ?? null,\n installedVersion: frozen?.installedVersion ?? snapshot?.installedVersion ?? null,\n latestVersion: frozen?.latestVersion ?? snapshot?.latestVersion ?? null,\n hostKind: frozen?.hostKind ?? hostKind ?? envHostKind,\n profileName: frozen?.profileName ?? profileNameOption ?? envProfileName,\n queuePosition: queue?.position ?? null,\n requestId: frozen?.requestId ?? requestId,\n checkId: frozen?.checkId ?? receipt?.checkId ?? null,\n route: null,\n diag: frozen?.diag ?? null,\n manual,\n format: diagCopyFormat\n });\n try {\n await copyText(text);\n sayCopy(\"\\u8BCA\\u65AD\\u5DF2\\u590D\\u5236\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u5373\\u53EF\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002\");\n } catch {\n sayCopy(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u4FE1\\u606F\\u3002\");\n }\n render();\n return;\n }\n case \"dismiss-failure\": {\n clearLatch();\n sayCopy(\"\\u5DF2\\u786E\\u8BA4\\u8BE5\\u5931\\u8D25\\u63D0\\u793A\\uFF1B\\u4E0B\\u6B21\\u67E5\\u65B0\\u7248\\u6216\\u5B89\\u88C5\\u5C06\\u91CD\\u65B0\\u8BC4\\u4F30\\u3002\");\n render();\n return;\n }\n case \"toggle-queue\": {\n showOthers = !showOthers;\n await refresh();\n return;\n }\n case \"toggle-changelog\": {\n changelogCollapsed = !changelogCollapsed;\n render();\n return;\n }\n // 「重启宿主」:宿主没有重启自己的电话,所以只做入口——\n // 调用方给了 onRestartRequested 就交给它;没给就如实说“请手动重启”,不假装。\n case \"restart-hint\": {\n try {\n if (typeof onRestartRequested === \"function\") {\n await onRestartRequested();\n sayCopy(\"\\u5DF2\\u6309\\u8C03\\u7528\\u65B9\\u7684\\u91CD\\u542F\\u6D41\\u7A0B\\u5904\\u7406\\uFF1B\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n } else {\n sayCopy(\"\\u672C\\u5BBF\\u4E3B\\u672A\\u63D0\\u4F9B\\u91CD\\u542F\\u5165\\u53E3\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n } catch {\n sayCopy(\"\\u91CD\\u542F\\u5165\\u53E3\\u8C03\\u7528\\u5931\\u8D25\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n render();\n return;\n }\n case \"close-view\": {\n await requestDialogClose();\n return;\n }\n }\n }\n async function setMode(next) {\n if (next !== \"embedded\" && next !== \"dialog\") {\n throw new Error(`[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n mode = next;\n render();\n }\n async function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n theme = normalizePanelTheme(next);\n render();\n }\n async function setShowOthers(show) {\n showOthers = show === true;\n await refresh();\n }\n async function setChangelogCollapsed(collapsed) {\n changelogCollapsed = collapsed === true;\n render();\n }\n function onClick(ev) {\n try {\n const t = ev;\n const btn = t?.target && typeof t.target.closest === \"function\" ? t.target.closest(\"[data-action]\") : null;\n const kind = btn?.getAttribute ? btn.getAttribute(\"data-action\") : null;\n if (!kind) return;\n void act(kind);\n } catch {\n }\n }\n async function requestDialogClose() {\n if (!mounted || mode !== \"dialog\") return;\n if (onCloseRequested) {\n try {\n await onCloseRequested();\n } catch {\n }\n }\n unmount();\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || mode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n void requestDialogClose();\n } catch {\n }\n }\n function setChangelogMarkdown(markdown) {\n changelogMarkdown = typeof markdown === \"string\" ? markdown : null;\n if (typeof markdown === \"string\") manualChangelogOverride = true;\n render();\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n autoSeq++;\n changelogInflight.clear();\n try {\n timer.clear(handle);\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n render();\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n const timer = getTimer();\n const handle = timer.set(() => {\n void refresh();\n }, pollMs);\n try {\n const h = handle;\n if (h && typeof h.unref === \"function\") h.unref();\n } catch {\n }\n void (async () => {\n await refresh();\n try {\n if (pendingAutoCheck(snapshot, busyAct)) void act(\"check\");\n } catch {\n }\n })();\n return { refresh, act, setMode, setTheme, setShowOthers, setChangelogCollapsed, setChangelogMarkdown, unmount };\n}\nimport { buildPhoneNames as buildPhoneNames2 } from \"./config.js\";\nimport { buildChangelogPhoneName as buildChangelogPhoneName2 } from \"./config.js\";\nconst PANEL_POLL = {\n defaultMs: DEFAULT_PANEL_POLL_MS,\n minMs: MIN_PANEL_POLL_MS\n};\nimport {\n QUEUE_INTENT_TTL_MS,\n emptyQueueState,\n normalizeQueueState,\n queuePositionOf,\n isHeadOfQueue,\n isQueueBusy,\n visibleQueueFor as visibleQueueFor2\n} from \"./queue.js\";\nimport {\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n changelogForUpdate as changelogForUpdate2,\n hasVisibleSections,\n isUnreleasedVersion,\n parseChangelog as parseChangelog2,\n renderChangelogHTML as renderChangelogHTML2,\n renderChangelogNeutral,\n renderChangelogSection,\n selectChangelogEntries\n} from \"./changelog.js\";\nexport {\n CHANGELOG_ALL_CATEGORIES,\n CHANGELOG_FILENAME,\n CHANGELOG_FOLDED,\n CHANGELOG_MAX_BULLETS_PER_SECTION,\n CHANGELOG_MAX_BULLET_CHARS,\n CHANGELOG_MAX_CHARS,\n CHANGELOG_MAX_ENTRIES,\n CHANGELOG_MUST_SHOW,\n CHANGELOG_NEUTRAL_HINT,\n CHANGELOG_NEUTRAL_LINE,\n PANEL_DIAG_MAX_CHARS,\n PANEL_POLL,\n QUEUE_INTENT_TTL_MS,\n UPDATE_PANEL_CSS,\n UPDATE_PANEL_D5_CSS,\n blockedCopy,\n buildDiagnosticText,\n buildChangelogPhoneName2 as buildPanelChangelogPhoneName,\n buildPhoneNames2 as buildPanelPhoneNames,\n buildUpdateDiagCopy,\n changelogForUpdate2 as changelogForUpdate,\n createBrowserSkipStore,\n createMemorySkipStore,\n emptyQueueState,\n failureCodeOf,\n failureCopy,\n formatLatchTime,\n hasVisibleSections,\n isHeadOfQueue,\n isKnownFailureCode,\n isQueueBusy,\n isUnreleasedVersion,\n mountUpdatePanel,\n normalizePanelTheme,\n normalizeQueueState,\n panelViewModel,\n parseChangelog2 as parseChangelog,\n pendingAutoCheck,\n queuePositionOf,\n readDiagTolerant,\n redactForCopy,\n renderChangelogHTML2 as renderChangelogHTML,\n renderChangelogNeutral,\n renderChangelogSection,\n renderUpdatePanelHTML,\n renderUpdatePanelKernel,\n selectChangelogEntries,\n visibleQueueFor2 as visibleQueueFor\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/lang.ts\nfunction normalizeLangTag(tag) {\n if (typeof tag !== \"string\") return \"zh\";\n const s = tag.trim().toLowerCase().replace(/_/g, \"-\");\n if (!s) return \"zh\";\n if (s === \"zh\" || s.startsWith(\"zh-\")) return \"zh\";\n if (s === \"en\" || s.startsWith(\"en-\")) return \"en\";\n if (/^[a-z]{2,3}(-[a-z0-9]+)*$/.test(s)) return \"en\";\n return \"zh\";\n}\nfunction readDocumentLang() {\n try {\n const g = globalThis;\n const v = g.document?.documentElement?.lang;\n if (typeof v === \"string\" && v.trim()) return v;\n } catch {\n }\n return null;\n}\nfunction readNavigatorLangs() {\n try {\n const g = globalThis;\n const nav = g.navigator;\n if (!nav) return [];\n const out = [];\n if (Array.isArray(nav.languages)) {\n for (const c of nav.languages) {\n if (typeof c === \"string\" && c.trim()) out.push(c);\n }\n }\n if (typeof nav.language === \"string\" && nav.language.trim()) out.push(nav.language);\n return out;\n } catch {\n return [];\n }\n}\nfunction resolveLang(override) {\n if (override !== null && typeof override === \"object\") {\n try {\n const fn = override.getActive;\n if (typeof fn === \"function\") {\n const v = fn.call(override);\n if (typeof v === \"string\" && v.trim()) return normalizeLangTag(v);\n }\n } catch {\n }\n } else if (typeof override === \"string\" && override.trim()) {\n return normalizeLangTag(override);\n }\n const docLang = readDocumentLang();\n if (docLang !== null) return normalizeLangTag(docLang);\n const navs = readNavigatorLangs();\n for (const c of navs) {\n return normalizeLangTag(c);\n }\n return \"zh\";\n}\nconst entries = /* @__PURE__ */ new Set();\nlet mo = null;\nfunction ensureGlobalObserver() {\n if (mo !== null) return;\n try {\n const g = globalThis;\n const docEl = g.document?.documentElement;\n const MO = g.MutationObserver;\n if (!docEl || typeof MO !== \"function\") return;\n const obs = new MO(() => {\n notifyAll();\n });\n obs.observe(docEl, { attributes: true, attributeFilter: [\"lang\"] });\n mo = obs;\n } catch {\n mo = null;\n }\n}\nfunction notifyAll() {\n for (const e of [...entries]) {\n try {\n const next = resolveLang(e.override);\n if (next !== e.last) {\n e.last = next;\n e.cb(next);\n }\n } catch {\n }\n }\n}\nfunction subscribeLang(cb, override) {\n if (typeof cb !== \"function\") throw new Error(\"[dsh-plugin-update] subscribeLang needs a function\");\n const entry = { cb, override: override ?? void 0, last: resolveLang(override), serviceUnsub: null };\n entries.add(entry);\n ensureGlobalObserver();\n try {\n if (entry.override !== null && typeof entry.override === \"object\") {\n const sub = entry.override.subscribe;\n if (typeof sub === \"function\") {\n const un = sub.call(entry.override, () => {\n try {\n const next = resolveLang(entry.override);\n if (next !== entry.last) {\n entry.last = next;\n entry.cb(next);\n }\n } catch {\n }\n });\n if (typeof un === \"function\") entry.serviceUnsub = un;\n }\n }\n } catch {\n }\n let done = false;\n return () => {\n if (done) return;\n done = true;\n entries.delete(entry);\n try {\n entry.serviceUnsub?.();\n } catch {\n }\n entry.serviceUnsub = null;\n if (entries.size === 0) {\n try {\n mo?.disconnect();\n } catch {\n }\n mo = null;\n }\n };\n}\nfunction __resetLangState() {\n for (const e of [...entries]) {\n try {\n e.serviceUnsub?.();\n } catch {\n }\n }\n entries.clear();\n try {\n mo?.disconnect();\n } catch {\n }\n mo = null;\n}\nexport {\n __resetLangState,\n normalizeLangTag,\n resolveLang,\n subscribeLang\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/bilingual.ts\nimport { normalizeLangTag, resolveLang } from \"./lang.js\";\nconst BILINGUAL_DICT_VERSION = \"2026-10-06-pin53\";\nconst BILINGUAL_STRINGS = {\n \"entry.label.idle\": { en: \"Check for updates\", zh: \"\\u68C0\\u67E5\\u66F4\\u65B0\", draft: true },\n \"entry.label.failed\": { en: \"Update failed \\u2014 View details\", zh: \"\\u66F4\\u65B0\\u5931\\u8D25\\uFF0C\\u70B9\\u6B64\\u67E5\\u770B\", draft: true },\n \"entry.label.busy\": { en: \"Installing\\u2026\", zh: \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\", draft: true },\n \"entry.label.restart\": { en: \"Restart required\", zh: \"\\u5F85\\u91CD\\u542F\", draft: true },\n \"entry.label.has-update\": { en: \"Update available {version}\", zh: \"\\u6709\\u65B0\\u7248 {version}\", draft: true },\n \"entry.action.checking\": { en: \"Checking for updates\\u2026\", zh: \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\", draft: true },\n \"entry.note.up-to-date\": { en: \"Up to date {version}\", zh: \"\\u5DF2\\u662F\\u6700\\u65B0 {version}\", draft: true },\n \"batch-entry.label.idle\": { en: \"Check for updates\", zh: \"\\u68C0\\u67E5\\u66F4\\u65B0\", draft: true },\n \"batch-entry.label.update\": { en: \"{count} updates available\", zh: \"{count} \\u5BB6\\u53EF\\u66F4\\u65B0\", draft: true },\n \"batch-entry.label.busy\": { en: \"Installing\\u2026\", zh: \"\\u6B63\\u5728\\u5B89\\u88C5\\u2026\", draft: true },\n \"batch-entry.label.restart\": { en: \"{count} restarts required\", zh: \"{count} \\u5BB6\\u5F85\\u91CD\\u542F\", draft: true },\n \"batch-entry.label.failed\": { en: \"{count} failed \\u2014 View details\", zh: \"{count} \\u5BB6\\u5931\\u8D25\\uFF0C\\u70B9\\u6B64\\u67E5\\u770B\", draft: true },\n \"batch-entry.action.checking\": { en: \"Checking for updates\\u2026\", zh: \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\u2026\", draft: true }\n};\nfunction escapeHtml(value) {\n return String(value ?? \"\").replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction formatTemplate(template, values) {\n const vals = values ?? {};\n return String(template).replace(/\\{([A-Za-z0-9_]+)\\}/g, (_, name) => {\n const v = vals[name];\n if (v === null || v === void 0) return \"\";\n return String(v).trim();\n });\n}\nfunction bilingualEntry(key) {\n const hit = BILINGUAL_STRINGS[key];\n if (!hit) throw new Error(\"[dsh-plugin-update] \\u672A\\u77E5\\u6587\\u6848 key\\uFF1A\" + String(key));\n return hit;\n}\nfunction copyText(key, lang, values) {\n const l = normalizeLangTag(lang ?? \"zh\");\n const e = bilingualEntry(key);\n return formatTemplate(l === \"en\" ? e.en : e.zh, values);\n}\nfunction copyHTML(key, lang, values) {\n const l = normalizeLangTag(lang ?? \"zh\");\n const e = bilingualEntry(key);\n const text = escapeHtml(formatTemplate(l === \"en\" ? e.en : e.zh, values));\n return '<span class=\"dsh-upd-bi\"><span lang=\"' + l + '\">' + text + \"</span></span>\";\n}\nfunction bilingualText(key, values) {\n return copyText(key, resolveLang(), values);\n}\nfunction bilingualHTML(key, values) {\n return copyHTML(key, resolveLang(), values);\n}\nfunction isDraft(key) {\n return bilingualEntry(key).draft;\n}\nfunction draftKeys() {\n return Object.keys(BILINGUAL_STRINGS).filter((k) => BILINGUAL_STRINGS[k].draft);\n}\nconst BILINGUAL_CSS = [\n \".dsh-upd-bi{display:inline;overflow-wrap:anywhere}\",\n \".dsh-upd-bi [lang]{overflow-wrap:anywhere}\"\n].join(\"\\n\");\nexport {\n BILINGUAL_CSS,\n BILINGUAL_DICT_VERSION,\n BILINGUAL_STRINGS,\n bilingualEntry,\n bilingualHTML,\n bilingualText,\n copyHTML,\n copyText,\n draftKeys,\n isDraft\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/entry.ts\nimport { DEFAULT_PREFIX, MIN_PANEL_POLL_MS, buildPhoneNames } from \"./config.js\";\nimport {\n failureCodeOf,\n mountUpdatePanel,\n normalizePanelTheme\n} from \"./panel.js\";\nimport { BILINGUAL_CSS, copyHTML, copyText } from \"./bilingual.js\";\nimport { resolveLang, subscribeLang } from \"./lang.js\";\nfunction hasUpdateOf(snapshot) {\n if (!snapshot) return false;\n const latest = snapshot.latestVersion;\n if (typeof latest !== \"string\" || !latest.trim()) return false;\n return latest.trim() !== snapshot.runningVersion;\n}\nfunction entryStateKind(state) {\n const snapshot = state?.snapshot ?? null;\n const job = snapshot?.job ?? null;\n if (job && (job.state === \"installing\" || job.state === \"verifying\")) return \"busy\";\n if (snapshot && (snapshot.blockedReason === \"pending-restart\" || job?.state === \"restart-required\")) {\n return \"restart\";\n }\n if (state && state.error || job && (job.state === \"failed\" || job.state === \"interrupted\")) {\n return \"failed\";\n }\n if (hasUpdateOf(snapshot)) return \"update\";\n return \"idle\";\n}\nfunction entryBilingualKeyFor(state) {\n switch (entryStateKind(state)) {\n case \"busy\":\n return \"entry.label.busy\";\n case \"restart\":\n return \"entry.label.restart\";\n case \"failed\":\n return \"entry.label.failed\";\n case \"update\": {\n const latest = state?.snapshot?.latestVersion;\n return typeof latest === \"string\" && latest.trim() ? \"entry.label.has-update\" : \"entry.label.idle\";\n }\n default:\n return \"entry.label.idle\";\n }\n}\nfunction entryBilingualValuesFor(state) {\n const key = entryBilingualKeyFor(state);\n if (key === \"entry.label.has-update\") {\n const v = state?.snapshot?.latestVersion;\n return { version: typeof v === \"string\" ? v.trim() : \"\" };\n }\n return {};\n}\nfunction entryBilingualHTMLFor(state, lang) {\n const l = lang ?? resolveLang();\n return copyHTML(entryBilingualKeyFor(state), l, entryBilingualValuesFor(state));\n}\nfunction entryBilingualTextFor(state, lang) {\n const l = lang ?? resolveLang();\n return copyText(entryBilingualKeyFor(state), l, entryBilingualValuesFor(state));\n}\nfunction entryLabelFor(state, lang) {\n const l = lang ?? resolveLang();\n return copyText(entryBilingualKeyFor(state), l, entryBilingualValuesFor(state));\n}\nconst UPDATE_ENTRY_CSS = [\n '.dsh-upd-entry{display:inline-flex;align-items:center;gap:8px;font:13px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937)}',\n \".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;\",\n \"background:var(--dsh-upd-btn,#f9fafb);color:inherit;padding:4px 12px;cursor:pointer}\",\n \".dsh-upd-entry-btn:hover{border-color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}\",\n \".dsh-upd-entry-btn:active:not(:disabled){transform:translateY(1px)}\",\n \".dsh-upd-entry-btn:disabled,.dsh-upd-entry-dot:disabled{opacity:.55;cursor:wait}\",\n '.dsh-upd-entry-btn[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:10px;height:10px;margin-left:7px;vertical-align:-1px;',\n \"border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-entry-spin .8s linear infinite}\",\n \"@keyframes dsh-upd-entry-spin{to{transform:rotate(360deg)}}\",\n '@media (prefers-reduced-motion: reduce){.dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:none}.dsh-upd-entry-btn[aria-busy=\"true\"]::after{animation:none}}',\n \".dsh-upd-entry-btn:focus-visible,.dsh-upd-entry-dot:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}\",\n '.dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-ok-line,#059669);color:var(--dsh-upd-ok-line,#059669)}',\n '.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-warn-line,#d97706);color:var(--dsh-upd-warn-line,#d97706)}',\n '.dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}',\n \".dsh-upd-entry-dot{width:10px;height:10px;padding:0;border:0;border-radius:50%;background:var(--dsh-upd-line,#9ca3af);cursor:pointer}\",\n '.dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-dot{background:var(--dsh-upd-ok-line,#059669)}',\n '.dsh-upd-entry[data-state=\"busy\"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-dot{background:var(--dsh-upd-warn-line,#d97706)}',\n '.dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}',\n // 小字自带底(深色宿主 + 浅色变量时也读得出;浅底宿主上只是多一圈细线,不抢戏)。\n \".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;padding:1px 8px}\",\n '.dsh-upd-entry[data-theme=\"archive\"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}',\n '.dsh-upd-entry[data-theme=\"archive\"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;',\n 'font-family:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",serif;color:var(--d5-ink)}',\n // 按钮脸自己不透明(深色宿主 + 浅色系统变量时也读得出;hover 红在深浅底上都可见)。\n '.dsh-upd-entry[data-theme=\"archive\"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}',\n '.dsh-upd-entry[data-theme=\"archive\"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}',\n '@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme=\"archive\"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}',\n \"@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}\",\n \".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}\"\n].join(\"\\n\");\nconst ENTRY_ATTR = \"data-dsh-upd-entry\";\nconst ENTRY_SELECTOR = \"[data-dsh-upd-entry]\";\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction asSnapshot(value) {\n if (!isObject(value)) return null;\n if (typeof value[\"runningVersion\"] !== \"string\") return null;\n if (typeof value[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction escapeHtml(value) {\n return String(value).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction mountUpdateEntry(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\");\n }\n const pluginId = options.pluginId;\n if (typeof pluginId !== \"string\" || !pluginId) {\n throw new Error(`[dsh-plugin-update] \\u63D2\\u4EF6\\u6807\\u8BC6 pluginId \\u5FC5\\u586B\\uFF1A\\u987B\\u4E3A\\u975E\\u7A7A\\u5B57\\u7B26\\u4E32\\uFF08\\u6536\\u5230 ${JSON.stringify(pluginId)}\\uFF09`);\n }\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u66F4\\u65B0\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u5165\\u53E3\\u4EF6\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const rawPrefix = options.prefix;\n const prefix = rawPrefix === void 0 ? DEFAULT_PREFIX : rawPrefix;\n const phoneNames = buildPhoneNames(prefix);\n const variant = options.variant ?? \"button\";\n if (variant !== \"button\" && variant !== \"badge\" && variant !== \"inline\") {\n throw new Error(`[dsh-plugin-update] \\u5165\\u53E3\\u4EF6\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 button / badge / inline\\uFF08\\u6536\\u5230 ${JSON.stringify(options.variant)}\\uFF09`);\n }\n const autoCheck = options.autoCheck ?? \"mount\";\n if (autoCheck !== \"mount\" && autoCheck !== \"never\") {\n throw new Error(`[dsh-plugin-update] \\u81EA\\u52A8\\u68C0\\u67E5\\u65F6\\u673A\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 mount \\u6216 never\\uFF08\\u6536\\u5230 ${JSON.stringify(options.autoCheck)}\\uFF09`);\n }\n const openOn = options.openOn ?? \"has-update\";\n if (openOn !== \"has-update\" && openOn !== \"always\" && openOn !== \"manual\" && openOn !== \"direct\") {\n throw new Error(`[dsh-plugin-update] \\u70B9\\u51FB\\u53BB\\u5411\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 has-update / always / manual / direct\\uFF08\\u6536\\u5230 ${JSON.stringify(options.openOn)}\\uFF09`);\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(options.theme)}\\uFF09`);\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n const pollMs = options.pollMs;\n if (pollMs !== void 0 && (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS)) {\n throw new Error(`[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 ${JSON.stringify(options.pollMs)}\\uFF09`);\n }\n const labelOverride = typeof options.label === \"string\" && options.label ? options.label : null;\n const localeOpt = options.locale ?? void 0;\n if (localeOpt !== void 0 && localeOpt !== null) {\n const isStr = typeof localeOpt === \"string\";\n const isObj = typeof localeOpt === \"object\" && typeof localeOpt.getActive === \"function\";\n if (!isStr && !isObj) {\n throw new Error(\"[dsh-plugin-update] invalid locale: expected zh / en / BCP47 or { getActive(), subscribe? }\");\n }\n if (isStr && !localeOpt.trim()) {\n throw new Error(\"[dsh-plugin-update] invalid locale: empty string\");\n }\n }\n function currentLang() {\n return resolveLang(localeOpt);\n }\n const profileName = typeof options.profileName === \"string\" && options.profileName ? options.profileName : null;\n const onActivate = typeof options.onActivate === \"function\" ? options.onActivate : null;\n const call = options.call;\n let snapshot = null;\n let error = null;\n let noteVersion = null;\n let activating = false;\n let mounted = true;\n let panel = null;\n let panelMode = null;\n const panelHost = {\n get innerHTML() {\n return container.innerHTML;\n },\n set innerHTML(value) {\n container.innerHTML = value;\n },\n addEventListener(type, listener) {\n container.addEventListener?.(type, listener);\n },\n removeEventListener(type, listener) {\n container.removeEventListener?.(type, listener);\n }\n };\n async function panelCall(name, args) {\n const reply = await call(name, args);\n return isObject(reply) ? reply : {};\n }\n function stateOf() {\n return { snapshot, error };\n }\n function currentLabel() {\n return labelOverride ?? entryLabelFor(stateOf(), currentLang());\n }\n function hasUpdate() {\n return hasUpdateOf(snapshot);\n }\n function phoneArgs() {\n return {};\n }\n function entryHTML() {\n const kind = entryStateKind(stateOf());\n const lang = currentLang();\n const labelHTML = labelOverride ? escapeHtml(labelOverride) : activating ? copyHTML(\"entry.action.checking\", lang) : copyHTML(entryBilingualKeyFor(stateOf()), lang, entryBilingualValuesFor(stateOf()));\n const labelText = labelOverride ?? (activating ? copyText(\"entry.action.checking\", lang) : copyText(entryBilingualKeyFor(stateOf()), lang, entryBilingualValuesFor(stateOf())));\n const busyAttr = activating ? ' disabled aria-busy=\"true\"' : \"\";\n const themeAttr = theme === \"archive\" ? ' data-theme=\"archive\"' : \"\";\n const control = variant === \"badge\" ? `<button type=\"button\" class=\"dsh-upd-entry-dot\" ${ENTRY_ATTR}=\"activate\" title=\"${escapeHtml(labelText)}\" aria-label=\"${escapeHtml(labelText)}\"${busyAttr}></button>` : `<button type=\"button\" class=\"dsh-upd-entry-btn\" ${ENTRY_ATTR}=\"activate\"${busyAttr}>${labelHTML}</button>`;\n const noteHTML = noteVersion ? `<span class=\"dsh-upd-entry-note\" data-dsh-upd-note=\"1\">${copyHTML(\"entry.note.up-to-date\", lang, { version: noteVersion })}</span>` : \"\";\n return `<style>${UPDATE_ENTRY_CSS}\n${BILINGUAL_CSS}</style>\n<span class=\"dsh-upd-entry\" data-variant=\"${variant}\" data-state=\"${kind}\"${themeAttr}>${control}${noteHTML}</span>`;\n }\n function render() {\n if (!mounted) return;\n if (panelMode !== null) return;\n container.innerHTML = entryHTML();\n }\n function applyReply(reply) {\n if (!isObject(reply) || reply[\"ok\"] !== true) {\n error = failureCodeOf(isObject(reply) ? reply : null, \"check-failed\");\n return;\n }\n const next = asSnapshot(reply[\"snapshot\"]);\n if (next) snapshot = next;\n error = null;\n }\n async function refresh() {\n if (!mounted) return;\n try {\n const reply = await call(phoneNames.updateStatus, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n render();\n }\n async function checkNow() {\n try {\n const reply = await call(phoneNames.updateCheck, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n }\n function mountPanel(mode) {\n if (!mounted || panelMode !== null) return;\n panel = mountUpdatePanel(panelHost, {\n pluginId,\n prefix,\n mode,\n theme,\n pollMs,\n profileName,\n autoChangelog: options.autoChangelog,\n changelogMarkdown: options.changelogMarkdown ?? null,\n locale: localeOpt,\n // 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。\n onCloseRequested: () => close(),\n call: panelCall\n });\n panelMode = mode;\n }\n function openDialog() {\n mountPanel(\"dialog\");\n }\n function open() {\n if (!mounted || panelMode !== null) return;\n if (variant === \"inline\") return;\n openDialog();\n }\n function close() {\n if (!mounted || panelMode !== \"dialog\" || !panel) return;\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened.unmount();\n } catch {\n }\n render();\n void refresh();\n }\n async function activate() {\n if (!mounted || panelMode !== null || activating) return;\n if (openOn === \"direct\" && variant === \"button\") {\n openDialog();\n return;\n }\n activating = true;\n noteVersion = null;\n render();\n try {\n await checkNow();\n } finally {\n activating = false;\n }\n if (!mounted) return;\n if (variant === \"badge\" || openOn === \"manual\") {\n if (onActivate) onActivate({ hasUpdate: hasUpdate(), latestVersion: snapshot?.latestVersion ?? null });\n else openDialog();\n render();\n return;\n }\n if (openOn === \"always\" || hasUpdate() || !snapshot || error) {\n openDialog();\n return;\n }\n noteVersion = snapshot.runningVersion;\n render();\n }\n function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(`[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 ${JSON.stringify(next)}\\uFF09`);\n }\n theme = normalizePanelTheme(next);\n if (panel) void panel.setTheme(theme);\n else render();\n }\n function onClick(ev) {\n if (!mounted) return;\n try {\n const target = ev?.target;\n const closest = target && typeof target.closest === \"function\" ? target.closest.bind(target) : null;\n if (!closest) return;\n if (closest(ENTRY_SELECTOR)) {\n void activate();\n return;\n }\n } catch {\n }\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || panelMode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n close();\n } catch {\n }\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n try {\n unsubLang();\n } catch {\n }\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened?.unmount();\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n const unsubLang = subscribeLang(() => {\n render();\n }, localeOpt);\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n if (variant === \"inline\") mountPanel(\"embedded\");\n else render();\n if (autoCheck === \"mount\") void refresh();\n return { refresh, open, close, label: currentLabel, setTheme, unmount };\n}\nexport {\n UPDATE_ENTRY_CSS,\n entryBilingualHTMLFor,\n entryBilingualKeyFor,\n entryBilingualTextFor,\n entryBilingualValuesFor,\n entryLabelFor,\n entryStateKind,\n mountUpdateEntry\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/batch.ts\nconst BATCH_SESSION_VERSION = 1;\nfunction isTerminalPhase(phase) {\n return phase === \"done\" || phase === \"failed\" || phase === \"skipped\" || phase === \"current\";\n}\nfunction asMillis(value, fallback) {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\nfunction isNonEmptyString(value) {\n return typeof value === \"string\" && value.length > 0;\n}\nconst PHASES = [\"pending\", \"checking\", \"ready\", \"installing\", \"current\", \"done\", \"failed\", \"skipped\"];\nfunction asPhase(value) {\n return typeof value === \"string\" && PHASES.includes(value) ? value : \"pending\";\n}\nfunction orderTargets(keys, selfKey) {\n const seen = /* @__PURE__ */ new Set();\n const others = [];\n let self = null;\n for (const key of keys) {\n if (!isNonEmptyString(key) || seen.has(key)) continue;\n seen.add(key);\n if (selfKey && key === selfKey) self = key;\n else others.push(key);\n }\n return self ? [...others, self] : others;\n}\nfunction batchRequestId(sessionId, key) {\n return \"batch:\" + sessionId + \":\" + key;\n}\nfunction createBatchSession(args) {\n const id = isNonEmptyString(args.id) ? args.id : \"batch\";\n const selfKey = isNonEmptyString(args.selfKey) ? args.selfKey : null;\n const order = orderTargets(args.keys, selfKey);\n const now = asMillis(args.now, 0);\n const entries = order.map((key) => ({\n key,\n phase: \"pending\",\n requestId: batchRequestId(id, key),\n targetVersion: null,\n restartRequired: false,\n error: null,\n updatedAt: now\n }));\n return {\n version: BATCH_SESSION_VERSION,\n id,\n selfKey,\n stopOnFailure: args.stopOnFailure === true,\n order,\n entries,\n createdAt: now,\n updatedAt: now\n };\n}\nfunction emptyBatchSession() {\n return {\n version: BATCH_SESSION_VERSION,\n id: \"\",\n selfKey: null,\n stopOnFailure: false,\n order: [],\n entries: [],\n createdAt: 0,\n updatedAt: 0\n };\n}\nfunction normalizeBatchSession(raw) {\n try {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return emptyBatchSession();\n const input = raw;\n if (input[\"version\"] !== BATCH_SESSION_VERSION) return emptyBatchSession();\n const id = isNonEmptyString(input[\"id\"]) ? input[\"id\"] : \"\";\n const selfKey = isNonEmptyString(input[\"selfKey\"]) ? input[\"selfKey\"] : null;\n const entries = [];\n const seen = /* @__PURE__ */ new Set();\n const rawEntries = input[\"entries\"];\n if (Array.isArray(rawEntries)) {\n for (const item of rawEntries) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) continue;\n const e = item;\n const key = e[\"key\"];\n if (!isNonEmptyString(key) || seen.has(key)) continue;\n seen.add(key);\n const requestId = isNonEmptyString(e[\"requestId\"]) ? e[\"requestId\"] : batchRequestId(id, key);\n entries.push({\n key,\n phase: asPhase(e[\"phase\"]),\n requestId,\n targetVersion: isNonEmptyString(e[\"targetVersion\"]) ? e[\"targetVersion\"] : null,\n restartRequired: e[\"restartRequired\"] === true,\n error: isNonEmptyString(e[\"error\"]) ? e[\"error\"] : null,\n updatedAt: asMillis(e[\"updatedAt\"], 0)\n });\n }\n }\n const order = entries.map((e) => e.key);\n return {\n version: BATCH_SESSION_VERSION,\n id,\n selfKey: selfKey && seen.has(selfKey) ? selfKey : null,\n stopOnFailure: input[\"stopOnFailure\"] === true,\n order,\n entries,\n createdAt: asMillis(input[\"createdAt\"], 0),\n updatedAt: asMillis(input[\"updatedAt\"], 0)\n };\n } catch {\n return emptyBatchSession();\n }\n}\nfunction batchEntryOf(session, key) {\n return session.entries.find((e) => e.key === key) ?? null;\n}\nfunction nextBatchKey(session) {\n if (session.stopOnFailure && session.entries.some((e) => e.phase === \"failed\")) return null;\n for (const key of session.order) {\n const entry = session.entries.find((e) => e.key === key);\n if (entry && !isTerminalPhase(entry.phase)) return key;\n }\n return null;\n}\nfunction markBatchEntry(session, key, patch, now) {\n const at = session.entries.findIndex((e) => e.key === key);\n if (at < 0) return { session, changed: false };\n const next = { ...session.entries[at], ...patch, key: session.entries[at].key, requestId: session.entries[at].requestId, updatedAt: asMillis(now, session.entries[at].updatedAt) };\n const entries = [...session.entries.slice(0, at), next, ...session.entries.slice(at + 1)];\n return { session: { ...session, entries, updatedAt: next.updatedAt }, changed: true };\n}\nfunction batchProgress(session) {\n const total = session.entries.length;\n let done = 0;\n let failed = 0;\n let skipped = 0;\n let current = 0;\n let pending = 0;\n for (const entry of session.entries) {\n if (entry.phase === \"done\" || entry.phase === \"current\") done += 1;\n else if (entry.phase === \"failed\") failed += 1;\n else if (entry.phase === \"skipped\") skipped += 1;\n else if (entry.phase === \"installing\" || entry.phase === \"checking\" || entry.phase === \"ready\") current += 1;\n else pending += 1;\n }\n return { total, done, failed, skipped, current, pending, finished: isBatchFinished(session) };\n}\nfunction isBatchFinished(session) {\n return session.entries.length > 0 && session.entries.every((e) => isTerminalPhase(e.phase));\n}\nfunction needsRestartKeys(session) {\n return session.entries.filter((e) => e.restartRequired).map((e) => e.key);\n}\nfunction failedKeys(session) {\n return session.entries.filter((e) => e.phase === \"failed\").map((e) => e.key);\n}\nfunction resumeBatchSession(session, now) {\n const at = asMillis(now, session.updatedAt);\n let changed = false;\n const entries = session.entries.map((entry) => {\n if (entry.phase === \"checking\" || entry.phase === \"installing\") {\n changed = true;\n return { ...entry, phase: \"pending\", error: null, updatedAt: at };\n }\n return entry;\n });\n return changed ? { ...session, entries, updatedAt: at } : session;\n}\nexport {\n BATCH_SESSION_VERSION,\n batchEntryOf,\n batchProgress,\n batchRequestId,\n createBatchSession,\n emptyBatchSession,\n failedKeys,\n isBatchFinished,\n isTerminalPhase,\n markBatchEntry,\n needsRestartKeys,\n nextBatchKey,\n normalizeBatchSession,\n orderTargets,\n resumeBatchSession\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel-batch.ts\nimport { assertPrefix, MIN_PANEL_POLL_MS } from \"./config.js\";\nimport { validReleaseVersion } from \"./service.js\";\nimport { shouldFetchChangelog } from \"./changelog.js\";\nimport {\n isTerminalPhase,\n normalizeBatchSession\n} from \"./batch.js\";\nimport {\n UPDATE_PANEL_CSS,\n UPDATE_PANEL_D5_CSS,\n buildDiagnosticText,\n createBrowserSkipStore,\n failureCopy,\n isKnownFailureCode,\n normalizePanelTheme,\n renderUpdatePanelHTML\n} from \"./panel.js\";\nconst BATCH_PANEL_POLL = {\n defaultMs: 1500,\n minMs: MIN_PANEL_POLL_MS\n};\nfunction buildBatchPhoneNames(prefix) {\n const p = assertPrefix(prefix, \"\\u6279\\u91CF\\u7535\\u8BDD\\u524D\\u7F00 prefix\");\n return {\n status: p + \".batchStatus\",\n check: p + \".batchCheck\",\n install: p + \".batchInstall\",\n resume: p + \".batchResume\",\n cancel: p + \".batchCancel\"\n };\n}\nfunction batchLedgerCounts(rows) {\n const counts = {\n updatable: 0,\n installing: 0,\n pending: 0,\n restart: 0,\n failed: 0,\n skipped: 0,\n settled: 0\n };\n for (const row of rows) {\n const phase = asBatchPhase(row.phase);\n if (isQueueBusyRow(row)) counts.updatable += 1;\n else if (phase === \"failed\") counts.failed += 1;\n else if (phase === \"installing\") counts.installing += 1;\n else if (phase === \"ready\") counts.updatable += 1;\n else if (phase === \"pending\" || phase === \"checking\") counts.pending += 1;\n else if (phase === \"skipped\") counts.skipped += 1;\n else if (row.restartRequired === true) counts.restart += 1;\n else counts.settled += 1;\n }\n return counts;\n}\nfunction batchLedgerText(counts) {\n const parts = [];\n if (counts.updatable > 0) parts.push(counts.updatable + \" \\u5BB6\\u53EF\\u66F4\\u65B0\");\n if (counts.installing > 0) parts.push(counts.installing + \" \\u5BB6\\u5B89\\u88C5\\u4E2D\");\n if (counts.pending > 0) parts.push(counts.pending + \" \\u5BB6\\u5F85\\u67E5\");\n if (counts.restart > 0) parts.push(counts.restart + \" \\u5BB6\\u5F85\\u91CD\\u542F\");\n if (counts.failed > 0) parts.push(counts.failed + \" \\u5BB6\\u5931\\u8D25\");\n if (counts.skipped > 0) parts.push(counts.skipped + \" \\u5BB6\\u5DF2\\u8DF3\\u8FC7\");\n if (counts.settled > 0) parts.push(counts.settled + \" \\u5BB6\\u5DF2\\u6700\\u65B0\");\n return parts.length > 0 ? parts.join(\" \\xB7 \") : \"\\u8FD8\\u6CA1\\u6709\\u76EE\\u6807\";\n}\nfunction batchQueuedStatus(position) {\n if (typeof position !== \"number\" || !Number.isFinite(position) || position < 1) return \"\\u5DF2\\u6392\\u961F \\xB7 \\u7B49\\u524D\\u9762\\u5B89\\u88C5\\u5B8C\";\n return \"\\u5DF2\\u6392\\u961F \\xB7 \\u524D\\u65B9 \" + position + \" \\u4E2A\";\n}\nfunction batchRowStatus(row, skippedVersion) {\n const skipped = typeof skippedVersion === \"string\" && skippedVersion ? skippedVersion : null;\n if (skipped !== null && asBatchPhase(row.phase) === \"ready\") return \"\\u5DF2\\u8DF3\\u8FC7 \" + skipped;\n switch (asBatchPhase(row.phase)) {\n case \"pending\":\n return \"\\u7B49\\u5B83\\uFF0C\\u8F6E\\u5230\\u5C31\\u81EA\\u52A8\\u67E5\\u65B0\\u7248\";\n case \"checking\":\n return \"\\u6B63\\u5728\\u67E5\\u65B0\\u7248\\uFF0C\\u7A0D\\u7B49\";\n case \"ready\":\n return row.targetVersion ? \"\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u5B89\\u88C5 \" + row.targetVersion : \"\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u5B89\\u88C5\\u65B0\\u7248\";\n case \"installing\":\n return \"\\u6B63\\u5728\\u5B89\\u88C5\\uFF0C\\u522B\\u52A8\";\n case \"current\":\n return \"\\u5DF2\\u662F\\u6700\\u65B0\\uFF0C\\u4E0D\\u7528\\u52A8\";\n case \"done\":\n return row.restartRequired === true ? \"\\u5B89\\u88C5\\u597D\\u4E86\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u624D\\u751F\\u6548\" : \"\\u5B89\\u88C5\\u597D\\u4E86\\uFF0C\\u4E0D\\u7528\\u52A8\";\n case \"failed\":\n return \"\\u5B89\\u88C5\\u6CA1\\u6210\\u529F\\uFF0C\\u70B9\\u300C\\u91CD\\u8BD5\\u300D\\u518D\\u6765\\u4E00\\u6B21\";\n case \"skipped\":\n return \"\\u8FD9\\u4E00\\u7248\\u5DF2\\u8DF3\\u8FC7\\uFF0C\\u4E0D\\u7528\\u52A8\";\n default:\n return \"\\u72B6\\u6001\\u8BA4\\u4E0D\\u51FA\\uFF0C\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u91CD\\u67E5\\u4E00\\u6B21\";\n }\n}\nfunction renderBatchPanelHTML(input) {\n const rows = Array.isArray(input.rows) ? input.rows : [];\n const theme = normalizePanelTheme(input.theme);\n const mode = input.mode === \"dialog\" ? \"dialog\" : \"embedded\";\n const titles = input.titles && typeof input.titles === \"object\" ? input.titles : {};\n const skippedVersions = input.skippedVersions && typeof input.skippedVersions === \"object\" ? input.skippedVersions : {};\n const expandedKey = typeof input.expandedKey === \"string\" && input.expandedKey ? input.expandedKey : null;\n const lastError = typeof input.lastError === \"string\" && input.lastError ? input.lastError : null;\n const loaded = input.loaded === void 0 ? rows.length > 0 : input.loaded === true;\n const counts = batchLedgerCounts(rows);\n const installing = counts.installing > 0;\n const macroBusy = input.inFlight === true || installing;\n const total = rows.length;\n const terminal = total > 0 && rows.every((row) => isTerminalPhase(asBatchPhase(row.phase)));\n const stalled = total > 0 && !terminal && !rows.some((row) => {\n const phase = asBatchPhase(row.phase);\n return phase === \"installing\" || phase === \"checking\";\n });\n const disabled = macroBusy ? \" disabled\" : \"\";\n const notice = typeof input.notice === \"string\" && input.notice ? input.notice : null;\n const noticeKey = typeof input.noticeKey === \"string\" && input.noticeKey ? input.noticeKey : null;\n const detailNoticeKey = notice !== null && noticeKey !== null && noticeKey === expandedKey ? noticeKey : null;\n const changelogs = input.changelogs && typeof input.changelogs === \"object\" ? input.changelogs : {};\n const ctx = {\n expanded: false,\n busy: installing,\n inFlight: input.inFlight === true,\n theme,\n titles,\n skipped: null,\n notice: null,\n changelogMarkdown: null\n };\n const parts = [];\n parts.push('<div class=\"dsh-upd-batch-head\">');\n parts.push('<div class=\"dsh-upd-batch-title\">\\u66F4\\u65B0\\u6863\\u6848 \\xB7 <i>\\u603B\\u8D26</i></div>');\n parts.push('<div class=\"dsh-upd-batch-macros\">');\n parts.push('<button type=\"button\" data-act=\"check\"' + disabled + ' title=\"\\u91CD\\u65B0\\u8BFB\\u53D6\\u6279\\u91CF\\u72B6\\u6001\\uFF08\\u53EA\\u8BFB\\uFF09\">\\u68C0\\u67E5\\u66F4\\u65B0</button>');\n parts.push('<button type=\"button\" data-act=\"install\" data-primary=\"1\"' + disabled + ' title=\"\\u628A\\u6709\\u65B0\\u7248\\u7684\\u51E0\\u5BB6\\u4E00\\u6B21\\u63D0\\u4EA4\\uFF1B\\u540C\\u4E00\\u4F1A\\u8BDD\\u540C\\u4E00\\u5E42\\u7B49\\u7F16\\u53F7\">\\u5168\\u90E8\\u66F4\\u65B0</button>');\n if (mode === \"dialog\") parts.push('<button type=\"button\" data-act=\"close\" title=\"\\u5173\\u95ED\\u7A97\\u53E3\\uFF0C\\u6279\\u91CF\\u66F4\\u65B0\\u4E0D\\u53D7\\u5F71\\u54CD\">\\u5173\\u95ED</button>');\n parts.push(\"</div>\");\n parts.push(\"</div>\");\n const summary = loaded ? batchSummaryText(counts, rows, total, installing, lastError !== null) : \"\\u6B63\\u5728\\u8BFB\\u53D6\\u6279\\u91CF\\u66F4\\u65B0\\u72B6\\u6001\\u2026\";\n parts.push('<div class=\"dsh-upd-batch-sum\" role=\"status\" aria-live=\"polite\">' + escapeHtml(summary) + \"</div>\");\n parts.push(bannersHTML(rows, titles, lastError));\n parts.push('<div class=\"dsh-upd-btable\">');\n for (const row of rows) {\n const raw = skippedVersions[row.key];\n const skipped = typeof raw === \"string\" && raw ? raw : null;\n const expanded = row.key === expandedKey;\n parts.push(\n rowSetHTML(row, {\n ...ctx,\n expanded,\n skipped,\n notice: expanded && row.key === detailNoticeKey ? notice : null,\n changelogMarkdown: expanded && typeof changelogs[row.key] === \"string\" ? changelogs[row.key] : null\n })\n );\n }\n parts.push(\"</div>\");\n if (total > 0) {\n parts.push('<div class=\"dsh-upd-batch-ledger\">' + escapeHtml(batchLedgerText(counts)) + \"</div>\");\n }\n if (total > 0 && !terminal) {\n const more = [];\n if (stalled) more.push('<button type=\"button\" data-act=\"resume\">\\u63A5\\u7740\\u4E0A\\u6B21</button>');\n const confirmCancel = input.confirmCancel === true;\n more.push(\n '<button type=\"button\" data-act=\"cancel\"' + disabled + (confirmCancel ? ' data-confirm=\"1\" title=\"\\u518D\\u70B9\\u4E00\\u6B21\\u786E\\u8BA4\\u53D6\\u6D88\"' : ' title=\"\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\uFF08\\u8981\\u70B9\\u4E24\\u6B21\\u786E\\u8BA4\\uFF0C\\u9632\\u8BEF\\u89E6\\uFF09\"') + \">\" + (confirmCancel ? \"\\u786E\\u8BA4\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\" : \"\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\") + \"</button>\"\n );\n parts.push('<div class=\"dsh-upd-batch-more\">' + more.join(\"\") + \"</div>\");\n }\n if (notice !== null && detailNoticeKey === null) {\n parts.push('<div class=\"dsh-upd-batch-notice\" role=\"status\">' + escapeHtml(notice) + \"</div>\");\n }\n const kernel = parts.join(String.fromCharCode(10));\n const d5 = normalizePanelTheme(theme) === \"archive\";\n const attr = d5 ? ' data-theme=\"archive\"' : \"\";\n const seal = batchSealOf(counts);\n const root = '<div class=\"dsh-upd dsh-upd-batch\" data-mode=\"' + mode + '\" data-seal=\"' + escapeHtml(seal.text) + '\" data-seal-tone=\"' + seal.tone + '\"' + attr + \">\\n\" + kernel + \"\\n</div>\";\n const body = mode === \"dialog\" ? '<div class=\"dsh-upd-overlay\" data-mode=\"dialog\">' + root + \"</div>\" : root;\n const css = d5 ? [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS, UPDATE_PANEL_D5_CSS, UPDATE_BATCH_PANEL_D5_CSS].join(String.fromCharCode(10)) : [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS].join(String.fromCharCode(10));\n return \"<style>\" + css + \"</style>\\n\" + body;\n}\nfunction batchSummaryText(counts, rows, total, installing, hasError) {\n if (hasError) return \"\\u521A\\u624D\\u90A3\\u6B21\\u6CA1\\u6210\\u529F\\uFF1A\\u770B\\u4E0B\\u9762\\u7684\\u7EA2\\u6761\\uFF0C\\u7167\\u5B83\\u8BF4\\u7684\\u505A\\u4E00\\u6B21\\u3002\";\n if (total === 0) return \"\\u8FD8\\u6CA1\\u6709\\u76EE\\u6807\\uFF1A\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u770B\\u770B\\u54EA\\u51E0\\u5BB6\\u6709\\u65B0\\u7248\\u3002\";\n if (installing) {\n const canQueue = rows.filter((row) => asBatchPhase(row.phase) === \"ready\" && !isQueuedRow(row)).length;\n return canQueue > 0 ? \"\\u6B63\\u5728\\u5B89\\u88C5 \" + counts.installing + \" \\u5BB6\\uFF1B\\u8FD8\\u6709 \" + canQueue + \" \\u5BB6\\u53EF\\u4EE5\\u70B9\\u300C\\u52A0\\u5165\\u961F\\u5217\\u300D\\u6392\\u961F\\u7B49\\u3002\" : \"\\u6B63\\u5728\\u5B89\\u88C5 \" + counts.installing + \" \\u5BB6\\uFF0C\\u5B89\\u88C5\\u5B8C\\u81EA\\u52A8\\u4E0B\\u4E00\\u5BB6\\u3002\";\n }\n if (counts.failed > 0) return counts.failed + \" \\u5BB6\\u5B89\\u88C5\\u5931\\u8D25\\uFF1B\\u7167\\u4E0B\\u9762\\u7684\\u5931\\u8D25\\u63D0\\u793A\\u9010\\u5BB6\\u91CD\\u8BD5\\u3002\";\n if (counts.updatable > 0)\n return counts.updatable + \" \\u5BB6\\u53EF\\u66F4\\u65B0\\uFF1B\\u70B9\\u300C\\u5168\\u90E8\\u66F4\\u65B0\\u300D\\u4E00\\u6B21\\u5B89\\u88C5\\u5B8C\\uFF0C\\u4E5F\\u53EF\\u4EE5\\u9010\\u5BB6\\u70B9\\u300C\\u5B89\\u88C5\\u8FD9\\u5BB6\\u300D\\u3002\";\n if (counts.restart > 0) return counts.restart + \" \\u5BB6\\u5DF2\\u5B89\\u88C5\\u597D\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002\";\n if (counts.pending > 0) return counts.pending + \" \\u5BB6\\u8FD8\\u6CA1\\u67E5\\u8FC7\\uFF1B\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u67E5\\u4E00\\u8F6E\\u3002\";\n return \"\\u5168\\u90E8\\u5DF2\\u6700\\u65B0\\uFF0C\\u6CA1\\u6709\\u8981\\u505A\\u7684\\u3002\";\n}\nfunction batchSealOf(counts) {\n if (counts.failed > 0) return { text: \"\\u603B\\u8D26\", tone: \"red\" };\n if (counts.installing > 0 || counts.restart > 0) return { text: \"\\u603B\\u8D26\", tone: \"yellow\" };\n if (counts.updatable > 0) return { text: \"\\u603B\\u8D26\", tone: \"green\" };\n return { text: \"\\u603B\\u8D26\", tone: \"ink\" };\n}\nfunction bannersHTML(rows, titles, lastError) {\n const parts = [];\n if (lastError) {\n const copy = failureCopy(lastError);\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"failed\" data-mini=\"\\u963B\" role=\"status\"><div><strong>\\u8FD9\\u6B21\\u6CA1\\u6210\\u529F\\uFF08' + escapeHtml(lastError) + \"\\uFF09\\uFF1A\" + escapeHtml(copy ? copy.zh : \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"\\u3002</strong></div><div>\" + escapeHtml(copy ? copy.act : \"\\u5148\\u91CD\\u8BD5\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u8FD9\\u6837\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u3002\") + \"</div></div>\"\n );\n }\n const failed = rows.filter((row) => asBatchPhase(row.phase) === \"failed\" && !isQueueBusyRow(row));\n if (failed.length > 0) {\n const lines = failed.map((row) => {\n const code = row.error || \"install-failed\";\n const copy = failureCopy(code);\n return \"<div>\" + escapeHtml(titleOf(row, titles)) + \"\\uFF1A<code>\" + escapeHtml(code) + \"</code> \" + escapeHtml(copy ? copy.zh : \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"</div>\";\n }).join(\"\");\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"failed\" data-mini=\"\\u963B\" role=\"status\"><div><strong>' + failed.length + ' \\u5BB6\\u5B89\\u88C5\\u5931\\u8D25\\u3002</strong></div><div>\\u9010\\u5BB6\\u70B9\\u884C\\u5185\\u300C\\u91CD\\u8BD5\\u300D\\u518D\\u6765\\u4E00\\u6B21\\uFF1B\\u4E00\\u76F4\\u5931\\u8D25\\u5C31\\u628A\\u590D\\u5236\\u8BCA\\u65AD\\u4EA4\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u3002</div><div class=\"dsh-upd-blist\">' + lines + \"</div></div>\"\n );\n }\n const restart = rows.filter((row) => row.restartRequired === true);\n if (restart.length > 0) {\n parts.push(\n '<div class=\"dsh-upd-banner\" data-kind=\"restart\" role=\"status\"><div><strong>' + restart.length + ' \\u5BB6\\u5DF2\\u5B89\\u88C5\\u597D\\uFF0C\\u91CD\\u542F\\u5BBF\\u4E3B\\u540E\\u751F\\u6548\\u3002</strong></div><div>\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u8BA9\\u65B0\\u7248\\u8DD1\\u8D77\\u6765\\uFF1B\\u8FD9\\u662F\\u6B63\\u5E38\\u7EC8\\u6001\\uFF0C\\u4E0D\\u662F\\u5931\\u8D25\\u3002</div><div class=\"dsh-upd-blist\">' + restart.map((row) => escapeHtml(titleOf(row, titles))).join(\"\\u3001\") + '</div><div><button type=\"button\" data-act=\"restart\" data-primary=\"1\">\\u91CD\\u542F\\u5BBF\\u4E3B</button></div></div>'\n );\n }\n return parts.join(\"\");\n}\nfunction rowSetHTML(row, ctx) {\n const phase = asBatchPhase(row.phase);\n const keyAttr = escapeHtml(row.key);\n const skipped = ctx.skipped;\n const queued = isQueuedRow(row);\n const position = queued ? queuePositionOf(row) : null;\n const current = currentVersionOf(row);\n const version = row.targetVersion ? (current === null ? \"?\" : current) + \" \\u2192 \" + row.targetVersion : current === null ? \"\" : current;\n const actions = [];\n const off = ctx.inFlight ? \" disabled\" : \"\";\n if (phase === \"installing\") {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" disabled>\\u5B89\\u88C5\\u4E2D\\u2026</button>'\n );\n } else if (queued) {\n if (cancelQueuePhoneOf(row) !== null && queuedRequestIdOf(row) !== null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-cancel-queue\" data-key=\"' + keyAttr + '\"' + off + \">\\u53D6\\u6D88\\u6392\\u961F</button>\"\n );\n }\n } else if (phase === \"ready\" && skipped === null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" data-primary=\"1\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u5B89\\u88C5\\u8FD9\\u5BB6\") + \"</button>\"\n );\n } else if (phase === \"ready\" && skipped !== null) {\n actions.push(\n '<button type=\"button\" data-act=\"row-resume-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u6062\\u590D\\uFF08\" + escapeHtml(skipped) + \"\\uFF09</button>\"\n );\n } else if (phase === \"failed\") {\n actions.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u91CD\\u8BD5\") + \"</button>\"\n );\n }\n if (row.restartRequired === true) {\n actions.push('<button type=\"button\" data-act=\"restart\" data-key=\"' + keyAttr + '\">\\u91CD\\u542F\\u5BBF\\u4E3B</button>');\n }\n actions.push(\n '<button type=\"button\" data-act=\"toggle-details\" data-key=\"' + keyAttr + '\">' + (ctx.expanded ? \"\\u6536\\u8D77\" : \"\\u8BE6\\u60C5\") + \"</button>\"\n );\n const failLine = phase === \"failed\" && !queued ? '<span class=\"dsh-upd-bfail\">\\u5931\\u8D25 <code>' + escapeHtml(row.error || \"install-failed\") + \"</code>\\uFF1A\" + escapeHtml(failureCopy(row.error || \"install-failed\")?.zh ?? \"\\u8BA4\\u4E0D\\u51FA\\u5177\\u4F53\\u539F\\u56E0\") + \"</span>\" : \"\";\n const main = '<button type=\"button\" class=\"dsh-upd-brow-main\" data-act=\"toggle-details\" data-key=\"' + keyAttr + '\" aria-expanded=\"' + (ctx.expanded ? \"true\" : \"false\") + '\"><span class=\"dsh-upd-updot\" data-tone=\"' + dotToneOf(row, phase, skipped, queued) + '\"></span><span class=\"dsh-upd-bname\">' + escapeHtml(titleOf(row, ctx.titles)) + \"</span>\" + (version ? '<span class=\"dsh-upd-bver\">' + escapeHtml(version) + \"</span>\" : \"\") + '<span class=\"dsh-upd-bstat\">' + escapeHtml(queued ? batchQueuedStatus(position) : batchRowStatus(row, skipped)) + \"</span>\" + failLine + \"</button>\";\n const detail = ctx.expanded ? '<div class=\"dsh-upd-bdetail\" data-key=\"' + keyAttr + '\">' + detailActionsHTML(row, ctx) + (ctx.notice !== null ? '<div class=\"dsh-upd-bdetail-notice\" role=\"status\">' + escapeHtml(ctx.notice) + \"</div>\" : \"\") + detailHTML(row, ctx) + \"</div>\" : \"\";\n return '<div class=\"dsh-upd-brow-set\" data-key=\"' + keyAttr + '\"><div class=\"dsh-upd-brow\" data-phase=\"' + escapeHtml(phase) + '\" data-key=\"' + keyAttr + '\"' + (queued ? ' data-queued=\"1\"' : \"\") + \">\" + main + '<span class=\"dsh-upd-brow-actions\">' + actions.join(\"\") + \"</span></div>\" + detail + \"</div>\";\n}\nfunction detailHTML(row, ctx) {\n const html = renderUpdatePanelHTML({\n snapshot: asSnapshotLike(row.snapshot),\n manual: typeof row.manual === \"string\" ? row.manual : null,\n changelogMarkdown: typeof ctx.changelogMarkdown === \"string\" ? ctx.changelogMarkdown : null,\n queue: asQueueLike(row.queue),\n // 跳过是面板本地记录(与单插件面板同一套语义):内核按「已跳过」画 tag 与提示行,\n // 恢复按钮由上面的动作行提供,措辞与内核提示里的「恢复」对得上。\n skippedLatest: ctx.skipped !== null,\n lastError: row.error,\n errorKind: row.error,\n mode: \"embedded\",\n showOthers: false,\n pluginId: pluginIdOf(row),\n copyNotice: null,\n theme: ctx.theme,\n profileName: typeof row.profileName === \"string\" && row.profileName ? row.profileName : null,\n actions: \"none\"\n });\n return stripActionButtons(html);\n}\nfunction detailActionsHTML(row, ctx) {\n const phase = asBatchPhase(row.phase);\n const keyAttr = escapeHtml(row.key);\n const version = latestVersionOf(row);\n const queued = isQueuedRow(row);\n const off = ctx.inFlight ? \" disabled\" : \"\";\n const buttons = [];\n if (phase === \"installing\") {\n buttons.push('<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" disabled>\\u5B89\\u88C5\\u4E2D\\u2026</button>');\n } else if (queued) {\n if (cancelQueuePhoneOf(row) !== null && queuedRequestIdOf(row) !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-cancel-queue\" data-key=\"' + keyAttr + '\"' + off + \">\\u53D6\\u6D88\\u6392\\u961F</button>\"\n );\n }\n } else if (phase === \"failed\") {\n buttons.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u91CD\\u8BD5\") + \"</button>\"\n );\n } else if (phase === \"ready\" && ctx.skipped === null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-install\" data-key=\"' + keyAttr + '\" data-primary=\"1\"' + off + \">\" + (ctx.busy ? \"\\u52A0\\u5165\\u961F\\u5217\" : \"\\u5B89\\u88C5 \" + escapeHtml(version ?? \"\\u65B0\\u7248\")) + \"</button>\"\n );\n }\n if (!queued && phase === \"ready\" && ctx.skipped === null && version !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u8DF3\\u8FC7\\u8FD9\\u4E00\\u7248</button>\"\n );\n }\n if (ctx.skipped !== null) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-resume-skip\" data-key=\"' + keyAttr + '\"' + off + \">\\u6062\\u590D\\uFF08\" + escapeHtml(ctx.skipped) + \"\\uFF09</button>\"\n );\n }\n if (typeof row.manual === \"string\" && row.manual) {\n buttons.push(\n '<button type=\"button\" data-act=\"row-copy-manual\" data-key=\"' + keyAttr + '\"' + off + \">\\u590D\\u5236\\u624B\\u5DE5\\u547D\\u4EE4</button>\"\n );\n }\n buttons.push('<button type=\"button\" data-act=\"row-copy-diag\" data-key=\"' + keyAttr + '\"' + off + \">\\u590D\\u5236\\u8BCA\\u65AD</button>\");\n return '<span class=\"dsh-upd-bdetail-actions\">' + buttons.join(\"\") + \"</span>\";\n}\nfunction stripActionButtons(html) {\n return html.replace(/<button\\b[^>]*data-action=[^>]*>[\\s\\S]*?<\\/button>/g, \"\");\n}\nfunction dotToneOf(row, phase, skipped, queued) {\n if (phase === \"failed\" && !queued) return \"bad\";\n if (phase === \"installing\" || phase === \"checking\") return \"busy\";\n if (queued) return \"warn\";\n if (row.restartRequired === true) return \"warn\";\n if (phase === \"ready\") return skipped === null ? \"todo\" : \"idle\";\n if (phase === \"current\" || phase === \"done\") return \"ok\";\n return \"idle\";\n}\nconst UPDATE_BATCH_PANEL_CSS = [\n \"/* \\u6279\\u91CF\\u9762\\u677F\\uFF08#25\\uFF09\\uFF1A\\u7C7B\\u540D\\u6CBF\\u7528 dsh-upd-* \\u524D\\u7F00\\uFF1B\\u9ED8\\u8BA4\\uFF08\\u6700\\u5C0F\\uFF09\\u4E3B\\u9898\\uFF0CD5 \\u76AE\\u80A4\\u89C1 UPDATE_BATCH_PANEL_D5_CSS\\u3002 */\",\n \".dsh-upd-batch-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;flex-wrap:wrap}\",\n \".dsh-upd-batch-title{font-weight:700;font-size:15px;letter-spacing:.06em}\",\n \".dsh-upd-batch-title i{font-style:normal;color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-batch-macros{flex:none}\",\n \".dsh-upd-batch-sum{margin:4px 0 8px;font-size:13.5px}\",\n \".dsh-upd-btable{margin-top:2px}\",\n \".dsh-upd-brow-set{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}\",\n \".dsh-upd-brow-set:first-child{border-top:0}\",\n \".dsh-upd-brow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 0}\",\n \".dsh-upd .dsh-upd-brow-main{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:1 1 260px;min-width:0;\",\n \"text-align:left;cursor:pointer;color:inherit;font:inherit;background:transparent;border:0;padding:2px 0;margin:0}\",\n \".dsh-upd .dsh-upd-brow-main:hover{color:var(--dsh-upd-primary,#2563eb)}\",\n \".dsh-upd-brow-actions{flex:none;margin-left:auto}\",\n \".dsh-upd-brow-actions button{font-size:12.5px}\",\n \".dsh-upd-updot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--dsh-upd-line,#9ca3af);opacity:.55}\",\n '.dsh-upd-updot[data-tone=\"todo\"]{background:var(--dsh-upd-ok-line,#059669);opacity:1}',\n '.dsh-upd-updot[data-tone=\"ok\"]{background:var(--dsh-upd-ok-line,#059669);opacity:1}',\n '.dsh-upd-updot[data-tone=\"busy\"]{background:var(--dsh-upd-busy-line,#2563eb);opacity:1}',\n '.dsh-upd-updot[data-tone=\"warn\"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',\n '.dsh-upd-updot[data-tone=\"bad\"]{background:var(--dsh-upd-bad-line,#dc2626);opacity:1}',\n \".dsh-upd-bname{font-weight:700;flex:none}\",\n \".dsh-upd-bver{font-family:Consolas,Menlo,monospace;font-size:12px;opacity:.8;flex:none}\",\n \".dsh-upd-bstat{font-size:12.5px;opacity:.9}\",\n \".dsh-upd-bfail{flex:1 1 100%;display:block;font-size:12.5px;color:var(--dsh-upd-bad-line,#dc2626)}\",\n \".dsh-upd-blist{font-size:12.5px;margin-top:2px}\",\n \".dsh-upd-batch-ledger{margin:8px 0 0;font-size:13px;opacity:.85}\",\n \".dsh-upd-batch-more{margin-top:6px}\",\n \".dsh-upd-batch-more button{font-size:12px}\",\n // 确认态:红框红字,与「关闭」等中性按钮一眼区分;disabled 照旧置灰。\n '.dsh-upd-batch-more button[data-confirm=\"1\"]{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}',\n \".dsh-upd-batch-notice{margin-top:6px;font-size:12.5px;opacity:.85}\",\n \".dsh-upd-bdetail-actions{margin:0 0 6px}\",\n \".dsh-upd .dsh-upd-bdetail-actions button{font-size:12.5px}\",\n \".dsh-upd-bdetail-notice{margin:0 0 6px;font-size:12.5px;opacity:.85}\",\n \"/* \\u8BE6\\u60C5\\u590D\\u7528\\u5355\\u63D2\\u4EF6\\u5185\\u6838\\uFF1A\\u4E0D\\u8BB8\\u5D4C\\u5957\\u6EDA\\u52A8\\u2014\\u2014\\u4E2D\\u6027\\u5316 overlay \\u7684 max-height/overflow\\uFF0C\\u81EA\\u8EAB\\u4E5F\\u4E0D\\u7ED9 overflow\\u3002 */\",\n \".dsh-upd-bdetail{margin:2px 0 10px;overflow:visible}\",\n \".dsh-upd .dsh-upd-bdetail .dsh-upd{max-width:none}\",\n \".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd{max-height:none;overflow:visible}\",\n \".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body{overflow:visible}\",\n \".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body .dsh-upd-chap-head{position:static}\",\n \"@media (prefers-color-scheme: dark){.dsh-upd-batch-title i{color:var(--dsh-upd-focus,#93c5fd)}}\",\n \"@media (prefers-reduced-motion: reduce){.dsh-upd-batch *{transition:none !important;animation:none !important}}\"\n].join(String.fromCharCode(10));\nconst UPDATE_BATCH_PANEL_D5_CSS = [\n \"/* \\u6279\\u91CF\\u9762\\u677F\\u7684 D5 \\u76AE\\u80A4\\uFF1A\\u53EA\\u6362\\u989C\\u8272/\\u5B57\\u4F53/\\u95F4\\u8DDD\\uFF1B\\u6839\\u4E0A\\u7684\\u5927\\u5370\\u7AE0\\u7531 UPDATE_PANEL_D5_CSS \\u8D1F\\u8D23\\u3002 */\",\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-head{padding-right:120px}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-title{font-family:var(--d5-serif);font-size:21px;letter-spacing:.04em}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-title i{color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-brow-main{background:transparent;border:0;color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-brow-main:hover{color:var(--d5-accent)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-bname{font-family:var(--d5-serif)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-bver{font-family:var(--d5-mono);color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-ledger{color:var(--d5-muted)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-sum{color:var(--d5-ink)}',\n '.dsh-upd[data-theme=\"archive\"] .dsh-upd-brow-set{border-top-color:var(--d5-line)}'\n].join(String.fromCharCode(10));\nfunction escapeHtml(text) {\n return String(text).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nconst BATCH_PHASES = [\n \"pending\",\n \"checking\",\n \"ready\",\n \"installing\",\n \"current\",\n \"done\",\n \"failed\",\n \"skipped\"\n];\nfunction isQueueBusyRow(row) {\n return asBatchPhase(row.phase) === \"failed\" && row.error === \"update-busy\";\n}\nfunction queuePositionOf(row) {\n const queue = asQueueLike(row.queue);\n if (!queue || queue.busy !== true) return null;\n const position = queue.position;\n return typeof position === \"number\" && Number.isFinite(position) && position > 0 ? position : null;\n}\nfunction isQueuedRow(row) {\n const phase = asBatchPhase(row.phase);\n if (phase === \"installing\") return false;\n if (isQueueBusyRow(row)) return true;\n if (isTerminalPhase(phase)) return false;\n return queuePositionOf(row) !== null;\n}\nfunction queuedRequestIdOf(row) {\n const queue = asQueueLike(row.queue);\n if (!queue || !Array.isArray(queue.waiting)) return null;\n for (const entry of queue.waiting) {\n if (entry && typeof entry.requestId === \"string\" && entry.requestId) return entry.requestId;\n }\n return null;\n}\nfunction cancelQueuePhoneOf(row) {\n const phones = isObject(row.phoneNames) ? row.phoneNames : null;\n if (!phones) return null;\n const install = phones[\"updateInstall\"];\n return typeof install === \"string\" && install ? install : null;\n}\nfunction asBatchPhase(value) {\n return typeof value === \"string\" && BATCH_PHASES.includes(value) ? value : \"pending\";\n}\nfunction asSnapshotLike(value) {\n if (!isObject(value)) return null;\n if (typeof value[\"runningVersion\"] !== \"string\") return null;\n if (typeof value[\"canInstall\"] !== \"boolean\") return null;\n return value;\n}\nfunction asQueueLike(value) {\n if (!isObject(value) || typeof value[\"busy\"] !== \"boolean\") return null;\n return value;\n}\nfunction titleOf(row, titles) {\n const override = titles[row.key];\n if (typeof override === \"string\" && override) return override;\n return row.title || row.key;\n}\nfunction pluginIdOf(row) {\n return typeof row.pluginId === \"string\" && row.pluginId ? row.pluginId : row.key;\n}\nfunction latestVersionOf(row) {\n if (typeof row.targetVersion === \"string\" && row.targetVersion) return row.targetVersion;\n const snapshot = row.snapshot;\n if (!isObject(snapshot)) return null;\n const latest = snapshot[\"latestVersion\"];\n return typeof latest === \"string\" && latest ? latest : null;\n}\nasync function defaultCopyText(text) {\n try {\n const holder = globalThis[\"navigator\"];\n const clip = holder?.clipboard;\n const write = clip?.writeText;\n if (clip && typeof write === \"function\") {\n await write.call(clip, text);\n return;\n }\n } catch {\n }\n}\nfunction currentVersionOf(row) {\n const snapshot = row.snapshot;\n if (!isObject(snapshot)) return null;\n const running = snapshot[\"runningVersion\"];\n if (typeof running === \"string\" && running) return running;\n const installed = snapshot[\"installedVersion\"];\n if (typeof installed === \"string\" && installed) return installed;\n return null;\n}\nfunction jobFailureOf(row) {\n const snapshot = isObject(row.snapshot) ? row.snapshot : null;\n if (!snapshot) return null;\n const job = isObject(snapshot[\"job\"]) ? snapshot[\"job\"] : null;\n if (!job) return null;\n const state = typeof job[\"state\"] === \"string\" ? job[\"state\"] : \"\";\n if (state !== \"failed\" && state !== \"interrupted\") return null;\n const message = typeof job[\"message\"] === \"string\" ? job[\"message\"].trim() : \"\";\n if (!message) return null;\n const at = message.indexOf(\":\");\n if (at < 0) {\n return isKnownFailureCode(message) ? { code: message, detail: null } : { code: null, detail: message };\n }\n const code = message.slice(0, at).trim();\n const detail = message.slice(at + 1).trim();\n return { code: code || null, detail: detail || null };\n}\nfunction diagTextOf(row) {\n const snapshot = isObject(row.snapshot) ? row.snapshot : null;\n const queue = isObject(row.queue) ? row.queue : null;\n const diag = isObject(row.diag) ? row.diag : null;\n const text = (value) => typeof value === \"string\" && value ? value : null;\n const job = jobFailureOf(row);\n const diagDetail = diag ? text(diag[\"detail\"]) : null;\n const detail = diagDetail !== null ? diagDetail : job !== null && job.detail !== null ? job.detail + \"\\uFF08\\u6765\\u6E90\\uFF1A\\u540E\\u53F0\\u4EFB\\u52A1\\u6536\\u5C3E\\u8BB0\\u5F55\\uFF09\" : null;\n const input = {\n pluginId: pluginIdOf(row),\n code: job !== null && job.code !== null ? job.code : row.error || \"check-failed\",\n detail,\n runningVersion: text(snapshot ? snapshot[\"runningVersion\"] : null),\n installedVersion: text(snapshot ? snapshot[\"installedVersion\"] : null),\n latestVersion: text(snapshot ? snapshot[\"latestVersion\"] : null),\n hostKind: text(row.hostKind),\n queuePosition: queue && typeof queue[\"position\"] === \"number\" ? queue[\"position\"] : null,\n manual: text(row.manual),\n diag: row.diag ?? null,\n profileName: text(row.profileName)\n };\n return buildDiagnosticText(input);\n}\nfunction isBusyReply(reply) {\n if (!isObject(reply) || reply[\"ok\"] === true) return false;\n const kind = typeof reply[\"errorKind\"] === \"string\" ? reply[\"errorKind\"] : \"\";\n const err = typeof reply[\"error\"] === \"string\" ? reply[\"error\"] : \"\";\n return kind === \"update-busy\" || err === \"update-busy\";\n}\nfunction readRows(value) {\n if (!Array.isArray(value)) return [];\n const out = [];\n const seen = /* @__PURE__ */ new Set();\n for (const item of value) {\n if (!isObject(item)) continue;\n const key = typeof item[\"key\"] === \"string\" && item[\"key\"] ? item[\"key\"] : \"\";\n if (!key || seen.has(key)) continue;\n seen.add(key);\n out.push({\n key,\n title: typeof item[\"title\"] === \"string\" && item[\"title\"] ? item[\"title\"] : key,\n phase: asBatchPhase(item[\"phase\"]),\n targetVersion: typeof item[\"targetVersion\"] === \"string\" && item[\"targetVersion\"] ? item[\"targetVersion\"] : null,\n restartRequired: item[\"restartRequired\"] === true,\n error: typeof item[\"error\"] === \"string\" && item[\"error\"] ? item[\"error\"] : null,\n snapshot: item[\"snapshot\"] === void 0 ? null : item[\"snapshot\"],\n manual: typeof item[\"manual\"] === \"string\" ? item[\"manual\"] : null,\n queue: item[\"queue\"] === void 0 ? null : item[\"queue\"],\n profileName: typeof item[\"profileName\"] === \"string\" && item[\"profileName\"] ? item[\"profileName\"] : null,\n pluginId: typeof item[\"pluginId\"] === \"string\" && item[\"pluginId\"] ? item[\"pluginId\"] : null,\n diag: item[\"diag\"] === void 0 ? null : item[\"diag\"],\n hostKind: typeof item[\"hostKind\"] === \"string\" && item[\"hostKind\"] ? item[\"hostKind\"] : null,\n phoneNames: item[\"phoneNames\"] === void 0 ? null : item[\"phoneNames\"]\n });\n }\n return out;\n}\nfunction rowsFromSession(session) {\n return session.entries.map((entry) => ({\n key: entry.key,\n title: entry.key,\n phase: entry.phase,\n targetVersion: entry.targetVersion,\n restartRequired: entry.restartRequired,\n error: entry.error,\n snapshot: null,\n manual: null,\n queue: null,\n profileName: null\n }));\n}\nfunction getTimer() {\n const g = globalThis;\n const set = g[\"setInterval\"];\n const clear = g[\"clearInterval\"];\n if (typeof set === \"function\" && typeof clear === \"function\") {\n return {\n set: (fn, ms) => set(fn, ms),\n clear: (h) => clear(h)\n };\n }\n const setT = g[\"setTimeout\"];\n const clearT = g[\"clearTimeout\"];\n return {\n set: (fn, ms) => setT(fn, ms),\n clear: (h) => clearT(h)\n };\n}\nfunction mountUpdateBatchPanel(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u6279\\u91CF\\u7535\\u8BDD\\u524D\\u7F00 prefix \\u5FC5\\u586B\");\n }\n const phones = buildBatchPhoneNames(options.prefix);\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u8F7D\\u6279\\u91CF\\u9762\\u677F\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u9762\\u677F\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const pollMs = options.pollMs === void 0 ? BATCH_PANEL_POLL.defaultMs : options.pollMs;\n if (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < BATCH_PANEL_POLL.minMs) {\n throw new Error(\n \"[dsh-plugin-update] \\u6279\\u91CF\\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.pollMs) + \"\\uFF09\"\n );\n }\n let mode = options.mode === void 0 ? \"embedded\" : options.mode;\n if (mode !== \"embedded\" && mode !== \"dialog\") {\n throw new Error(\"[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.mode) + \"\\uFF09\");\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.theme) + \"\\uFF09\");\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n const call = options.call;\n const titles = options.titles && typeof options.titles === \"object\" ? options.titles : {};\n const onRestartRequested = options.onRestartRequested;\n const onCloseRequested = typeof options.onCloseRequested === \"function\" ? options.onCloseRequested : null;\n const copyText = options.copyText ?? defaultCopyText;\n const skipStores = /* @__PURE__ */ new Map();\n const autoChangelogEnabled = options.autoChangelog !== false;\n const rowChangelog = /* @__PURE__ */ new Map();\n const rowInflight = /* @__PURE__ */ new Set();\n const rowFailedAt = /* @__PURE__ */ new Map();\n let rowAutoSeq = 0;\n let rows = [];\n let expandedKey = null;\n let lastError = null;\n let notice = null;\n let noticeKey = null;\n let inFlight = false;\n let loaded = false;\n let refreshing = false;\n let confirmCancelArmed = false;\n let mounted = true;\n function changelogView() {\n const out = {};\n for (const row of rows) {\n const cached = rowChangelog.get(row.key);\n if (!cached || typeof cached.markdown !== \"string\") continue;\n if (latestVersionOf(row) !== cached.version) continue;\n out[row.key] = cached.markdown;\n }\n return out;\n }\n function changelogPhoneOf(row) {\n const phones2 = isObject(row.phoneNames) ? row.phoneNames : null;\n if (!phones2) return null;\n const name = phones2[\"updateChangelog\"];\n return typeof name === \"string\" && name ? name : null;\n }\n function maybeAutoRowChangelog(key) {\n if (!autoChangelogEnabled || !mounted || !key) return;\n const row = rowOfKey(key);\n if (!row || asBatchPhase(row.phase) !== \"ready\") return;\n const version = latestVersionOf(row);\n if (!version || !validReleaseVersion(version)) return;\n try {\n if (skipStoreFor(pluginIdOf(row)).has(version)) return;\n } catch {\n }\n const cached = rowChangelog.get(key);\n if (cached && cached.version === version) return;\n if (rowInflight.has(key)) return;\n try {\n const failKey = key + \"\\0\" + version;\n const hasCache = false;\n const failedAt = rowFailedAt.has(failKey) ? rowFailedAt.get(failKey) : null;\n let nowMs = 0;\n try {\n nowMs = Date.now();\n } catch {\n nowMs = 0;\n }\n if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return;\n } catch {\n }\n const phone2 = changelogPhoneOf(row);\n if (!phone2) return;\n rowInflight.add(key);\n const seq = rowAutoSeq;\n void Promise.resolve().then(() => call(phone2, { version })).then(\n (reply) => {\n rowInflight.delete(key);\n if (!mounted || seq !== rowAutoSeq) return;\n if (!isObject(reply) || reply[\"ok\"] !== true) {\n try {\n rowFailedAt.set(key + \"\\0\" + version, Date.now());\n } catch {\n try {\n rowFailedAt.set(key + \"\\0\" + version, 0);\n } catch {\n }\n }\n return;\n }\n const md = reply[\"markdown\"];\n rowChangelog.set(key, { version, markdown: typeof md === \"string\" ? md : null });\n if (typeof md === \"string\") render();\n },\n () => {\n rowInflight.delete(key);\n if (mounted && seq === rowAutoSeq) {\n try {\n rowFailedAt.set(key + \"\\0\" + version, Date.now());\n } catch {\n try {\n rowFailedAt.set(key + \"\\0\" + version, 0);\n } catch {\n }\n }\n }\n }\n );\n }\n let lastHTML = \"\";\n function render() {\n if (!mounted) return;\n const nextHTML = renderBatchPanelHTML({\n rows,\n theme,\n mode,\n expandedKey,\n lastError,\n notice,\n noticeKey,\n skippedVersions: skippedVersionsOf(),\n inFlight,\n loaded,\n titles,\n confirmCancel: confirmCancelArmed,\n changelogs: changelogView()\n });\n if (nextHTML !== lastHTML) {\n lastHTML = nextHTML;\n container.innerHTML = nextHTML;\n }\n }\n function rowOfKey(key) {\n return rows.find((row) => row.key === key) ?? null;\n }\n function skipStoreFor(pluginId) {\n let store = skipStores.get(pluginId);\n if (!store) {\n store = createBrowserSkipStore(pluginId);\n skipStores.set(pluginId, store);\n }\n return store;\n }\n function skippedVersionOf(row) {\n if (asBatchPhase(row.phase) !== \"ready\") return null;\n const version = latestVersionOf(row);\n if (!version) return null;\n try {\n return skipStoreFor(pluginIdOf(row)).has(version) ? version : null;\n } catch {\n return null;\n }\n }\n function skippedVersionsOf() {\n const out = {};\n for (const row of rows) out[row.key] = skippedVersionOf(row);\n return out;\n }\n function say(text, key = null) {\n notice = text;\n noticeKey = key;\n }\n function clearNotice() {\n notice = null;\n noticeKey = null;\n }\n async function copyWith(text, okNotice, key) {\n try {\n await copyText(text);\n say(okNotice, key);\n } catch {\n say(\"\\u590D\\u5236\\u5931\\u8D25\\uFF0C\\u8BF7\\u624B\\u52A8\\u9009\\u4E2D\\u4E0A\\u9762\\u7684\\u4FE1\\u606F\\u3002\", key);\n }\n render();\n }\n function applyReply(reply) {\n if (!isObject(reply)) {\n lastError = \"internal\";\n return;\n }\n if (reply[\"ok\"] === true) {\n const session = normalizeBatchSession(reply[\"session\"]);\n const parsed = readRows(reply[\"rows\"]);\n rows = parsed.length > 0 ? parsed : rowsFromSession(session);\n loaded = true;\n lastError = null;\n if (expandedKey !== null && !rows.some((row) => row.key === expandedKey)) expandedKey = null;\n return;\n }\n const err = typeof reply[\"error\"] === \"string\" ? reply[\"error\"].trim() : \"\";\n const kind = typeof reply[\"errorKind\"] === \"string\" ? reply[\"errorKind\"].trim() : \"\";\n lastError = kind || err || \"internal\";\n }\n async function refresh() {\n if (!mounted || refreshing) return;\n refreshing = true;\n try {\n const reply = await call(phones.status, {});\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n } finally {\n refreshing = false;\n }\n render();\n maybeAutoRowChangelog(expandedKey);\n }\n async function phone(name, args, busyRowKey = null) {\n if (!mounted || inFlight) return;\n inFlight = true;\n clearNotice();\n render();\n try {\n const reply = await call(name, args);\n if (!mounted) return;\n if (busyRowKey !== null && isBusyReply(reply)) {\n say(\"\\u524D\\u9762\\u8FD8\\u5728\\u88C5\\uFF1A\\u8FD9\\u4E00\\u5BB6\\u8FD8\\u6CA1\\u6392\\u4E0A\\uFF0C\\u7B49\\u90A3\\u5BB6\\u88C5\\u5B8C\\u518D\\u70B9\\u4E00\\u6B21\\u3002\", busyRowKey);\n return;\n }\n applyReply(reply);\n rowFailedAt.clear();\n } catch {\n if (!mounted) return;\n lastError = \"check-failed\";\n } finally {\n inFlight = false;\n if (mounted) render();\n }\n maybeAutoRowChangelog(expandedKey);\n }\n async function act(kind, key) {\n if (!mounted) return;\n if (kind !== \"cancel\" && confirmCancelArmed) {\n confirmCancelArmed = false;\n render();\n }\n switch (kind) {\n case \"check\":\n return phone(phones.check, {});\n case \"install\":\n return phone(phones.install, {});\n case \"row-install\": {\n if (typeof key !== \"string\" || !key) return;\n return phone(phones.install, { keys: [key] }, key);\n }\n case \"resume\": {\n await phone(phones.resume, {});\n if (!mounted) return;\n say(\"\\u5DF2\\u63A5\\u7740\\u4E0A\\u6B21\\u7684\\u4F1A\\u8BDD\\u63A8\\u8FDB\\u4E00\\u6B65\\u3002\");\n render();\n return;\n }\n // 程序调用(controller.act)即执行:调它就是明确意图,不上膛。\n // 鼠标误触的防护在 onClick 那一层(第一次点击只上膛)。\n case \"cancel\": {\n confirmCancelArmed = false;\n await phone(phones.cancel, {});\n if (!mounted) return;\n say(\"\\u5DF2\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\uFF1A\\u5269\\u4E0B\\u7684\\u4E0D\\u518D\\u63A8\\u8FDB\\uFF1B\\u8981\\u91CD\\u6765\\u70B9\\u300C\\u68C0\\u67E5\\u66F4\\u65B0\\u300D\\u3002\");\n render();\n return;\n }\n // —— 详情里的动作行(与行内同一通道:都走 row-install / row-skip 这些 data-act)——\n case \"row-skip\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n const version = row ? latestVersionOf(row) : null;\n if (!row || !version) return;\n try {\n skipStoreFor(pluginIdOf(row)).skip(version);\n } catch {\n }\n say(\"\\u5DF2\\u8DF3\\u8FC7 \" + version + \"\\uFF1A\\u8FD9\\u4E00\\u7248\\u4E0D\\u518D\\u63D0\\u9192\\uFF1B\\u70B9\\u300C\\u6062\\u590D\\u300D\\u53EF\\u64A4\\u9500\\u3002\", row.key);\n render();\n return;\n }\n case \"row-resume-skip\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n const version = latestVersionOf(row);\n try {\n skipStoreFor(pluginIdOf(row)).reset(version ?? void 0);\n } catch {\n }\n say(version ? \"\\u5DF2\\u6062\\u590D \" + version + \"\\uFF1A\\u8FD9\\u4E00\\u7248\\u4F1A\\u7167\\u5E38\\u63D0\\u9192\\u3002\" : \"\\u5DF2\\u6062\\u590D\\u8DF3\\u8FC7\\u63D0\\u9192\\u3002\", row.key);\n render();\n return;\n }\n // 「取消排队」:走**该家自己的**单插件安装电话(宿主支持 cancelQueued:只撤自己那条 waiting,\n // 不碰别家、也不碰正在装的拥有者)。批量取消会清掉整轮会话,所以这里绝不用它顶。\n case \"row-cancel-queue\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n const phoneName = cancelQueuePhoneOf(row);\n const requestId = queuedRequestIdOf(row);\n if (phoneName === null || requestId === null) {\n say(\"\\u8FD9\\u4E00\\u884C\\u6CA1\\u5E26\\u53D6\\u6D88\\u6392\\u961F\\u8981\\u7528\\u7684\\u7535\\u8BDD\\u540D\\u6216\\u7F16\\u53F7\\uFF0C\\u6682\\u4E0D\\u80FD\\u53D6\\u6D88\\uFF1A\\u7B49\\u4E0B\\u4E00\\u6B21\\u5237\\u65B0\\u518D\\u770B\\u3002\", row.key);\n render();\n return;\n }\n if (inFlight) return;\n inFlight = true;\n clearNotice();\n render();\n try {\n await call(phoneName, { cancelQueued: true, requestId });\n if (mounted) say(\"\\u5DF2\\u53D6\\u6D88\\u6392\\u961F\\uFF1A\\u8FD9\\u4E00\\u5BB6\\u4E0D\\u7B49\\u4E86\\u3002\", row.key);\n } catch {\n if (mounted) say(\"\\u53D6\\u6D88\\u6392\\u961F\\u6CA1\\u6210\\u529F\\uFF08\\u53EF\\u80FD\\u5DF2\\u7ECF\\u5F00\\u59CB\\u88C5\\u4E86\\uFF09\\uFF1A\\u770B\\u4E0B\\u9762\\u6700\\u65B0\\u72B6\\u6001\\u3002\", row.key);\n } finally {\n inFlight = false;\n }\n await refresh();\n return;\n }\n case \"row-copy-manual\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n const manual = row && typeof row.manual === \"string\" ? row.manual : \"\";\n if (!row || !manual) return;\n await copyWith(manual, \"\\u624B\\u5DE5\\u547D\\u4EE4\\u5DF2\\u590D\\u5236\\uFF0C\\u7C98\\u5230\\u7EC8\\u7AEF\\u6574\\u884C\\u6267\\u884C\\u5373\\u53EF\\u3002\", row.key);\n return;\n }\n case \"row-copy-diag\": {\n const row = typeof key === \"string\" && key ? rowOfKey(key) : null;\n if (!row) return;\n await copyWith(diagTextOf(row), \"\\u8BCA\\u65AD\\u5DF2\\u590D\\u5236\\uFF0C\\u76F4\\u63A5\\u7C98\\u7ED9\\u63D2\\u4EF6\\u4F5C\\u8005\\u5373\\u53EF\\uFF08\\u5DF2\\u8131\\u654F\\uFF09\\u3002\", row.key);\n return;\n }\n case \"toggle-details\": {\n if (typeof key !== \"string\" || !key) return;\n expandedKey = expandedKey === key ? null : key;\n render();\n maybeAutoRowChangelog(expandedKey);\n return;\n }\n // 「重启宿主」:宿主没有重启自己的电话,只做入口——调用方给了 onRestartRequested 就交给它,\n // 没给就如实说「请手动重启」,不假装能重启(与单插件面板逐字同一口径)。\n case \"restart\": {\n try {\n if (typeof onRestartRequested === \"function\") {\n await onRestartRequested();\n say(\"\\u5DF2\\u6309\\u8C03\\u7528\\u65B9\\u7684\\u91CD\\u542F\\u6D41\\u7A0B\\u5904\\u7406\\uFF1B\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n } else {\n say(\"\\u672C\\u5BBF\\u4E3B\\u672A\\u63D0\\u4F9B\\u91CD\\u542F\\u5165\\u53E3\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n } catch {\n say(\"\\u91CD\\u542F\\u5165\\u53E3\\u8C03\\u7528\\u5931\\u8D25\\uFF1A\\u8BF7\\u624B\\u52A8\\u91CD\\u542F\\u5BBF\\u4E3B\\uFF0C\\u91CD\\u542F\\u540E\\u65B0\\u7248\\u751F\\u6548\\u3002\");\n }\n render();\n return;\n }\n case \"close\": {\n await requestDialogClose();\n return;\n }\n default:\n return;\n }\n }\n async function requestDialogClose() {\n if (!mounted || mode !== \"dialog\") return;\n if (onCloseRequested) {\n try {\n await onCloseRequested();\n } catch {\n }\n }\n unmount();\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || mode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n void requestDialogClose();\n } catch {\n }\n }\n function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 \" + JSON.stringify(next) + \"\\uFF09\");\n }\n theme = normalizePanelTheme(next);\n render();\n }\n function setMode(next) {\n if (next !== \"embedded\" && next !== \"dialog\") {\n throw new Error(\"[dsh-plugin-update] \\u6446\\u653E\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 embedded \\u6216 dialog\\uFF08\\u6536\\u5230 \" + JSON.stringify(next) + \"\\uFF09\");\n }\n mode = next;\n render();\n }\n function onClick(ev) {\n try {\n const t = ev;\n const btn = t?.target && typeof t.target.closest === \"function\" ? t.target.closest(\"[data-act]\") : null;\n const kind = btn?.getAttribute ? btn.getAttribute(\"data-act\") : null;\n if (!kind) return;\n if (kind === \"cancel\" && !confirmCancelArmed) {\n confirmCancelArmed = true;\n say(\"\\u518D\\u70B9\\u4E00\\u6B21\\u300C\\u786E\\u8BA4\\u53D6\\u6D88\\u8FD9\\u4E00\\u6279\\u300D\\u624D\\u771F\\u7684\\u53D6\\u6D88\\uFF1B\\u70B9\\u522B\\u7684\\u6309\\u94AE\\u53EF\\u64A4\\u9500\\u8FD9\\u6B21\\u786E\\u8BA4\\u3002\");\n render();\n return;\n }\n if (kind === \"cancel\") confirmCancelArmed = false;\n const key = btn?.getAttribute ? btn.getAttribute(\"data-key\") : null;\n void act(kind, key === null ? void 0 : key);\n } catch {\n }\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n rowAutoSeq++;\n rowInflight.clear();\n try {\n timer.clear(handle);\n } catch {\n }\n try {\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n render();\n try {\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n const timer = getTimer();\n const handle = timer.set(() => {\n void refresh();\n }, pollMs);\n try {\n const h = handle;\n if (h && typeof h.unref === \"function\") h.unref();\n } catch {\n }\n void refresh();\n return { refresh, act, setTheme, setMode, unmount };\n}\nexport {\n BATCH_PANEL_POLL,\n UPDATE_BATCH_PANEL_CSS,\n UPDATE_BATCH_PANEL_D5_CSS,\n batchLedgerCounts,\n batchLedgerText,\n batchQueuedStatus,\n batchRowStatus,\n buildBatchPhoneNames,\n mountUpdateBatchPanel,\n renderBatchPanelHTML\n};\n","// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/entry-batch.ts\nimport { MIN_PANEL_POLL_MS } from \"./config.js\";\nimport { UPDATE_ENTRY_CSS } from \"./entry.js\";\nimport { BILINGUAL_CSS, copyHTML, copyText } from \"./bilingual.js\";\nimport { normalizeLangTag, resolveLang, subscribeLang } from \"./lang.js\";\nimport {\n batchLedgerCounts,\n batchLedgerText,\n buildBatchPhoneNames,\n mountUpdateBatchPanel\n} from \"./panel-batch.js\";\nimport {\n failureCodeOf,\n normalizePanelTheme\n} from \"./panel.js\";\nfunction isObject(value) {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction asRows(value) {\n if (!Array.isArray(value)) return null;\n return value;\n}\nfunction escapeHtml(value) {\n return String(value).replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\nfunction batchEntrySummary(state, lang) {\n const rows = state?.rows ?? null;\n const counts = batchLedgerCounts(Array.isArray(rows) ? rows : []);\n const total = Array.isArray(rows) ? rows.length : 0;\n let kind;\n if (!Array.isArray(rows) || rows.length === 0) {\n kind = state?.error ? \"failed\" : \"idle\";\n } else if (counts.installing > 0) {\n kind = \"busy\";\n } else if (counts.failed > 0) {\n kind = \"failed\";\n } else if (counts.restart > 0) {\n kind = \"restart\";\n } else if (counts.updatable > 0) {\n kind = \"update\";\n } else {\n kind = \"idle\";\n }\n if (kind !== \"busy\" && state?.error) {\n if (Array.isArray(rows) && rows.length > 0 && counts.failed === 0 && counts.installing === 0) {\n kind = \"failed\";\n }\n }\n const hasUpdate = counts.installing > 0 || counts.failed > 0 || counts.restart > 0 || counts.updatable > 0;\n const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();\n let label;\n switch (kind) {\n case \"busy\":\n label = copyText(\"batch-entry.label.busy\", l);\n break;\n case \"failed\":\n label = counts.failed > 0 ? copyText(\"batch-entry.label.failed\", l, { count: String(counts.failed) }) : copyText(\"entry.label.failed\", l);\n break;\n case \"restart\":\n label = copyText(\"batch-entry.label.restart\", l, { count: String(counts.restart) });\n break;\n case \"update\":\n label = copyText(\"batch-entry.label.update\", l, { count: String(counts.updatable) });\n break;\n default:\n label = copyText(\"batch-entry.label.idle\", l);\n break;\n }\n return {\n kind,\n label,\n updatable: counts.updatable,\n installing: counts.installing,\n pending: counts.pending,\n restart: counts.restart,\n failed: counts.failed,\n skipped: counts.skipped,\n settled: counts.settled,\n total,\n hasUpdate\n };\n}\nfunction batchEntryStateKind(state) {\n return batchEntrySummary(state).kind;\n}\nfunction batchEntryLabelFor(state, lang) {\n return batchEntrySummary(state, lang).label;\n}\nfunction batchEntryBilingualKeyFor(state) {\n const s = batchEntrySummary(state);\n switch (s.kind) {\n case \"busy\":\n return \"batch-entry.label.busy\";\n case \"failed\":\n return s.failed > 0 ? \"batch-entry.label.failed\" : \"entry.label.failed\";\n case \"restart\":\n return \"batch-entry.label.restart\";\n case \"update\":\n return \"batch-entry.label.update\";\n default:\n return \"batch-entry.label.idle\";\n }\n}\nfunction batchEntryBilingualValuesFor(state) {\n const s = batchEntrySummary(state);\n switch (s.kind) {\n case \"update\":\n return { count: String(s.updatable) };\n case \"restart\":\n return { count: String(s.restart) };\n case \"failed\":\n return s.failed > 0 ? { count: String(s.failed) } : {};\n default:\n return {};\n }\n}\nfunction batchEntryBilingualHTMLFor(state, lang) {\n const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();\n return copyHTML(batchEntryBilingualKeyFor(state), l, batchEntryBilingualValuesFor(state));\n}\nfunction batchEntryBilingualTextFor(state, lang) {\n const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();\n return copyText(batchEntryBilingualKeyFor(state), l, batchEntryBilingualValuesFor(state));\n}\nfunction mountUpdateBatchEntry(container, options) {\n if (!container || typeof container.innerHTML !== \"string\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u6279\\u91CF\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4E00\\u4E2A\\u6709 innerHTML \\u7684\\u5BB9\\u5668\");\n }\n if (!options || typeof options !== \"object\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u6279\\u91CF\\u5165\\u53E3\\u4EF6\\u7F3A\\u5C11\\u914D\\u7F6E\\uFF1A\\u6279\\u91CF\\u7535\\u8BDD\\u524D\\u7F00 prefix \\u5FC5\\u586B\");\n }\n if (typeof options.call !== \"function\") {\n throw new Error(\"[dsh-plugin-update] \\u6302\\u6279\\u91CF\\u5165\\u53E3\\u4EF6\\u9700\\u8981\\u4F20\\u8F93\\u51FD\\u6570 call\\uFF08\\u5165\\u53E3\\u4EF6\\u8C03\\u5BBF\\u4E3B\\u7535\\u8BDD\\u7684\\u552F\\u4E00\\u63A5\\u89E6\\u9762\\uFF09\");\n }\n const phones = buildBatchPhoneNames(options.prefix);\n const variant = options.variant ?? \"button\";\n if (variant !== \"button\" && variant !== \"badge\" && variant !== \"inline\") {\n throw new Error(\"[dsh-plugin-update] \\u5165\\u53E3\\u4EF6\\u5F62\\u6001\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 button / badge / inline\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.variant) + \"\\uFF09\");\n }\n const autoCheck = options.autoCheck ?? \"mount\";\n if (autoCheck !== \"mount\" && autoCheck !== \"never\") {\n throw new Error(\"[dsh-plugin-update] \\u81EA\\u52A8\\u68C0\\u67E5\\u65F6\\u673A\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 mount \\u6216 never\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.autoCheck) + \"\\uFF09\");\n }\n const openOn = options.openOn ?? \"always\";\n if (openOn !== \"has-update\" && openOn !== \"always\" && openOn !== \"manual\" && openOn !== \"direct\") {\n throw new Error(\"[dsh-plugin-update] \\u70B9\\u51FB\\u53BB\\u5411\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 has-update / always / manual / direct\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.openOn) + \"\\uFF09\");\n }\n if (options.theme !== void 0 && options.theme !== \"default\" && options.theme !== \"archive\" && options.theme !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.theme) + \"\\uFF09\");\n }\n let theme = normalizePanelTheme(options.theme ?? \"default\");\n const pollMs = options.pollMs;\n if (pollMs !== void 0 && (typeof pollMs !== \"number\" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS)) {\n throw new Error(\"[dsh-plugin-update] \\u9762\\u677F\\u8F6E\\u8BE2\\u95F4\\u9694\\u975E\\u6CD5\\uFF1A\\u4E0D\\u5F97\\u5C0F\\u4E8E 250 \\u6BEB\\u79D2\\uFF08\\u6536\\u5230 \" + JSON.stringify(options.pollMs) + \"\\uFF09\");\n }\n const labelOverride = typeof options.label === \"string\" && options.label ? options.label : null;\n const localeOpt = options.locale ?? void 0;\n if (localeOpt !== void 0 && localeOpt !== null) {\n const isStr = typeof localeOpt === \"string\";\n const isObj = typeof localeOpt === \"object\" && typeof localeOpt.getActive === \"function\";\n if (!isStr && !isObj) {\n throw new Error(\"[dsh-plugin-update] invalid locale: expected zh / en / BCP47 or { getActive(), subscribe? }\");\n }\n if (isStr && !localeOpt.trim()) {\n throw new Error(\"[dsh-plugin-update] invalid locale: empty string\");\n }\n }\n function currentLang() {\n return resolveLang(localeOpt);\n }\n const titles = options.titles && typeof options.titles === \"object\" ? options.titles : {};\n const onActivate = typeof options.onActivate === \"function\" ? options.onActivate : null;\n const call = options.call;\n const ENTRY_ATTR = \"data-dsh-upd-entry\";\n const ENTRY_SELECTOR = \"[data-dsh-upd-entry]\";\n let rows = null;\n let error = null;\n let note = null;\n let loaded = false;\n let activating = false;\n let mounted = true;\n let panel = null;\n let panelMode = null;\n const panelHost = {\n get innerHTML() {\n return container.innerHTML;\n },\n set innerHTML(value) {\n container.innerHTML = value;\n },\n addEventListener(type, listener) {\n ;\n container.addEventListener?.(type, listener);\n },\n removeEventListener(type, listener) {\n ;\n container.removeEventListener?.(type, listener);\n }\n };\n async function panelCall(name, args) {\n const reply = await call(name, args);\n return isObject(reply) ? reply : {};\n }\n function stateOf() {\n return { rows, error };\n }\n function summaryOf() {\n return batchEntrySummary(stateOf(), currentLang());\n }\n function currentLabel() {\n return labelOverride ?? summaryOf().label;\n }\n function phoneArgs() {\n return {};\n }\n function entryHTML() {\n const lang = currentLang();\n const summary = batchEntrySummary(stateOf(), lang);\n const labelHTML = labelOverride ? escapeHtml(labelOverride) : activating ? copyHTML(\"batch-entry.action.checking\", lang) : copyHTML(batchEntryBilingualKeyFor(stateOf()), lang, batchEntryBilingualValuesFor(stateOf()));\n const labelText = labelOverride ?? (activating ? copyText(\"batch-entry.action.checking\", lang) : copyText(batchEntryBilingualKeyFor(stateOf()), lang, batchEntryBilingualValuesFor(stateOf())));\n const busyAttr = activating ? ' disabled aria-busy=\"true\"' : \"\";\n const themeAttr = theme === \"archive\" ? ' data-theme=\"archive\"' : \"\";\n const control = variant === \"badge\" ? '<button type=\"button\" class=\"dsh-upd-entry-dot\" ' + ENTRY_ATTR + '=\"activate\" title=\"' + escapeHtml(labelText) + '\" aria-label=\"' + escapeHtml(labelText) + '\"' + busyAttr + \"></button>\" : '<button type=\"button\" class=\"dsh-upd-entry-btn\" ' + ENTRY_ATTR + '=\"activate\"' + busyAttr + \">\" + labelHTML + \"</button>\";\n const noteHTML = note ? '<span class=\"dsh-upd-entry-note\" data-dsh-upd-note=\"1\">' + escapeHtml(note) + \"</span>\" : \"\";\n return \"<style>\" + UPDATE_ENTRY_CSS + \"\\n\" + BILINGUAL_CSS + '</style>\\n<span class=\"dsh-upd-entry\" data-variant=\"' + variant + '\" data-state=\"' + summary.kind + '\"' + themeAttr + \">\" + control + noteHTML + \"</span>\";\n }\n function render() {\n if (!mounted) return;\n if (panelMode !== null) return;\n container.innerHTML = entryHTML();\n }\n function applyReply(reply) {\n if (!isObject(reply) || reply[\"ok\"] !== true) {\n error = failureCodeOf(isObject(reply) ? reply : null, \"check-failed\");\n return;\n }\n const next = asRows(reply[\"rows\"]);\n if (next) rows = next;\n error = null;\n loaded = true;\n }\n async function refresh() {\n if (!mounted) return;\n try {\n const reply = await call(phones.status, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n render();\n }\n async function checkNow() {\n try {\n const reply = await call(phones.check, phoneArgs());\n if (!mounted) return;\n applyReply(reply);\n } catch {\n if (!mounted) return;\n error = \"check-failed\";\n }\n }\n function mountPanel(mode) {\n if (!mounted || panelMode !== null) return;\n panel = mountUpdateBatchPanel(panelHost, {\n prefix: options.prefix,\n mode,\n theme,\n pollMs,\n titles,\n onRestartRequested: options.onRestartRequested,\n copyText: options.copyText,\n autoChangelog: options.autoChangelog,\n locale: localeOpt,\n // 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。\n onCloseRequested: () => close(),\n call: panelCall\n });\n panelMode = mode;\n }\n function openDialog() {\n mountPanel(\"dialog\");\n }\n function open() {\n if (!mounted || panelMode !== null) return;\n if (variant === \"inline\") return;\n openDialog();\n }\n function close() {\n if (!mounted || panelMode !== \"dialog\" || !panel) return;\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened.unmount();\n } catch {\n }\n render();\n void refresh();\n }\n async function activate() {\n if (!mounted || panelMode !== null || activating) return;\n if (openOn === \"direct\" && variant === \"button\") {\n openDialog();\n return;\n }\n activating = true;\n note = null;\n render();\n try {\n await checkNow();\n } finally {\n activating = false;\n }\n if (!mounted) return;\n if (variant === \"badge\" || openOn === \"manual\") {\n if (onActivate) onActivate(summaryOf());\n else openDialog();\n render();\n return;\n }\n const summary = summaryOf();\n if (openOn === \"always\" || summary.hasUpdate || !loaded || error) {\n openDialog();\n return;\n }\n note = batchLedgerText({\n updatable: summary.updatable,\n installing: summary.installing,\n pending: summary.pending,\n restart: summary.restart,\n failed: summary.failed,\n skipped: summary.skipped,\n settled: summary.settled\n });\n render();\n }\n function setTheme(next) {\n if (next !== \"default\" && next !== \"archive\" && next !== \"d5-paper\") {\n throw new Error(\"[dsh-plugin-update] \\u4E3B\\u9898\\u975E\\u6CD5\\uFF1A\\u53EA\\u6536 default \\u6216 archive\\uFF08d5-paper \\u4E3A\\u65E7\\u522B\\u540D\\u4ECD\\u53EF\\u7528\\uFF09\\uFF08\\u6536\\u5230 \" + JSON.stringify(next) + \"\\uFF09\");\n }\n theme = normalizePanelTheme(next);\n if (panel) void panel.setTheme(theme);\n else render();\n }\n function onClick(ev) {\n if (!mounted) return;\n try {\n const target = ev?.target;\n const closest = target && typeof target.closest === \"function\" ? target.closest.bind(target) : null;\n if (!closest) return;\n if (closest(ENTRY_SELECTOR)) {\n void activate();\n return;\n }\n } catch {\n }\n }\n function onKeyDown(ev) {\n try {\n const e = ev;\n if (!mounted || panelMode !== \"dialog\" || !e || e.key !== \"Escape\") return;\n close();\n } catch {\n }\n }\n function unmount() {\n if (!mounted) return;\n mounted = false;\n try {\n unsubLang();\n } catch {\n }\n const opened = panel;\n panel = null;\n panelMode = null;\n try {\n opened?.unmount();\n } catch {\n }\n try {\n ;\n container.removeEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n ;\n container.removeEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n }\n const unsubLang = subscribeLang(() => {\n render();\n }, localeOpt);\n try {\n ;\n container.addEventListener?.(\"click\", onClick);\n } catch {\n }\n try {\n ;\n container.addEventListener?.(\"keydown\", onKeyDown);\n } catch {\n }\n if (variant === \"inline\") mountPanel(\"embedded\");\n else render();\n if (autoCheck === \"mount\") void refresh();\n return { refresh, open, close, label: currentLabel, summary: summaryOf, setTheme, unmount };\n}\nexport {\n batchEntryBilingualHTMLFor,\n batchEntryBilingualKeyFor,\n batchEntryBilingualTextFor,\n batchEntryBilingualValuesFor,\n batchEntryLabelFor,\n batchEntryStateKind,\n batchEntrySummary,\n mountUpdateBatchEntry\n};\n","/** dsh-life-pack 安装口径(P10 脚手架,P1 #2 定案单命令双包)。\n *\n * 真相(#2 真机结论):dsh plugin 是 pnpm 薄转发,add 单品等价 pnpm add,\n * 传递依赖落盘是;激活否:reconcile 只扫直接 dependencies,传递的 manager\n * 不进 bundles;pnpm 隔离布局下从 profile 解析不到它。\n *\n * 口径:任何单品落地时总管必须已在位导航。实现为单命令双包:\n * dsh plugin add dsh-life-pack dsh-calorie\n * 两者皆直接依赖,由 reconcile 按序激活。单品 package 内保留对总管的\n * dependencies 只作本地开发兜底,不作为单 add 即激活的依据。\n * 不许单卸总管:缺席时总管对应 tab 显示推荐安装并提示补装(见 nav.ts)。\n * 安装验收以 bundles 双含为准,另加单 add 单品后 bundles 无总管的负向断言。\n */\n\nexport const MANAGER_PACKAGE = 'dsh-life-pack' as const;\n\nexport const SINGLE_PLUGINS: readonly string[] = [\n 'dsh-memo-ilife',\n 'dsh-calorie',\n 'dsh-schedule-ilife',\n 'dsh-home-ilife',\n 'dsh-chef',\n 'dsh-bill-ilife',\n];\n\n/** reconcile 语义仿真:只扫直接 dependencies,按序进 bundles(P1 定案)。 */\nexport function reconcileBundles(directDeps: readonly string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const d of directDeps) {\n if (!seen.has(d)) {\n seen.add(d);\n out.push(d);\n }\n }\n return out;\n}\n\nexport function assertDualBundles(bundles: readonly string[], single: string): void {\n if (!bundles.includes(MANAGER_PACKAGE)) {\n throw new Error('bundles 缺总管[' + MANAGER_PACKAGE + ']:请用单命令双包补装:dsh plugin add dsh-life-pack ' + single);\n }\n if (!bundles.includes(single)) {\n throw new Error('bundles 缺单品[' + single + ']');\n }\n}\n\nexport function assertNegativeSingleOnly(bundles: readonly string[]): void {\n if (bundles.includes(MANAGER_PACKAGE)) {\n throw new Error('负向断言失败:单 add 单品时 bundles 不应含总管(传递依赖不激活,P1 #2)');\n }\n}\n","/** 七个更新目标(总管自己 + 六个单品),薄接线唯一定义地(票 1170)。\n *\n * 包名不新开第二源:六单品取自 nav.ts 的 MANAGER_TABS,总管自己取自 install.ts。\n * 七个 phonePrefix 必须互不相同,否则串台。\n */\nimport { MANAGER_PACKAGE } from './install.js';\nimport { MANAGER_TABS } from './nav.js';\n\nexport interface UpdateTarget {\n readonly key: string;\n readonly title: string;\n readonly packageName: string;\n readonly phonePrefix: string;\n}\n\nexport const MANAGER_TARGET_KEY = 'life-pack' as const;\n\n/** 批量电话前缀:宿主与面板共用,唯一定义地(面板 mount 参数与宿主登记同源)。 */\nexport const BATCH_PREFIX = 'life' as const;\n\nexport const UPDATE_TARGETS: readonly UpdateTarget[] = [\n { key: MANAGER_TARGET_KEY, title: '爱生活', packageName: MANAGER_PACKAGE, phonePrefix: 'ilife-life-pack' },\n ...MANAGER_TABS.map((tab) => ({\n key: tab.skill,\n title: tab.title,\n packageName: tab.plugin,\n phonePrefix: 'ilife-' + tab.skill,\n })),\n];\n\nexport function targetFor(packageName: string): UpdateTarget | undefined {\n return UPDATE_TARGETS.find((target) => target.packageName === packageName);\n}\n","/** 面板薄包装(票 1170 起,浏览器侧:无 node 内建,可进 client 束;完全上游 UI,本包零自家样式)。\n *\n * 只定死三组参数,不自建文案与样式:批量入口形态 button、皮肤 archive、打开策略沿上游缺省 always\n *(查完总是开批量面板;0.5.4 #49 到达,manual 桥接已删);弹窗 dialog 由入口件内置(含关闭落地);\n * 轮询与更新日志沿上游缺省,中文标题沿目标表(不传 titles 覆盖)。\n */\nimport { mountUpdateBatchEntry } from 'dsh-plugin-update/entry-batch';\nimport type { UpdateBatchEntryController, UpdateBatchEntryOptions } from 'dsh-plugin-update/entry-batch';\nimport type { BatchPanelContainer } from 'dsh-plugin-update/panel-batch';\nimport { MANAGER_RPC } from './update-contract.js';\nimport { BATCH_PREFIX } from './update-targets.js';\nimport type { RpcCallFace } from './dsh-ctx.js';\n\n/** 面板调宿主: upstream 面板唯一的宿主接触面(电话名 + 参数)。 */\nexport type LifePanelCall = (name: string, args: Record<string, unknown>) => Promise<unknown>;\n\n/** 把 RpcCallFace 收成面板要的一参形状:上游回包原样透传(含失败回包的 errorKind 与 diag)。\n *\n * 宿主电话表把上游回包整体装进回执 value(成功失败皆如此,只有宿主级故障才走外层失败信封),\n * 故这里只拆信封、不重组回包:value 里有 ok 布尔即上游原文,原样交面板。\n */\nexport function managerCallAdapter(call: RpcCallFace | null): LifePanelCall {\n return async (name: string, args: Record<string, unknown>): Promise<unknown> => {\n if (typeof call !== 'function') return { ok: false, error: 'manager-unreachable', errorKind: 'manager-unreachable' };\n let result: unknown;\n try {\n result = await call(MANAGER_RPC.base, MANAGER_RPC.endpoint, { method: name, payload: args });\n } catch {\n return { ok: false, error: 'manager-unreachable', errorKind: 'manager-unreachable' };\n }\n if (typeof result !== 'object' || result === null || (result as { ok?: unknown }).ok !== true) {\n const problem = (result as { error?: { code?: unknown; details?: unknown } } | null)?.error;\n const code = typeof problem?.code === 'string' && problem.code.length > 0 ? problem.code : 'manager-unreachable';\n const details = (problem?.details as Record<string, unknown> | undefined) ?? {};\n const kind = typeof details.errorKind === 'string' && (details.errorKind as string).length > 0\n ? (details.errorKind as string)\n : code;\n const raw: Record<string, unknown> = { ok: false, error: code, errorKind: kind };\n if (details.diag !== undefined) raw.diag = details.diag;\n return raw;\n }\n const value = (result as { value?: unknown }).value;\n if (typeof value === 'object' && value !== null && typeof (value as { ok?: unknown }).ok === 'boolean') return value;\n return { ok: false, error: 'internal', errorKind: 'internal' };\n };\n}\n\n/** 挂批量入口(一颗按钮看七家聚合,弹窗 dialog 由入口件内置,关闭落地自带)。\n *\n * 0.5.4 #49 到达:`prefix: life` 直调批量五电话,徽标与面板总账同一份数法;manual 桥接与容器事件同步已整段删除。\n */\nexport function mountLifeBatchEntry(\n container: BatchPanelContainer,\n call: LifePanelCall,\n overrides?: Partial<Pick<UpdateBatchEntryOptions, 'autoCheck' | 'label'>> | undefined,\n): UpdateBatchEntryController {\n return mountUpdateBatchEntry(container, {\n prefix: BATCH_PREFIX,\n call,\n variant: 'button',\n theme: 'archive',\n autoCheck: overrides?.autoCheck ?? 'mount',\n label: overrides?.label,\n });\n}\n","/** 共用配置面板 · 契约:行表形状、配置面回执、几条两侧共用的常量。\n *\n * 为什么住这里:六个单品插件的设置页各有一份逐字相同的那套代码(卡片骨架/行渲染/样式表/\n * 状态机/配置面三通电话约 3105 行)。本件是抽件之后的**唯一一处定义地**。\n *\n * 本件不认识任何一家能力:行表由调用方给,分支只能由行表数据与配置面回执决定,\n * 里面不出现任何一个能力的名字(结构纪律「依赖方向」)。\n *\n * 对外只经 `config-panel-api.ts` 那一道门;本件自己不出门。\n */\n\n/** 分级:常用项直接画在页面上,其余进默认收起的「高级」组。 */\nexport type ConfigTier = 'common' | 'advanced';\n\n/** 控件种类:只有这四种(目录是**字符串那一档的页面形态**——取值仍是串,只是多一个选目录的入口)。 */\nexport type ConfigControl = 'text' | 'number' | 'switch' | 'directory';\n\n/** 设置页的一行:一个可配置项一行,写清它在配置文件里的键、页面上那一行的中文标题、分级、\n * 控件种类、只读与否与人话指引。它是页面渲染的唯一依据(各家自己的那份住各自的 `settings.ts`)。 */\nexport interface ConfigItem {\n /** 配置文件里的键路径,一层嵌套用 `.` 连接,例 `backup.dir`。 */\n readonly key: string;\n /** 页面上那一行的中文标题。 */\n readonly title: string;\n readonly tier: ConfigTier;\n readonly control: ConfigControl;\n /** 一行人话:这一项管什么、留空会怎样(一到两句,不写开发期口径)。 */\n readonly hint: string;\n /** **只读行**:页面上不给改,值只经技能侧解析后显示;改它要编辑配置文件。\n * 只读行**不参与保存**(填值时跳过它,免得把显示用的绝对路径当成配置值写回去)。 */\n readonly readonly?: boolean;\n /** 只读行的**显示值来源**:回执 `resolved` 组里的哪一格。标了它 ⇒ 显示技能算好的绝对路径;\n * 不标 ⇒ 显示配置文件里那个值本身(数字类只读项走这一档)。 */\n readonly resolveFrom?: string;\n /** 可改行的**落点来源**:这一行取值空着时,页面上拿回执里的哪一格当它显示的绝对路径。\n * 取法先看回执 `resolved` 组同名格,再看回执顶层同名格(如 `dataDir`)——两种写法收成同一格。 */\n readonly prefillFrom?: string;\n}\n\n/** 配置面整面回执:形状与各技能侧 `<技能>.config.read` 的 data 逐字段同形。\n *\n * 技能侧多带的格(各家自己的那几格)不写在这里:各家在附加块里自己认(回执形状的唯一定义地\n * 在各家的 `contract.ts`)。本件只认下面这几格,因此里面不出现任何一个能力的名字。 */\nexport interface ConfigSurfaceReply {\n /** 配置文件绝对路径。 */\n readonly path: string;\n /** 数据目录绝对路径。 */\n readonly dataDir: string;\n /** 本次是不是「文件不存在、按默认值落了一份」。 */\n readonly created: boolean;\n /** 当前取值(文件里的缺项按默认值补)。 */\n readonly values: Record<string, unknown>;\n /** 一组解析后的绝对路径,给只读行显示用;**可选**:装的是旧技能时这一格缺席。 */\n readonly resolved?: Readonly<Record<string, string | undefined>>;\n /** 按行键给的告警(#915 第二步):**只有技能侧真判出「这一格用不了」时才有这一格**;\n * 缺席=这一行没问题,面板不亮任何东西。面板只原样画 `message`,不比较、不合成新句子。 */\n readonly alerts?: Readonly<Record<string, ConfigRowAlert>> | undefined;\n}\n\n/** 技能侧报出的「这一格真用不了」:一句人话 + 故障码(面板只画 `message`,`code` 给判据与回执用)。 */\nexport interface ConfigRowAlert {\n readonly code: string;\n readonly message: string;\n}\n\n/** 面板对配置面的三通电话:读整面/保存一份取值/重置为默认。三个名字各家逐字相同,收在这里一处。 */\nexport const RPC_ENDPOINT_CONFIG_GET = 'config.get' as const;\nexport const RPC_ENDPOINT_CONFIG_SAVE = 'config.save' as const;\nexport const RPC_ENDPOINT_CONFIG_RESET = 'config.reset' as const;\n\n/** 高级组标题(六家逐字相同,收在这里一处)。\n *\n * #934:原先还有一条副文案 `ADVANCED_GROUP_NOTE`(「不常改。留空=用默认值。」),\n * 维护者裁定**整条删除、删后不另找地方说**——理由:三家高级项全是只读行、不参与保存、根本没有\"留空\",\n * 而\"留空会怎样\"这条口径本就逐行住在每条的 `hint` 里(见本件 `ConfigItem.hint` 的契约)。 */\nexport const ADVANCED_GROUP_TITLE = '高级' as const;\n\n/** 请求超时毫秒:与宿主侧 SPAWN_TIMEOUT_MS 同级,界面永不无限转圈。 */\nexport const READ_TIMEOUT_MS = 20_000 as const;\n\n/** 快路径首帧有界重试表(#1142,照 #982 更新目标表同值:瞬时未就绪不再整面失败)。\n *\n * 有界、调用方负责卸载清理(见 `config-panel.ts` 的取数),不是无界轮询;仅读整面用,保存/重置永不自动重试。 */\nexport const CONFIG_READ_RETRY_MS: readonly number[] = [1000, 3000, 8000];\n\n/** 复制那一枚按钮就地变「已复制」之后,多久变回「复制」(定稿 v3 第三条:终值 900ms,不弹提示)。\n *\n * #1162 差值终结:由现状 1500 切到终值 900(真相源 #1159 参数表+原型 `},900)`);老期望变红属预期,见 #1162。 */\nexport const COPY_FEEDBACK_MS = 900 as const;\n\n/** T1 交互 tokens(#1160,唯一定义地,真相源 #1159 参数表)。\n *\n * 按压 .97、焦点隔离 2px+主题色 5px、过渡 .15s——取值逐字照原型 V1\n *(`.scratch/1155-real-panel-prototype.html`:`btn:active{transform:scale(.97)}`、\n * `btn:focus-visible{box-shadow:0 0 0 2px #0b0b0d,0 0 0 5px var(--focus)}`、\n * 主题橙 #f6ad55/蓝 #0a84ff/绿 #30d158、过渡 background/transform/box-shadow/border-color 各 .15s)。\n *\n * 颜色全走主题变量、永不写死蓝:隔离环取卡面底(深浅自适应),焦点环取\n * `--ilife-focus`(缺席时退到宿主品牌色,宿主品牌色本身跟主题走)。\n * 悬停/长按/折叠/复制变勾/提示/液态归后票,本件只定按压与焦点四格。 */\nexport const PRESS_SCALE = 0.97 as const;\nexport const FOCUS_RING_GAP_PX = 2 as const;\nexport const FOCUS_RING_WIDTH_PX = 5 as const;\nexport const INTERACTION_TRANSITION = 'background .15s ease,transform .15s ease,box-shadow .15s ease,border-color .15s ease' as const;\n\n/** T2 指针与按住 tokens(#1161,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 悬停:聚光半径 120、淡洗 13%、辉光内圈 55%+外发光 25%、上浮 1px——逐字照原型 V1\n *(`.scratch/1155-real-panel-prototype.html`:`.btn:hover{background:color-mix(…13%…);…box-shadow:0 0 0 1px …55%…,0 4px 16px …25%…;transform:translateY(-1px)}`、\n * `::before{background:radial-gradient(120px circle at var(--mx) var(--my),…22%…,transparent 65%)}`)。\n * 页签 12%/高级行 10%不归本票(页签见 #1165,高级见 #1162),这里只定按钮 13% 一档。\n *\n * 禁用:摇头 ±2px/.3s、禁止指针、不透明度 .4——照原型(`shakeV1`+`.btn:disabled{cursor:not-allowed;opacity:.4}`)。\n *\n * 长按:500ms 纯视觉环(底边 3px 主题色条,`:active` 驱动 0→100%,永不触发动作、不进状态机分支)+\n * 回弹(沿用 T1 按压释放 .15s 回弹,不另起回弹曲线)。原型环色 `#30d158` 在生产侧换成主题变量。\n *\n * 颜色全走主题变量、永不写死蓝:悬停/辉光/聚光/环一律取 `--ilife-focus` 退品牌色。\n * 触屏降级与减少动态无静态常量(媒体查询即定义,见视图 `interactionCss`)。 */\nexport const HOVER_SPOTLIGHT_RADIUS_PX = 120 as const;\nexport const HOVER_WASH_PERCENT = 13 as const;\nexport const HOVER_GLOW_EDGE_PERCENT = 55 as const;\nexport const HOVER_GLOW_SOFT_PERCENT = 25 as const;\nexport const HOVER_LIFT_PX = 1 as const;\nexport const SPOTLIGHT_PEAK_PERCENT = 22 as const;\nexport const SPOTLIGHT_FADE_MS = 180 as const;\nexport const DISABLED_OPACITY = 0.4 as const;\nexport const DISABLED_SHAKE_MS = 300 as const;\nexport const LONGPRESS_MS = 500 as const;\nexport const LONGPRESS_RING_HEIGHT_PX = 3 as const;\n\n/** T3 展开与结果 tokens(#1162,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 高级组:220ms 分级展开(`.advbody{transition:max-height .22s ease,opacity .22s ease}`,\n * 开态 cap 300px 即 `.open{max-height:300px;opacity:1}`;原生 details 开关离散、过渡跑不起来,\n * 故生产侧取等价形态 `ilifeFoldIn` 关键帧 0→300+0→1 同 .22s)+箭头旋转\n * (`.chev{transition:transform .2s}`、开态 `rotate(90deg)`)+悬停箭头右移 2px 并跟焦点色\n * (`:hover .chev{translateX(2px)}`)。页签 12%/按钮 13% 淡洗各归其票,这里只定高级头。\n *\n * 轻提示:渐显 .18s(`.toast{transition:opacity .18s ease,transform .18s ease}`,\n * 入场位移 8px 即 `translateY(8px)→0`;生产侧 `notice` 条件挂载,进场即跑 `ilifeToastIn`)。\n *\n * 复制变勾只切时长(字面「已复制」由 #920/#908 锁死不改,见 #1162 进展)。\n * 液态拉伸影子消散与时长公式归 #1165(本工作区已落地 LIQUID_*,本票不另起第二套);\n * 低端机模糊降级无静态常量(`@media (update: slow)` 即定义,见两处 CSS 函数尾部)。\n * 颜色全走主题变量、永不写死蓝。 */\nexport const ADVANCED_OPEN_MS = 220 as const;\nexport const ADVANCED_CHEV_MS = 200 as const;\nexport const ADVANCED_CHEV_SHIFT_PX = 2 as const;\nexport const ADVANCED_OPEN_MAX_PX = 300 as const;\nexport const NOTICE_FADE_MS = 180 as const;\nexport const NOTICE_RISE_PX = 8 as const;\n\n/** T6 页签与液态 tokens(#1165,唯一定义地,真相源 #1159 参数表+原型 V1)。\n *\n * 页签悬停淡洗 12%(原型 V1 `.tab:hover{background:color-mix(…12%…)}`,按钮 13% 不串用);\n * 圆点呼吸 .8s(`dotPulseV1`:scale 1→1.5、opacity 1→.6,悬停驱动);\n * 液态彗星式:拉伸 1.28/.86(45% 处)+影子 180ms 消散(ghost opacity .18s),\n * 滑移时长 200+距离×0.35、夹 220–380ms、缓动 cubic-bezier(.3,1.1,.4,1)(原型 `liquidTo` 原式);\n * 页签过渡只 background+border-color 各 .15s(不带 transform:页签不收缩,滑移由 glider 走)。\n * 颜色全走主题变量(`--ilife-focus` 退品牌色、缺席退橙,永不写死蓝);触屏/减少动态无静态常量(媒体查询即定义,见视图 `tabInteractionCss`)。\n * 共用位第二个用法:配置面板按钮(T1/T2)+爱生活页签条(本票)共用同一契约。 */\nexport const TAB_HOVER_WASH_PERCENT = 12 as const;\nexport const TAB_DOT_PULSE_MS = 800 as const;\nexport const TAB_TRANSITION = 'background .15s ease,border-color .15s ease' as const;\nexport const LIQUID_STRETCH_X = 1.28 as const;\nexport const LIQUID_STRETCH_Y = 0.86 as const;\nexport const LIQUID_GHOST_MS = 180 as const;\nexport const LIQUID_MIN_MS = 220 as const;\nexport const LIQUID_MAX_MS = 380 as const;\nexport const LIQUID_BASE_MS = 200 as const;\nexport const LIQUID_DIST_FACTOR = 0.35 as const;\nexport const LIQUID_EASE = 'cubic-bezier(.3,1.1,.4,1)' as const;\n\n/** 宿主给「这是哪一种目录选择能力」时用的那两格原语的名字(软依赖:按名字现取,缺席即没有入口)。 */\nexport const REMOTE_DIRECTORY_PICKER = 'remote.directoryPicker' as const;\n\n/** 宿主在「这条路不给」时回的那个码(`directory-browser-state` 的 `openRowBrowser` 只照比不认名)。 */\nexport const DIRECTORY_PICKER_REFUSED = 'directory-picker/unavailable' as const;\n\n/** 一次配置电话的失败格。 */\nexport interface RpcErrorFace {\n readonly code?: string;\n readonly message?: string;\n}\n\n/** 配置电话的信封(收或拒,永不抛)。 */\nexport type RpcReply =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error?: RpcErrorFace };\n\n/** 信封守卫:拆包前校验(信封形状的定义地在这里)。 */\nexport function isRpcResult(raw: unknown): raw is RpcReply {\n if (typeof raw !== 'object' || raw === null) return false;\n return (raw as { ok?: unknown }).ok === true || (raw as { ok?: unknown }).ok === false;\n}\n\n/** 印章角色:skill=技能横条/help=HELP 旧版/plugin=插件双铆(定义只此一处)。 */\nexport type SealRole = 'skill' | 'help' | 'plugin';\n\n/** 印章档位:copper=铜/silver=银/gold=金(定义只此一处)。 */\nexport type SealTier = 'copper' | 'silver' | 'gold';\n\n/** 面板上的一枚签:角色×档位+印文+卷轴三段文案(各家交自家三枚,六家以外不许有第四枚)。 */\nexport interface SealInstance {\n readonly role: SealRole;\n readonly tier: SealTier;\n /** 印文那一行(技能/某 HELP/插件)。 */\n readonly label: string;\n /** 卷轴进展。 */\n readonly progress: string;\n /** 卷轴状态。 */\n readonly status: string;\n /** 卷轴计划。 */\n readonly plan: string;\n}\n","/** 目录浏览器的纯逻辑半(**不认识宿主**:只认两格原语)。\n *\n * 为什么在这里:六个单品插件的设置页各有一批「值是一个目录」的行,它们要的界面是同一个东西。\n * 界面的**取数**来自宿主给的两个原语(`list`/`createDirectory`),而这两个原语的形状是**调用方喂进来的**\n * ——本件不 import 宿主的任何实现,也不认识 `remote.directoryPicker` 这个名字(那两件住在各家的\n * `dsh-ctx.ts` 镜像里)。于是本件在 Windows 桌面、远程浏览器、SSH 三种部署下是同一份代码。\n *\n * 装什么:本次要浏览的那一层怎么走(进子目录/回上一级/跳面包屑)、路径怎么切出「目录部分」与\n * 「筛选词」、回执怎么认。**不装**任何 IO、不装任何持久化——`list`/`createDirectory` 由调用方执行。\n *\n * 与平台自带浏览器件的关系:`@deepseek-ai/dsh-client-ui-directory-picker-browse` 的 `DirectoryBrowser`\n * 是包内私有件(只经 `ui-workspace` 的流程空位暴露),插件侧取不到,所以这里是一份独立实现,\n * 只对齐它的**交互结果**(进/选/新建),不对齐它的 DOM。\n */\n\n/** 一层目录里的一个子目录。`hidden` 由宿主判,本件只照传。 */\nexport interface DirectoryEntry {\n readonly name: string;\n readonly path: string;\n readonly hidden: boolean;\n}\n\n/** 面包屑的一格:从文件系统根到当前层,每一格都是一个可跳转目标。 */\nexport interface DirectoryCrumb {\n readonly name: string;\n readonly path: string;\n readonly hidden: boolean;\n}\n\n/** 一次列举的回执(形状照宿主 browse 原语:`path`/`home`/`crumbs`/`entries`/`truncated`)。 */\nexport interface DirectoryListing {\n readonly path: string;\n readonly home: string;\n readonly crumbs: readonly DirectoryCrumb[];\n readonly entries: readonly DirectoryEntry[];\n readonly truncated: boolean;\n}\n\n/** 取数面:由调用方注入。本件只调这两个方法,别的什么都不调。 */\nexport interface DirectoryBrowseFace {\n list(path?: string, signal?: AbortSignal): Promise<DirectoryListing>;\n createDirectory(path: string, name: string): Promise<string>;\n}\n\n/** 浏览失败的人话(`code` 保留给用例断言,`message` 直接上图)。 */\nexport interface BrowseFailure {\n readonly code: string;\n readonly message: string;\n}\n\n/** 宿主拒了一条路的说法随宿主变化,**本件不认识**:调用方把它的回执码传进 `openRowBrowser`。\n *\n * 它为什么是唯一信号:一次组合里只服务一种能力(native 或 browse),另一条路上的动词一律被拒\n * (`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability`);\n * 而三条动词在客户端命名空间上都在(都由 `TYPERT_REMOTE.descriptors` 生成),\n * 所以「有没有 `pick`/有没有 `list`」问不出组合里服务哪种能力,只有真调一次、看它拒没拒才知道。 */\n\n/** 目录行入口的三态判定结果:**按命名空间上有什么动词**看一眼,不是问组合里服务哪种能力。 */\nexport type PickerMode = 'native' | 'browse' | 'none';\n\n/** 一个「根」是什么类型:界面只拿它写悬停说明,不拿它做任何判断。\n *\n * 这五个值由**取数方**(宿主半)归一:Windows 上是 `DriveInfo.DriveType` 映过来的;\n * 别的根(挂载点等)落 `other`。本件不认识任何宿主名词,只认识这五个值。 */\nexport type RootKind = 'fixed' | 'network' | 'removable' | 'optical' | 'other';\n\n/** 一个可跳转的根:`path` 是绝对路径(Windows 上形如 `D:\\`),`kind` 只用于显示。 */\nexport interface RootRow {\n readonly path: string;\n readonly kind: RootKind;\n}\n\n/** 平台回执的信封(各家的 `DirectoryPickerAnswer` 与此同形;本件只认这三格,不认别家的类名)。 */\nexport interface PickEnvelope {\n readonly ok: boolean;\n readonly value?: string | null;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\n/** 一次「唤起选择器」的归一结果:选中/取消/供不了,永不抛。\n *\n * `code` 只在平台给了的时候带上(就是 `error.code`):调用方据它分辨「宿主没给这条路」与\n * 「别的失败」——前者该换一条路走,后者才该报出来。那个码由调用方从自己的宿主镜像里传进来\n * (见 `openRowBrowser` 的 `refusalCode`),本件不认它的名字。 */\nexport type PickOutcome =\n | { readonly kind: 'picked'; readonly path: string }\n | { readonly kind: 'cancelled' }\n | { readonly kind: 'unavailable'; readonly code?: string; readonly message: string };\n\n/** 认一认某个值是不是可用的目录取数面(软依赖守卫用:认不出就当没有,绝不把页面带下来)。 */\nexport function isBrowseFace(raw: unknown): raw is DirectoryBrowseFace {\n if (typeof raw !== 'object' || raw === null) return false;\n const face = raw as Partial<DirectoryBrowseFace>;\n return typeof face.list === 'function' && typeof face.createDirectory === 'function';\n}\n\n/** 认一认某个值有没有 `pick`(对应宿主 composition 里的 `native` 能力)。 */\nexport function hasPickFn(raw: unknown): boolean {\n if (typeof raw !== 'object' || raw === null) return false;\n return typeof (raw as { pick?: unknown }).pick === 'function';\n}\n\n/** 这次列举失败算不算「宿主没给这条路」(调用方据此换另一条路走)。\n *\n * 为什么码不够:旧宿主被拒回固定回执码(调用方经 `refusalCode` 传进来),新宿主\n * (`dsh 0.2.0-rc.2` 实测 #990)拒 `list` 时没带那个码——`code` 落到兜底值,\n * 只有人话里那句能力说明还在。所以码命中算被拒,人话命中能力句式也算被拒;\n * 人话对不上=这一层真读不出来,不换路。\n *\n * 人话只认两句能力句式(大小写不敏感):`needs the browse/native capability`\n * 与 `cannot serve pick/list/createDirectory`(含组合服务说明变体)。\n * 本件仍不认识宿主名:这里只出现能力词,不出现任何接缝名。 */\nexport function isCapabilityRefusal(code: unknown, message: unknown, refusalCode: string): boolean {\n if (typeof code === 'string' && code !== '' && code === refusalCode) return true;\n if (typeof message !== 'string' || message.trim() === '') return false;\n return (\n /needs the (browse|native) capability/i.test(message) ||\n /cannot serve (pick|list|createDirectory)/i.test(message)\n );\n}\n\n/** 三态判定:**先看两格浏览原语**(都在=应用内浏览器这条路在),再看单独的 `pick`,都没有=`none`。\n *\n * 为什么浏览优先:客户端命名空间上三条动词一定都在(由 `TYPERT_REMOTE.descriptors` 生成,\n * 与组合里服务哪种能力无关),所以两格原语在=这台宿主**可能**给浏览;真调一次才知道给不给,\n * 不给({@link CAPABILITY_REFUSED})再由调用方换系统对话框。反过来先认 `pick` 就会在\n * Desktop 这种「只服务 browse」的宿主上每次都去唤一次系统对话框、被拒一次(#744 实测)。 */\nexport function pickerModeOf(raw: unknown): PickerMode {\n if (isBrowseFace(raw)) return 'browse';\n return hasPickFn(raw) ? 'native' : 'none';\n}\n\n/** 平台回执 → 三态(**永不抛**):成功回的是信封里的 `value` 不是路径;`ok:false` 是「供不了」不是「取消」。\n *\n * 出处:`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`(失败不抛、成功也不回裸值),\n * 第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js:99-103` 就照这个信封拆。\n * 裸串照收(老形状兜底),认不出的形状当「供不了」报出来,不当取消吞掉(#743 的教训)。 */\nexport function readPickAnswer(raw: unknown): PickOutcome {\n if (typeof raw === 'string') return raw.trim() === '' ? { kind: 'cancelled' } : { kind: 'picked', path: raw };\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as PickEnvelope;\n if (answer.ok === true) {\n const value = answer.value;\n return typeof value === 'string' && value.trim() !== '' ? { kind: 'picked', path: value } : { kind: 'cancelled' };\n }\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n return {\n kind: 'unavailable',\n ...(typeof code === 'string' && code !== '' ? { code } : {}),\n message: '打不开系统文件夹对话框' + (detail === '' ? '' : '(' + detail + ')') + ':请直接在框里填绝对路径。',\n };\n}\n\n/** 取数原语失败:带上宿主给的回执码,好让调用方分辨「没这条路」与「这一层读不出来」。\n *\n * `code` 就是 `humanize`(状态那半)读的那一格,人话直接上图。 */\nexport class BrowseAnswerError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = 'BrowseAnswerError';\n this.code = code;\n }\n}\n\n/** 宿主 browse 原语的**信封** → 值,或按信封里的 `error` 抛。\n *\n * 为什么必须走这一道:`list`/`createDirectory` 与 `pick` 一样是 Remote 动词,客户端拿到的是\n * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`)。\n * 把信封当值用,图上就会是一层「读出来了但一行都没有」的空目录(#744 实测),\n * 而不是报错——所以宁可在这里抛,也不许把它当列举结果传下去。\n * 第一方消费方同口径:`@deepseek-ai/dsh-client-ui-workspace/lib/client.js:105-113` 也是这么拆的。 */\nexport function readBrowseAnswer<T>(raw: unknown, what: string): T {\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as {\n ok?: unknown;\n value?: unknown;\n error?: { code?: string; message?: string };\n };\n if (answer.ok === true && answer.value !== undefined && answer.value !== null) return answer.value as T;\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n throw new BrowseAnswerError(\n typeof code === 'string' && code !== '' ? code : 'browse-failed',\n detail === '' ? what + '没有回执' : detail,\n );\n}\n\n/** 这次失败算不算「客户端按参数个数拦的」(与能力被拒、读不出来都不是一回事)。\n *\n * 出处:新宿主客户端按 `values.length` 对描述符的业务参数个数(`signal` 另算,\n * `values.length === 业务参数个数 + 1` 才算带了 signal),对不上就地抛\n * `expected N …argument(s)…, got M`,还没走到服务端。家目录重试只认这一句,\n * 能力句式与读失败一律不碰(它们各有各的去处)。 */\nexport function isArgCountRejection(cause: unknown): boolean {\n const message =\n cause instanceof Error ? cause.message : typeof cause === 'string' ? cause : '';\n return /expected \\d+ .*argument/i.test(message);\n}\n\n/** 宿主命名空间 → 本件的取数面:把两条原语的信封拆开,成功给值、失败按码抛。\n *\n * 命名空间上两格原语不在就回 null(调用方据此不画入口)。**透传参数个数照宿主的规矩**:\n * 描述符声明了可选 `AbortSignal`(`cancellation: {parameter:'signal'}`),而客户端按\n * `values.length === 业务参数个数 + 1` 判有没有 signal——旧宿主的家目录要零参直调,\n * 多传一个 `undefined` 会被当成 signal 去 `AbortSignal.any([…, undefined])` 而炸。\n * 本件的界面不做取消,故**从不传 signal**。\n *\n * 家目录多一档兼容:新宿主的 `list` 描述符带一个业务参数(接受 `undefined`,\n * 服务端 `undefined` 即回家),零参会被客户端按个数直接拦下。于是家目录先零参试一次,\n * 是个数问题再补一个显式 `undefined` 重试;有参调用与旧语义逐字相同,不多试。 */\nexport function browseFaceOf(raw: unknown): DirectoryBrowseFace | null {\n if (!isBrowseFace(raw)) return null;\n const namespace = raw as DirectoryBrowseFace;\n return {\n list: async (path) => {\n if (path !== undefined) {\n return readBrowseAnswer<DirectoryListing>(await namespace.list(path), '列举目录');\n }\n try {\n return readBrowseAnswer<DirectoryListing>(await namespace.list(), '列举目录');\n } catch (first) {\n if (!isArgCountRejection(first)) throw first;\n return readBrowseAnswer<DirectoryListing>(await namespace.list(undefined), '列举目录');\n }\n },\n createDirectory: async (path, name) => readBrowseAnswer<string>(await namespace.createDirectory(path, name), '新建文件夹'),\n };\n}\n\n/** 一段路径里最后那个分隔符的位置(Windows 上 `\\` 与 `/` 都算;POSIX 上 `\\` 是合法文件名字符,不算)。 */\nfunction lastSeparator(path: string): number {\n const back = path.lastIndexOf('\\\\');\n const slash = path.lastIndexOf('/');\n // 只含 `\\` 的路径按 Windows 规则解;两者都有时取更靠后的那个。\n return back > slash ? back : slash;\n}\n\n/** 上一层:`'C:\\\\a\\\\b\\\\'` → `'C:\\\\a'`;已经是根则回自身;相对名(没有任何分隔符)也回自身。\n *\n * 尾分隔符先吃掉再切——否则「回上一级」会在原地打转。**盘根单独认**:`C:\\` 掐掉尾分隔符会剩成\n * `C:`,而 `C:` 是「当前盘」不是「上一层」,回它就会多跑一趟、还可能跨到别的盘上去。\n * UNC(`\\\\server\\share`)不特殊处理:切到头就是共享名本身,浏览停在共享根。 */\nexport function parentOf(path: string): string {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '') return path;\n const sep = path.includes('\\\\') ? '\\\\' : '/';\n if (/^[A-Za-z]:$/.test(trimmed)) return trimmed + sep;\n const cut = lastSeparator(trimmed);\n if (cut === -1) return trimmed;\n if (cut === 0) return path.slice(0, 1);\n if (cut === 2 && /^[A-Za-z]:/.test(trimmed)) return trimmed.slice(0, 2) + sep;\n return trimmed.slice(0, cut);\n}\n\n/** 把「当前路径 + 一段输入」拼成一个目标路径。 */\nexport function joinPath(directory: string, name: string): string {\n const sep = directory.includes('\\\\') ? '\\\\' : '/';\n const base = directory.replace(/[\\\\/]+$/, '');\n if (base === '') return sep + name;\n return base + sep + name;\n}\n\n/** 一层列举里,哪些行可进:隐藏目录按开关过滤,其余照宿主给的顺序。 */\nexport function visibleEntries(listing: DirectoryListing, showHidden: boolean): readonly DirectoryEntry[] {\n return showHidden ? listing.entries : listing.entries.filter((entry) => !entry.hidden);\n}\n\n/** 这一层有多少个被藏起来的目录(用来决定「显示隐藏目录」那个开关要不要出现在图上)。 */\nexport function hiddenCount(listing: DirectoryListing): number {\n return listing.entries.filter((entry) => entry.hidden).length;\n}\n\n/** 拿一段文本按「最后一个分隔符」切成「目录部分 + 筛选词」。\n *\n * 没打过任何分隔符时目录部分=null(那段文字还不指任何目录,只当筛选词用)。\n * Windows 上 `/` 也当分隔符(宿主那边两种都收),POSIX 上不。 */\nexport function splitDraft(draft: string): { readonly directory: string | null; readonly filter: string } {\n const cut = lastSeparator(draft);\n if (cut === -1) return { directory: null, filter: draft };\n return { directory: draft.slice(0, cut + 1), filter: draft.slice(cut + 1) };\n}\n\n/** 按筛选词过一层目录(大小写不敏感;空筛选词=全留)。 */\nexport function filterEntries(entries: readonly DirectoryEntry[], filter: string): readonly DirectoryEntry[] {\n const needle = filter.trim().toLowerCase();\n if (needle === '') return entries;\n return entries.filter((entry) => entry.name.toLowerCase().includes(needle));\n}\n\n/** 新建文件夹的名字合不合法(单个路径段:不许分隔符、不许空、不许 `.`/`..`)。\n *\n * 与宿主 browse 后端的校验同口径——这里先拦一道,是为了在图上给得出人话,不是替代宿主校验。 */\nexport function validateFolderName(name: string): { readonly ok: true } | { readonly ok: false; readonly reason: string } {\n const trimmed = name.trim();\n if (trimmed === '') return { ok: false, reason: '文件夹名不能为空。' };\n if (/[\\\\/]/.test(trimmed)) return { ok: false, reason: '文件夹名里不能带路径分隔符。' };\n if (trimmed === '.' || trimmed === '..') return { ok: false, reason: '「.」与「..」不是文件夹名。' };\n return { ok: true };\n}\n","/** 文件浏览器的操作半:把「进哪一层、这一层显示什么、选到了什么」算成状态,**IO 全部由调用方注入**。\n *\n * 分两层的原因:本件(操作)在 Node 里就能直测——喂一个假的取数面,就能把「进目录/回上一级/\n * 跳面包屑/筛选/新建文件夹/失败出人话」全跑一遍;视图那半(`directory-browser-ui.ts`)\n * 只负责把这份状态画成 DOM。两边都不认识宿主。\n *\n * 数据来源只有两格:`list(path?)` 与 `createDirectory(path, name)`(调用方注入的 `DirectoryBrowseFace`)。\n */\n\nimport type {\n BrowseFailure,\n DirectoryBrowseFace,\n DirectoryEntry,\n DirectoryListing,\n RootRow,\n} from './directory-browser-contract.js';\nimport {\n browseFaceOf,\n filterEntries,\n isCapabilityRefusal,\n joinPath,\n parentOf,\n splitDraft,\n validateFolderName,\n visibleEntries,\n} from './directory-browser-contract.js';\n\n/** 一层列举的三态:加载中/拿到了/这一层读不出来。 */\nexport type BrowsePhase = 'idle' | 'loading' | 'ready' | 'failed';\n\n/** 图上的全部状态。视图只读它,不改它。 */\nexport interface BrowseState {\n readonly phase: BrowsePhase;\n readonly listing: DirectoryListing | null;\n /** 这一层读失败时的人话;`phase==='failed'` 时一定有。 */\n readonly failure: BrowseFailure | null;\n /** 当前选中的子目录(点行选中;选中后再点「打开」就是它)。 */\n readonly selected: string | null;\n /** 面包屑上那一层的编号,只用于画图。 */\n readonly showHidden: boolean;\n /** 路径框里的草稿(用户可能正在编辑)。 */\n readonly draft: string;\n /** 草稿指向的那一层已经列举过之后,用来按末段过滤的词。 */\n readonly filter: string;\n /** 正在建的文件夹名;null=没在建。 */\n readonly creating: string | null;\n /** 上一次操作的失败人话(新建失败等);看一眼就好,下一次操作清掉。 */\n readonly notice: string | null;\n /** 可跳转的根(Windows 上的盘符等):取数方给什么就是什么,空数组=不画那一行。 */\n readonly roots: readonly RootRow[];\n}\n\n/** 订阅者拿到的状态(不可变,每次变更是新对象)。 */\nexport type BrowseListener = (state: BrowseState) => void;\n\n/** 操作面:视图与用例都只用这些方法。 */\nexport interface BrowseController {\n getState(): BrowseState;\n subscribe(listener: BrowseListener): () => void;\n /** 开图:先试配置里那个目录(空则直接家目录),读不出来(不存在、没权限)退回宿主家目录。\n *\n * 回**第一份落定的状态**:调用方据此分辨「这条路宿主没给」与「这一层读不出来」。 */\n open(): Promise<BrowseState>;\n /** 进某一层(绝对路径,或 `list()` 的默认家目录当入参为 null)。 */\n enter(path: string | null): Promise<void>;\n /** 回上一级(当前层就是根时原地不动)。 */\n up(): Promise<void>;\n /** 采纳当前选择(选中行,没有选中就用当前层)并回调 `onPicked`。 */\n pick(): void;\n /** 取消:关图,什么都不回调。 */\n cancel(): void;\n /** 选中一个子目录(再点一次同一条=取消选中)。 */\n select(path: string): void;\n /** 切换隐藏目录。 */\n toggleHidden(): void;\n /** 改路径框草稿(不列举,只更新草稿与筛选词)。 */\n setDraft(draft: string): void;\n /** 提交路径框草稿:指到哪一层就去哪一层。 */\n commitDraft(): Promise<void>;\n /** 开「新建文件夹」;传 null 收起。 */\n setCreating(name: string | null): void;\n /** 在当前层(或选中的那一层)下建一个新文件夹,成功后进它并选中它。 */\n createFolder(name: string): Promise<void>;\n /** 关图(与 `cancel` 同义,供宿主侧统一收口)。 */\n close(): void;\n}\n\n/** 建一个浏览器控制器。取数、回调、初值全部由调用方给。 */\nexport function createBrowseController(deps: {\n readonly face: DirectoryBrowseFace;\n readonly initialPath: string;\n readonly onPicked: (path: string) => void;\n readonly onClose: () => void;\n /** 失败人话的前缀,默认「浏览失败」。 */\n readonly failureLabel?: string;\n /** 可跳转的根从哪来;不给就是没有这一行(都是空清单,一样不画)。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): BrowseController {\n const label = deps.failureLabel ?? '浏览失败';\n const listeners = new Set<BrowseListener>();\n let state: BrowseState = {\n phase: 'idle',\n listing: null,\n failure: null,\n selected: null,\n showHidden: false,\n draft: deps.initialPath,\n filter: '',\n creating: null,\n notice: null,\n roots: [],\n };\n\n /** 每次列举带一个序号:晚回来的旧回执直接丢掉,不许覆盖新一层。 */\n let generation = 0;\n\n const emit = (next: Partial<BrowseState>): void => {\n state = { ...state, ...next };\n for (const listener of [...listeners]) listener(state);\n };\n\n /** 根清单只问一次:取不到就是空(不报错、不重试——它只是图上的一行入口)。\n *\n * 「问一次」钉在这里而不是图里:视图那半每次重画都会重新求值,问在这里才谈得上一次。 */\n let rootsAsked = false;\n const loadRoots = async (): Promise<void> => {\n if (rootsAsked || deps.rootsSource === undefined) return;\n rootsAsked = true;\n try {\n const roots = await deps.rootsSource();\n if (roots.length > 0) emit({ roots });\n } catch {\n /* 取不到就当没有这一行:浏览本身照常。 */\n }\n };\n\n const humanize = (cause: unknown): BrowseFailure => {\n const raw = (typeof cause === 'object' && cause !== null ? cause : {}) as {\n code?: unknown;\n message?: unknown;\n name?: unknown;\n };\n const message = typeof raw.message === 'string' && raw.message.trim() !== '' ? raw.message.trim() : String(cause);\n const code = typeof raw.code === 'string' && raw.code !== '' ? raw.code : 'browse-failed';\n return { code, message: `${label}:${message}` };\n };\n\n const load = async (path: string | null): Promise<void> => {\n const mine = ++generation;\n emit({ phase: 'loading', failure: null, notice: null });\n try {\n const listing = path === null ? await deps.face.list() : await deps.face.list(path);\n if (mine !== generation) return;\n emit({\n phase: 'ready',\n listing,\n failure: null,\n selected: null,\n draft: listing.path,\n filter: '',\n });\n } catch (cause) {\n if (mine !== generation) return;\n emit({ phase: 'failed', listing: null, failure: humanize(cause), selected: null });\n }\n };\n\n /** 草稿指到的那一层已经列举过时,按末段筛当前这一层的行;否则不过滤。\n *\n * 两边都先掐掉尾分隔符再比:草稿那边切开后天然带尾分隔符(`C:\\a\\` + `b`),\n * 而这一层的 `path` 由宿主给、不带尾分隔符。 */\n const applyDraft = (draft: string): void => {\n const { directory, filter } = splitDraft(draft);\n const listing = state.listing;\n const sameLevel = listing !== null && directory !== null && normalize(directory) === normalize(listing.path);\n emit({ draft, filter: sameLevel ? filter : '' });\n };\n\n const normalize = (path: string): string => path.replace(/[\\\\/]+$/, '');\n\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n async open(): Promise<BrowseState> {\n // 根清单一并去问,但**不等它**:它只是图上的一行入口,落定了自会推一次重画;\n // 等它就等于让「宿主那台机器列盘符快不快」决定对话框什么时候开。\n void loadRoots();\n const wanted = deps.initialPath.trim();\n if (wanted !== '') {\n await load(wanted);\n if (state.phase === 'ready') return state;\n }\n await load(null);\n return state;\n },\n enter: (path) => load(path),\n async up() {\n const listing = state.listing;\n if (listing === null) return;\n const parent = parentOf(listing.path);\n if (parent === listing.path) return;\n await load(parent);\n },\n pick() {\n const target = state.selected ?? state.listing?.path ?? null;\n if (target === null) return;\n deps.onPicked(target);\n },\n cancel() {\n deps.onClose();\n },\n close() {\n deps.onClose();\n },\n select(path) {\n emit({ selected: state.selected === path ? null : path, notice: null });\n },\n toggleHidden() {\n emit({ showHidden: !state.showHidden });\n },\n setDraft(draft) {\n applyDraft(draft);\n },\n async commitDraft() {\n const trimmed = state.draft.trim();\n if (trimmed === '') return;\n await load(trimmed);\n },\n setCreating(name) {\n emit({ creating: name, notice: null });\n },\n async createFolder(name) {\n const checked = validateFolderName(name);\n if (!checked.ok) {\n emit({ notice: checked.reason });\n return;\n }\n const base = state.selected ?? state.listing?.path ?? null;\n if (base === null) {\n emit({ notice: '还没有打开任何目录,无法新建文件夹。' });\n return;\n }\n try {\n const created = await deps.face.createDirectory(base, name.trim());\n emit({ creating: null, notice: null });\n await load(created);\n emit({ selected: created });\n } catch (cause) {\n const failure = humanize(cause);\n emit({ notice: failure.message, creating: null });\n }\n },\n };\n}\n\n/** 图上这一层要画的行(按隐藏开关与草稿末段过滤之后的)。 */\nexport function rowsOf(state: BrowseState): readonly DirectoryEntry[] {\n if (state.listing === null) return [];\n return filterEntries(visibleEntries(state.listing, state.showHidden), state.filter);\n}\n\n/** 图上这一层能不能回上一级(根目录不能)。 */\nexport function canGoUp(state: BrowseState): boolean {\n if (state.listing === null) return false;\n return parentOf(state.listing.path) !== state.listing.path;\n}\n\n/** 「打开」这颗按钮的目标:选中行优先,没选中就是当前层。 */\nexport function targetOf(state: BrowseState): string | null {\n return state.selected ?? state.listing?.path ?? null;\n}\n\n/** 新建文件夹那一行要展示的位置(\"在 <这里> 下新建\")。 */\nexport function createBaseOf(state: BrowseState): string | null {\n return state.selected ?? state.listing?.path ?? null;\n}\n\n/** 一段路径拼成可读的一句话(给用例与调试用;图上不直接显示)。 */\nexport function describe(state: BrowseState): string {\n if (state.phase === 'failed') return `${state.failure?.code ?? 'browse-failed'} @ ${state.draft}`;\n if (state.listing === null) return `${state.phase}`;\n return `${state.listing.path}(${rowsOf(state).length} 行)`;\n}\n\n/** 一行子目录的完整路径(图上点行时用)。 */\nexport function entryPath(directory: string, entry: DirectoryEntry): string {\n return entry.path === '' ? joinPath(directory, entry.name) : entry.path;\n}\n\n/** 目录行接线的返回:**不碰 React**——`actions` 原样喂给 `directory-browser-ui` 的组件 props,\n * `isOpen`/`setOpen` 由调用方自己接进它的状态(宿主怎么重画是它的事,本件不管)。 */\nexport interface DirectoryRowBrowser {\n readonly actions: {\n readonly onPick: () => void;\n readonly onClose: () => void;\n readonly onEnter: (path: string) => void;\n readonly onUp: () => void;\n readonly onSelect: (path: string) => void;\n readonly onToggleHidden: () => void;\n readonly onDraft: (draft: string) => void;\n readonly onCommitDraft: () => void;\n readonly onCreate: (name: string) => void;\n readonly onCreatingChange: (name: string | null) => void;\n };\n /** 开图(先试配置里那个目录,读不出来退回家目录),回第一份落定的状态。 */\n open(): Promise<BrowseState>;\n /** 状态变更订阅:视图那半靠 `useSyncExternalStore` 用它重画。\n *\n * **没有它图上只有第一帧**——列举是异步落定的,React 不订阅就永远停在开图那一刻的状态。 */\n subscribe(listener: BrowseListener): () => void;\n state(): BrowseState;\n /** 一段可读的当前状态(用例与调试用)。 */\n summary(): string;\n}\n\n/** 建一份「目录行接线」:给一个取数面与初值,回一组动作。\n *\n * 只做接线:把控制器算出来的状态与回调打包好,不取数、不画图、不认宿主。 */\nexport function createDirectoryRowBrowser(deps: {\n readonly face: DirectoryBrowseFace;\n readonly initialPath: string;\n readonly onPicked: (path: string) => void;\n readonly onClosed?: () => void;\n readonly failureLabel?: string;\n /** 可跳转的根从哪来(各家给的是同一份「总管电话」的取数器);不给就没有那一行。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): DirectoryRowBrowser {\n const controller = createBrowseController({\n face: deps.face,\n initialPath: deps.initialPath,\n onPicked: deps.onPicked,\n onClose: () => deps.onClosed?.(),\n ...(deps.failureLabel === undefined ? {} : { failureLabel: deps.failureLabel }),\n ...(deps.rootsSource === undefined ? {} : { rootsSource: deps.rootsSource }),\n });\n return {\n open: () => controller.open(),\n subscribe: (listener) => controller.subscribe(listener),\n state: () => controller.getState(),\n summary: () => describe(controller.getState()),\n actions: {\n onPick: () => controller.pick(),\n onClose: () => controller.cancel(),\n onEnter: (path) => void controller.enter(path),\n onUp: () => void controller.up(),\n onSelect: (path) => controller.select(path),\n onToggleHidden: () => controller.toggleHidden(),\n onDraft: (draft) => controller.setDraft(draft),\n onCommitDraft: () => void controller.commitDraft(),\n onCreate: (name) => void controller.createFolder(name),\n onCreatingChange: (name) => controller.setCreating(name),\n },\n };\n}\n\n/** 一条目录行点下去该走哪条路(**六家共用这一处策略**,单独写在各家就会各错各的)。\n *\n * 顺序是「先应用内浏览,被拒再换系统对话框」,因为客户端命名空间上三条动词都在、问不出组合里服务哪种\n * 能力(见 `pickerModeOf`)。回执:\n * - `undefined`=连两格浏览原语都没有(这条路没有,调用方自己想办法);\n * - `'refused'`=宿主回了 `refusalCode`(组合里是系统对话框),图已收起;\n * - `'open'`=图开着,后面的事都在图里。\n *\n * **状态由调用方持有**:`onRow` 收到刚建好的那条(或 null=收起),React 那边据此决定画不画图。\n *\n * `refusalCode` 是**宿主的回执码**(各家自己从宿主镜像里拿,本件只照比不认名):\n * 它换名字本件照样跑,六家一起换。 */\nexport function openRowBrowser(input: {\n readonly picker: unknown;\n readonly initialPath: string;\n readonly onChange: (next: string) => void;\n readonly onRow: (row: DirectoryRowBrowser | null) => void;\n readonly refusalCode: string;\n /** 可跳转的根从哪来(可选:不给就是图上没有那一行)。 */\n readonly rootsSource?: () => Promise<readonly RootRow[]>;\n}): Promise<'open' | 'refused'> | undefined {\n const face = browseFaceOf(input.picker);\n if (face === null) return undefined;\n const row = createDirectoryRowBrowser({\n face,\n initialPath: input.initialPath,\n onPicked: (picked) => {\n input.onChange(picked);\n input.onRow(null);\n },\n onClosed: () => input.onRow(null),\n ...(input.rootsSource === undefined ? {} : { rootsSource: input.rootsSource }),\n });\n input.onRow(row);\n return row.open().then((settled) => {\n if (\n settled.phase === 'failed' &&\n isCapabilityRefusal(settled.failure?.code, settled.failure?.message, input.refusalCode)\n ) {\n input.onRow(null);\n return 'refused' as const;\n }\n return 'open' as const;\n });\n}\n","/** 文件浏览器视图半的**零件**:样式表与四行控件(面包屑/根/一行子目录/新建行)。\n *\n * 为什么拆出来:整张对话框的组版与这些零件的变化频率不同——零件跟着「图上多一行/少一行」动,\n * 组版跟着「对话框长什么样」动。拆开之后两边都在 350 行告警线内,而对外面不变\n * (`directory-browser-ui.ts` 照旧转出组件与两个接线函数)。\n *\n * 本件与视图半同样**不认识宿主**:只认状态与文案。\n */\n\nimport * as React from 'react';\nimport type { BrowseState } from './directory-browser-state.js';\nimport type { DirectoryEntry, RootKind } from './directory-browser-contract.js';\n\n/** 图上出现的每一句文案;调用方给(本件不自造口径)。 */\nexport interface DirectoryBrowserLabels {\n /** 对话框标题,例:「选择数据目录」。 */\n readonly title: string;\n readonly close: string;\n readonly up: string;\n readonly pathPlaceholder: string;\n readonly go: string;\n readonly showHidden: (count: number) => string;\n readonly empty: string;\n readonly loading: string;\n readonly newFolder: string;\n readonly createConfirm: string;\n readonly createCancel: string;\n readonly select: string;\n readonly selected: string;\n readonly open: string;\n readonly cancel: string;\n /** 选中预览那一行的前缀,例:「将选定:」。 */\n readonly willPick: string;\n}\n\n/** 根的类型说明(悬停可见):**本件自己的词汇**,与取数方无关。 */\nconst ROOT_KIND_TEXT: Record<RootKind, string> = {\n fixed: '固定磁盘',\n network: '网络驱动器',\n removable: '可移动磁盘',\n optical: '光驱',\n other: '磁盘',\n};\n\nexport const S = {\n scrim: {\n position: 'fixed',\n inset: 0,\n background: 'rgba(0,0,0,0.35)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n } as React.CSSProperties,\n dialog: {\n width: 680,\n maxWidth: '92vw',\n height: 500,\n maxHeight: '86vh',\n display: 'flex',\n flexDirection: 'column',\n background: 'var(--dsw-alias-bg-layer-1, #1f1f23)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 10,\n boxShadow: '0 12px 32px rgba(0,0,0,0.35)',\n overflow: 'hidden',\n } as React.CSSProperties,\n head: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n } as React.CSSProperties,\n title: { fontSize: 14, fontWeight: 700 } as React.CSSProperties,\n crumbs: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2, padding: '8px 12px 0', fontSize: 12 } as React.CSSProperties,\n chromeButton: {\n background: 'transparent',\n border: 'none',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n padding: '2px 4px',\n fontSize: 12,\n opacity: 0.85,\n } as React.CSSProperties,\n crumbSep: { opacity: 0.45, fontSize: 12 } as React.CSSProperties,\n roots: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6, padding: '0 12px 8px', fontSize: 12 } as React.CSSProperties,\n rootButton: {\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n padding: '3px 8px',\n opacity: 1,\n } as React.CSSProperties,\n pathRow: { display: 'flex', gap: 6, padding: '8px 12px' } as React.CSSProperties,\n input: {\n flex: 1,\n minWidth: 0,\n fontSize: 12,\n padding: '5px 8px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n } as React.CSSProperties,\n button: {\n fontSize: 12,\n padding: '5px 10px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n } as React.CSSProperties,\n buttonOff: { opacity: 0.45, cursor: 'default' } as React.CSSProperties,\n list: {\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n margin: '0 12px',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n borderRadius: 6,\n } as React.CSSProperties,\n row: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n padding: '6px 10px',\n textAlign: 'left',\n background: 'transparent',\n color: 'inherit',\n border: 'none',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))',\n cursor: 'pointer',\n fontSize: 13,\n } as React.CSSProperties,\n rowSelected: { background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.18))' } as React.CSSProperties,\n empty: { padding: '14px 12px', fontSize: 12, opacity: 0.7 } as React.CSSProperties,\n footNote: { padding: '6px 12px 0', fontSize: 12, minHeight: 20 } as React.CSSProperties,\n foot: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n } as React.CSSProperties,\n target: { marginRight: 'auto', fontSize: 12, opacity: 0.8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } as React.CSSProperties,\n error: { color: 'var(--dsw-alias-state-error-primary, #e66)' } as React.CSSProperties,\n} as const;\n\n/** 面包屑:从文件系统根到当前层,每一格都是跳转目标(`listing.crumbs` 由取数方给)。 */\nexport function crumbRow(state: BrowseState, onEnter: (path: string) => void): React.ReactElement | null {\n const listing = state.listing;\n if (listing === null || listing.crumbs.length === 0) return null;\n const parts: React.ReactNode[] = [];\n listing.crumbs.forEach((crumb, index) => {\n if (index > 0) parts.push(React.createElement('span', { key: 'sep-' + crumb.path, style: S.crumbSep }, '›'));\n const isCurrent = crumb.path === listing.path;\n parts.push(\n React.createElement(\n 'button',\n {\n key: crumb.path,\n type: 'button',\n style: { ...S.chromeButton, fontWeight: isCurrent ? 700 : 400 },\n title: crumb.path,\n // #1164 T5:对话框组 V1 全量(复用共享 interactionCss,不另起 tokens/链路)。\n // 面包屑与普通按钮同口径:悬停聚光+淡洗+辉光+上浮、按下收缩、焦点双环、\n // 长按 500ms 纯视觉环(只做视觉标记,不注册动作回调、不进状态机分支)。\n 'data-ilife-press': 'dialog-crumb',\n 'data-ilife-longpress': 'dialog-crumb',\n onClick: () => onEnter(crumb.path),\n },\n // 每一格都写**它自己那一层的名字**:第一格是文件系统根(`C:\\`),不是「上一级」——\n // 「上一级」是路径行那颗按钮的活(#744 返修:这一格原先串了那颗按钮的文案)。\n crumb.name,\n ),\n );\n });\n return React.createElement('div', { style: S.crumbs }, parts);\n}\n\n/** 可跳转的根那一行(Windows 上的盘符等):**就是本机全部磁盘**,故不加行首说明。\n * **少于两个根就不画**:一个根没有「跳到别处」可言。 */\nexport function rootsRow(state: BrowseState, onEnter: (path: string) => void): React.ReactElement | null {\n if (state.roots.length < 2) return null;\n const parts: React.ReactNode[] = state.roots.map((root) =>\n React.createElement(\n 'button',\n {\n key: root.path,\n type: 'button',\n style: { ...S.chromeButton, ...S.rootButton },\n title: ROOT_KIND_TEXT[root.kind] ?? ROOT_KIND_TEXT.other,\n // #1164 T5:同上(根跳转与普通按钮同口径)。\n 'data-ilife-press': 'dialog-root',\n 'data-ilife-longpress': 'dialog-root',\n onClick: () => onEnter(root.path),\n },\n root.path,\n ),\n );\n return React.createElement('div', { style: S.roots }, parts);\n}\n\n/** 一行子目录:点名字进它,点「选」把它当目标(「选」再点一次取消)。 */\nexport function entryRow(\n entry: DirectoryEntry,\n selected: boolean,\n labels: DirectoryBrowserLabels,\n onToggleSelect: (path: string) => void,\n onEnter: (path: string) => void,\n): React.ReactElement {\n return React.createElement(\n 'div',\n { key: entry.path, style: { ...S.row, padding: 0, ...(selected ? S.rowSelected : {}) } },\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.row, flex: 1, border: 'none' },\n // #1164 T5:条目进入与普通按钮同口径(按压+焦点双环+长按纯视觉环)。\n 'data-ilife-press': 'dialog-entry',\n 'data-ilife-longpress': 'dialog-entry',\n onClick: () => onEnter(entry.path),\n },\n '📁 ' + entry.name,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, marginRight: 8, padding: '2px 8px' },\n 'aria-pressed': selected,\n title: selected ? labels.selected : labels.select,\n // #1164 T5:条目选中与普通按钮同口径。\n 'data-ilife-press': 'dialog-select',\n 'data-ilife-longpress': 'dialog-select',\n onClick: () => onToggleSelect(entry.path),\n },\n selected ? '✓' : labels.select,\n ),\n );\n}\n\n/** 「新建文件夹」那一行(`creatingName === null` 时收起)。 */\nexport function createRow(\n labels: DirectoryBrowserLabels,\n onName: (name: string) => void,\n onCancel: () => void,\n onCreate: (name: string) => void,\n creatingName: string | null,\n): React.ReactElement | null {\n if (creatingName === null) return null;\n // #1164 T5:空名不可点(禁用态摇头+禁止指针);非空才可建(validateFolderName 的其余非法名仍走 notice 人话)。\n const createEmpty = creatingName.trim() === '';\n return React.createElement(\n 'div',\n { style: { ...S.pathRow, borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))' } },\n React.createElement('input', {\n style: S.input,\n value: creatingName,\n placeholder: labels.newFolder,\n 'aria-label': labels.newFolder,\n // #1164 T5:文本框只给焦点双环(不收缩、无长按环,与主面板输入框同口径)。\n 'data-ilife-focus': 'dialog-input',\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => onName(event.currentTarget.value),\n onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') onCreate(creatingName);\n if (event.key === 'Escape') onCancel();\n },\n }),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: createEmpty,\n // #1164 T5:创建确认与普通按钮同口径;禁用时摇头+禁止指针(共享 interactionCss)。\n 'data-ilife-press': 'dialog-create',\n 'data-ilife-longpress': 'dialog-create',\n onClick: () => onCreate(creatingName),\n },\n labels.createConfirm,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n // #1164 T5:创建取消与普通按钮同口径(常可用)。\n 'data-ilife-press': 'dialog-create-cancel',\n 'data-ilife-longpress': 'dialog-create-cancel',\n onClick: onCancel,\n },\n labels.createCancel,\n ),\n );\n}\n","/** 文件浏览器的视图半:把 `BrowseState` 画成一张对话框。**不认识宿主**,也不自己做 IO。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX,见 `tsdown.config.ts` 的既成口径)。\n * 只读状态、只回调;进哪一层、选到什么由操作半(`directory-browser-state.ts`)算。\n * 样式与四行控件在 `directory-browser-parts.ts`(那里跟着「多一行/少一行」动,本件只管组版)。\n */\n\nimport * as React from 'react';\nimport { canGoUp, rowsOf, targetOf } from './directory-browser-state.js';\nimport type { BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nimport { hiddenCount } from './directory-browser-contract.js';\nimport type { DirectoryListing } from './directory-browser-contract.js';\nimport { S, createRow, crumbRow, entryRow, rootsRow } from './directory-browser-parts.js';\nimport type { DirectoryBrowserLabels } from './directory-browser-parts.js';\n\nexport type { DirectoryBrowserLabels } from './directory-browser-parts.js';\n\n/** 组件 props:样式只吃这一格,别的一律不给。 */\nexport interface DirectoryBrowserProps {\n readonly open: boolean;\n readonly state: BrowseState;\n readonly labels: DirectoryBrowserLabels;\n readonly onPick: () => void;\n readonly onClose: () => void;\n readonly onEnter: (path: string) => void;\n readonly onUp: () => void;\n readonly onSelect: (path: string) => void;\n readonly onToggleHidden: () => void;\n readonly onDraft: (draft: string) => void;\n readonly onCommitDraft: () => void;\n readonly onCreate: (name: string) => void;\n readonly onCreatingChange: (name: string | null) => void;\n}\n\nconst EMPTY_LISTING: DirectoryListing = { path: '', home: '', crumbs: [], entries: [], truncated: false };\n\n/** 画一张文件浏览器对话框。`open=false` 时返回 null(本件不挂 portal,由调用方决定挂在哪)。 */\nexport function DirectoryBrowser(props: DirectoryBrowserProps): React.ReactElement | null {\n const { open, state, labels } = props;\n if (!open) return null;\n const rows = rowsOf(state);\n const target = targetOf(state);\n const listing = state.listing ?? EMPTY_LISTING;\n const hidden = hiddenCount(listing);\n // #1164 T5:对话框组 V1 全量(复用共享 interactionCss,不另起 tokens/链路)。\n // 按钮一律按压+长按纯视觉环(只做视觉标记,不注册动作回调、不进状态机分支);\n // 输入框/复选框只给焦点双环(不收缩、无长按环);禁用态摇头+禁止指针走共享 CSS。\n const upOff = !canGoUp(state);\n const goOff = state.draft.trim() === '';\n const pickOff = target === null;\n const body: React.ReactNode[] = [];\n body.push(crumbRow(state, props.onEnter));\n body.push(rootsRow(state, props.onEnter));\n body.push(\n React.createElement(\n 'div',\n { style: S.pathRow },\n React.createElement('input', {\n style: S.input,\n value: state.draft,\n placeholder: labels.pathPlaceholder,\n 'aria-label': labels.pathPlaceholder,\n // #1164 T5:路径框只给焦点双环(与主面板输入框同口径)。\n 'data-ilife-focus': 'dialog-input',\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => props.onDraft(event.currentTarget.value),\n onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') props.onCommitDraft();\n },\n }),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: upOff,\n 'data-ilife-press': 'dialog-up',\n 'data-ilife-longpress': 'dialog-up',\n onClick: props.onUp,\n },\n labels.up,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n disabled: goOff,\n 'data-ilife-press': 'dialog-go',\n 'data-ilife-longpress': 'dialog-go',\n onClick: props.onCommitDraft,\n },\n labels.go,\n ),\n ),\n );\n body.push(\n React.createElement(\n 'div',\n { style: S.list, role: 'listbox', 'aria-label': labels.title },\n rows.length === 0\n ? React.createElement('div', { style: S.empty }, state.phase === 'loading' ? labels.loading : labels.empty)\n : rows.map((entry) => entryRow(entry, state.selected === entry.path, labels, props.onSelect, props.onEnter)),\n ),\n );\n body.push(createRow(labels, props.onDraft, () => props.onCreatingChange(null), props.onCreate, state.creating));\n body.push(\n React.createElement(\n 'div',\n { style: S.footNote },\n hidden > 0\n ? React.createElement(\n 'label',\n { style: { fontSize: 12, cursor: 'pointer', marginRight: 10 } },\n React.createElement('input', {\n type: 'checkbox',\n checked: state.showHidden,\n // #1164 T5:复选框只给焦点双环(不收缩、无长按环)。\n 'data-ilife-focus': 'dialog-check',\n onChange: props.onToggleHidden,\n }),\n ' ' + labels.showHidden(hidden),\n )\n : null,\n state.creating === null\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.chromeButton, textDecoration: 'underline' },\n // #1164 T5:新建入口与普通按钮同口径。\n 'data-ilife-press': 'dialog-newfolder',\n 'data-ilife-longpress': 'dialog-newfolder',\n onClick: () => props.onCreatingChange(''),\n },\n labels.newFolder,\n )\n : null,\n state.notice !== null ? React.createElement('div', { style: S.error }, state.notice) : null,\n state.phase === 'failed' && state.failure !== null\n ? React.createElement('div', { style: S.error }, state.failure.message)\n : null,\n ),\n );\n body.push(\n React.createElement(\n 'div',\n { style: S.foot },\n React.createElement('span', { style: S.target, title: target ?? '' }, target === null ? '' : labels.willPick + target),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, ...(pickOff ? S.buttonOff : {}) },\n disabled: pickOff,\n // #1164 T5:无目标时不可点(禁用态摇头+禁止指针);有目标时与普通按钮同口径。\n 'data-ilife-press': 'dialog-pick',\n 'data-ilife-longpress': 'dialog-pick',\n onClick: props.onPick,\n },\n labels.open,\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: S.button,\n // #1164 T5:取消与普通按钮同口径(常可用)。\n 'data-ilife-press': 'dialog-cancel',\n 'data-ilife-longpress': 'dialog-cancel',\n onClick: props.onClose,\n },\n labels.cancel,\n ),\n ),\n );\n return React.createElement(\n 'div',\n {\n style: S.scrim,\n role: 'presentation',\n onMouseDown: (event: React.MouseEvent<HTMLDivElement>) => {\n if (event.target === event.currentTarget) props.onClose();\n },\n },\n React.createElement(\n 'div',\n { style: S.dialog, role: 'dialog', 'aria-modal': true, 'aria-label': labels.title },\n React.createElement(\n 'div',\n { style: S.head },\n React.createElement('span', { style: S.title }, labels.title),\n // 标题右侧**不再重复当前路径**:同一条路径下面已经出现两次(面包屑与路径框),\n // 而盘符那行也要地方——头一行越干净越好(#744 第三轮现场反馈)。\n React.createElement(\n 'button',\n {\n type: 'button',\n style: { ...S.button, marginLeft: 'auto' },\n 'aria-label': labels.close,\n title: labels.close,\n // #1164 T5:标题栏关闭与普通按钮同口径。\n 'data-ilife-press': 'dialog-close',\n 'data-ilife-longpress': 'dialog-close',\n onClick: props.onClose,\n },\n '✕',\n ),\n ),\n body,\n ),\n );\n}\n\n/** 目录行接线的 React 半边:把操作半那组动作直接喂给组件。\n *\n * 操作半(`directory-browser-state.ts` 的 `createDirectoryRowBrowser`)不碰 React,\n * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。\n *\n * `useSyncExternalStore` 是**必需**的:列举是异步落定的,不订阅的话这一帧画完就再没有重画,\n * 图上会永远停在「正在读取…」(#744 实测)。 */\nexport function DirectoryBrowserFromRow(props: {\n readonly open: boolean;\n readonly row: DirectoryRowBrowser;\n readonly labels: DirectoryBrowserLabels;\n}): React.ReactElement | null {\n const state = React.useSyncExternalStore(props.row.subscribe, props.row.state);\n return DirectoryBrowser({\n open: props.open,\n state,\n labels: props.labels,\n ...props.row.actions,\n });\n}\n\nexport { createBrowseController, createDirectoryRowBrowser, describe, rowsOf, targetOf } from './directory-browser-state.js';\nexport type { BrowseController, BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nexport type { DirectoryEntry, DirectoryListing, RootKind, RootRow } from './directory-browser-contract.js';\n","/** 共用标题 + 印章书签 · 纯组件(#996,不认任何一家)。\n *\n * 来源:原型 #995 变体 A(行尾悬签,人裁已定形)——标题左、签贴同行行尾向右探出。\n * 用法分两档(#1069 收窄后):`TitleBlock` 两家——`config-panel-view.ts` 的 `PanelBody`\n * (`product`=各家产品名/`purpose`=`配置`)与 `health-view.ts` 的 `HealthTable`\n * (`product`=各家产品名/`purpose`=`体检`);`Seal` 只跟 `PanelBody` 走——**签只印在配置头,\n * 体检头不带签**(负责人 2026-10-03 定;签的第二个用法是 `HealthTable`,本票已摘掉,签回到单一用法)。\n * 别处要同形标题才用 `TitleBlock`,用不出来就留在那两家;签再要第二个用法,先问负责人。\n *\n * 形状(逐值照原型锁死,不许自己重新设计):\n * 标题:同行双样式、无 `·`——`product` 700/1.15em 主色 + `purpose` 400/0.85em 次色,\n * 同行基线、`gap .5em`(16px 下=8px,满足地图 8px),整块 `h2`(`aria-label`=`product purpose`)。\n * 字号取原型锁(地图 Notes 写 1.08em/640 是 grill 共识早值,原型后锁 1.15em/700,以原型为准)。\n * 签:横向书签固定色 `#a5281b` 白字、`clip-path` 右端 V 尾切角 + 微旋 `-3°`、\n * `white-space:pre` 保空格、`role=\"note\"` 纯展示不可点(无点击、无 tabindex)。\n * 印文由调用方交 `sealText`(显示形一律带空格大写 HELP,空格=U+0020,997 接线);只配头给。\n *\n * 纪律(#739/#941):\n * 零字族(无 `fontFamily`、无 `font:` 简写)、字号一律 em;颜色走 DSH 主题别名,\n * 唯签固定色沿 health 红黄固定色先例(纸变印不变,印不管纸面派生)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\n\nconst S = {\n /** 整块 `h2`:同行基线双样式容器(原型 `.titleblock`;`flex:1 1 auto` 把签顶到行尾,390 宽可换行不挤)。 */\n titleBlock: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'baseline',\n gap: '0 0.5em',\n margin: 0,\n fontSize: '1em',\n lineHeight: 1.5,\n flex: '1 1 auto',\n minWidth: '9em',\n } as React.CSSProperties,\n /** 产品名(原型 `.product`:700/1.15em 主色)。 */\n product: {\n fontSize: '1.15em',\n fontWeight: 700,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** 用途(原型 `.purpose`:400/0.85em 次色)。 */\n purpose: {\n fontSize: '0.85em',\n fontWeight: 400,\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n /** 签(原型 `.seal`:固定印色+右端 V 尾+微旋,纯展示)。 */\n seal: {\n display: 'inline-block',\n flexShrink: 0,\n fontSize: '0.78em',\n lineHeight: 1.6,\n color: '#fff',\n background: '#a5281b',\n padding: '0.15em 0.9em 0.15em 0.8em',\n whiteSpace: 'pre',\n transform: 'rotate(-3deg)',\n clipPath: 'polygon(0 0, calc(100% - 0.65em) 0, 100% 50%, calc(100% - 0.65em) 100%, 0 100%)',\n } as React.CSSProperties,\n} as const;\n\nexport interface TitleBlockProps {\n /** 产品名(各家自己的那一格;缺席=空串,只印用途)。 */\n readonly product: string;\n /** 用途(`配置`/`体检`两档,不认第三档)。 */\n readonly purpose: string;\n}\n\nexport interface SealProps {\n /** 印文(显示形一律产品名+空格+大写 HELP,空格=U+0020;纯展示不可点)。 */\n readonly sealText: string;\n}\n\n/** 标题分层(同行双样式,无 `·`,整块 `h2`)。 */\nexport function TitleBlock(props: TitleBlockProps): React.ReactElement {\n const label = (props.product + ' ' + props.purpose).trim();\n return React.createElement(\n 'h2',\n { style: S.titleBlock, 'aria-label': label },\n React.createElement('span', { style: S.product }, props.product),\n React.createElement('span', { style: S.purpose }, props.purpose),\n );\n}\n\n/** 印章书签(横向固定印,纯展示)。 */\nexport function Seal(props: SealProps): React.ReactElement {\n return React.createElement(\n 'span',\n { style: S.seal, role: 'note', 'aria-label': props.sealText },\n props.sealText,\n );\n}\n","/** 卷轴纸面字排 + 关闭钮样子 · **定稿件**(2026-10-06 定档,原型选型期结束)。\n *\n * 定稿形状(逐值锁死,改动须同步 `test/seal-scroll.test.mjs` 与 `test/seal-bundle-1158.test.mjs`):\n * 纸面:三段紧凑行内(进展/状态/计划),行首一枚朱砂小方印(进/状/计),标签与正文同行基线对齐;\n * 末行吃回 0.5em 行距(不然底比顶空一截);饰线上距加宽到 `0.5em 0 0.75em`;纸底与头部沿用原样式。\n * 档位语:该档金属渐变压进字里(金=鎏金/银=冷银/铜=暖铜),不再一律金。\n * 关闭钮:右上角斜披的朱砂菱形绦带(正立「收卷」字独立成层),两条飘尾沿菱形两条下边各向底尖收,\n * 各自朝外 26°(左尾朝左下、右尾朝右下);尾是两层——外层过糙边出毛边,内层纯色芯不受滤镜。\n *\n * 为什么单独一件:`seal-scroll.ts` 已接近告警线(外框/纸/浮层/滤镜都住那里),纸面字排与关闭钮住这里。\n * 本件是纯函数:吃 ctx 回五件,不留状态、不取数。原型期的二十余档方案已按「定稿后只留胜出那一档」删除。\n */\nimport * as React from 'react';\nimport { SEAL_ROUGH_INK_ID, SEAL_ROUGH_METAL_ID } from './seal-scroll.js';\n\n/** 关闭钮的样子:样式 + 字面 + 可选装饰件。 */\nexport interface CloseLook {\n readonly style: React.CSSProperties;\n /** 字面(可为节点:绦带那档要把字反转回正立)。 */\n readonly label: React.ReactNode;\n readonly decor?: React.ReactNode;\n}\n\n/** 材质底层:**糙边滤镜只挂这一层**——文字住在它外面,所以字是清晰的(照插件章「金属带+印文」的老做法)。 */\nfunction bgLayer(style: React.CSSProperties, children?: React.ReactNode): React.ReactElement {\n return React.createElement('span', { 'aria-hidden': true, style: { position: 'absolute', inset: 0, ...style } }, children ?? null);\n}\n\n/** 清晰字面:压在材质层之上,不参与任何滤镜。 */\nfunction crisp(text: React.ReactNode, style?: React.CSSProperties): React.ReactElement {\n return React.createElement('span', { style: { position: 'relative', zIndex: 1, ...style } }, text);\n}\n\n/** 档位语三档金属(鎏金/冷银/暖铜):主色+渐变(`backgroundClip:text` 描出金属光泽)。 */\nexport const TIER_METAL: Record<string, { readonly flat: string; readonly gradient: string }> = {\n gold: { flat: '#b8912a', gradient: 'linear-gradient(180deg,#f6e27a 0%,#d9b53c 45%,#a5811b 100%)' },\n silver: { flat: '#8b959d', gradient: 'linear-gradient(180deg,#f7fafc 0%,#c3ccd3 45%,#8b959d 100%)' },\n copper: { flat: '#a8663a', gradient: 'linear-gradient(180deg,#f0bd8e 0%,#c07b45 45%,#8a4a24 100%)' },\n};\n/** 档位金属边(铜/银/金):章与绦带共用这一份取值。 */\nconst TIER_EDGE: Record<string, string> = { gold: '#f5d97a', silver: '#d8dee3', copper: '#a5652f' };\nconst KAI = '\"Kaiti SC\",\"KaiTi\",\"STKaiti\",serif';\n\n/** 关闭钮(定稿:绦带·飘尾)。右上角一枚斜披 45° 的朱砂菱形,两条飘尾分挂两条下边,尾先画、带后画压住尾根。 */\nexport function closeLook(tierKey: string): CloseLook {\n const crimson = 'linear-gradient(135deg,#c63d2a 8%,#a32216 50%,#7e1a10)';\n /** 档位金属边(铜/银/金):绦带**只借这一条**——主体恒朱砂,边随档走,跟章「红底+金属边」同构。 */\n const edge = TIER_EDGE[tierKey] ?? TIER_EDGE['gold']!;\n const ribbon = bgLayer(\n // 边要压得住:2px 实色金属边(1px 半透明那版看不出档)+ 外圈一道暗线把边从框上切出来。\n { borderRadius: '0.375em', background: crimson, boxShadow: '0 0 0 1px #5c110a59, 0 2px 8px #3d241066, inset 0 0 0 2px ' + edge, transform: 'rotate(45deg)', filter: 'url(#' + SEAL_ROUGH_METAL_ID + ')' },\n null,\n );\n /** 尾色=绦带上亮一档的朱砂(悬在纸上也不发灰、不发黑):两条尾同色。 */\n const TAIL_FILL = 'linear-gradient(180deg,#e8573c 0%,#c33a24 55%,#a52612 100%)';\n /** 飘尾:屏幕坐标(不随菱形旋转)——左尾往左下、右尾往右下(CSS 正角=尾梢往左)。\n * 两层:外层过糙边滤镜出毛边,内层纯色芯不受滤镜——尾悬在纸上还是悬在暗底上都是同一色。 */\n const loose = (key: string, left: string, deg: number, len: string, top: string): React.ReactElement =>\n React.createElement('span', {\n key, 'aria-hidden': true,\n style: { position: 'absolute', left, top, width: '0.4375em', height: len, transformOrigin: 'top center', transform: 'rotate(' + deg + 'deg)' },\n },\n bgLayer({\n borderRadius: '0 0 0.1875em 0.1875em',\n background: TAIL_FILL,\n // 只留一道暗红描边把尾从纸面/暗底上切出来;**不给金边**——尾只有 6px 宽,\n // 1px 金边被糙边滤镜一搅会把整条尾染成金灰(左尾「不够亮」就是这么来的)。\n boxShadow: '0 0 0 1.5px ' + edge + 'cc, 0 1px 3px #00000055',\n filter: 'url(#' + SEAL_ROUGH_METAL_ID + ')',\n }),\n // 纯色芯几乎铺满(只留 0.5px 给毛边),这样尾的观感=上面那档朱砂,不被滤镜的破洞拉灰。\n React.createElement('span', { 'aria-hidden': true, style: { position: 'absolute', inset: '0.5px', borderRadius: '0 0 0.15625em 0.15625em', background: TAIL_FILL } }));\n return {\n label: crisp('收卷', {\n fontFamily: KAI, fontSize: '0.8125em', letterSpacing: '0.22em', textIndent: '0.22em',\n color: '#f8f1e2', textShadow: '0 1px 2px #000000aa',\n }),\n decor: React.createElement(React.Fragment, null,\n // 尾根压在带面之下(top 取在菱形内),梢头各自朝外:tL +26° ⇒ 尾梢往左,tR −26° ⇒ 尾梢往右。\n loose('tL', 'calc(50% - 0.8125em)', 26, '2em', '2.8125em'),\n loose('tR', 'calc(50% + 0.4375em)', -26, '2em', '2.8125em'),\n ribbon),\n style: {\n boxSizing: 'border-box', padding: 0, position: 'absolute', right: '-1.5em', top: '-1.5em', width: '3.25em', height: '3.25em',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n cursor: 'pointer', border: 'none', background: 'transparent',\n },\n };\n}\n\nexport interface PaperStyles {\n readonly head: React.CSSProperties;\n readonly paper: React.CSSProperties;\n readonly tier: React.CSSProperties;\n readonly orn: React.CSSProperties;\n readonly ornLine: React.CSSProperties;\n readonly ornDot: React.CSSProperties;\n readonly block: React.CSSProperties;\n readonly blockDivided: React.CSSProperties;\n readonly blockLast: React.CSSProperties;\n readonly label: React.CSSProperties;\n readonly text: React.CSSProperties;\n}\n\nexport interface PaperCtx {\n /** 档位(gold/silver/copper;决定档位语用哪一档金属)。 */\n readonly tierKey: string;\n /** 档位话(金=精雕细琢中/银=全打通中/铜=基础建设中;定义在 `seal-scroll.ts`)。 */\n readonly tierText: string;\n readonly progress: string;\n readonly status: string;\n readonly plan: string;\n /** 印色(材质那一份,固定色)。 */\n readonly accent: string;\n /** 分隔线同族色。 */\n readonly ruler: string;\n /** 纸边色。 */\n readonly paperEdge: string;\n readonly styles: PaperStyles;\n}\n\n/** 纸面产出:五件里只有需要覆盖的才给(定稿不覆盖纸底与头部)。 */\nexport interface PaperParts {\n readonly paper?: React.CSSProperties | undefined;\n readonly head?: React.CSSProperties | undefined;\n readonly tier: React.ReactNode;\n readonly orn: React.ReactNode | null;\n readonly body: React.ReactNode;\n}\n\n/** 画一条饰线(方向 90/270 决定渐变朝向;定稿的段上距是 0.5em 0 0.75em)。 */\nfunction ornament(st: PaperStyles, ruler: string): React.ReactNode {\n const line = (key: string, dir: string): React.ReactElement =>\n React.createElement('span', { key, style: { ...st.ornLine, background: 'linear-gradient(' + dir + 'deg,#0000,' + ruler + ')' } });\n return React.createElement('div', { style: { ...st.orn, margin: '0.5em 0 0.75em' } },\n line('l', '90'), React.createElement('i', { style: { ...st.ornDot, background: ruler } }), line('r', '270'));\n}\n\n/** 行首那枚朱砂小印(进/状/计):**两层**——材质层过印那条糙边滤镜(印泥压出来的毛边与斑驳),\n * 字独立成层保持清晰;方块各带一点歪斜(真印章不会次次压得方正)。\n * 外框仍是 0.7em 字/1.6 行高/左右 0.375em 垫——换的是质感,版面逐值不动。 */\nfunction inkMark(name: string): React.ReactElement {\n const tilt = name === '进展' ? -2.4 : name === '状态' ? 1.6 : -1.2;\n return React.createElement('span', {\n style: { position: 'relative', flex: '0 0 auto', fontSize: '0.7em', lineHeight: 1.6, padding: '0 0.375em', transform: 'rotate(' + tilt + 'deg)' },\n },\n // 印泥层:中心偏亮(按下去的那一下)、四周吃墨更深,再压一道内影当「厚」\n React.createElement('span', { 'aria-hidden': true, style: {\n position: 'absolute', inset: 0, borderRadius: '0.1875em',\n // 平涂朱砂(大红色):**不给中心高光、不给白色内圈**——那是塑料的读法;\n // 墨色浓淡全部交给滤镜里那层噪点蒙版。\n background: '#d92b1c',\n filter: 'url(#' + SEAL_ROUGH_INK_ID + ')',\n } },\n // 只在同色系里做浓淡:浅的=纸没吃透(偏橘红),深的=墨厚(偏暗红)。不许再用白色(白了就是高光)。\n React.createElement('span', { 'aria-hidden': true, style: {\n position: 'absolute', inset: 0, borderRadius: 'inherit',\n background: 'radial-gradient(circle at 72% 76%,#a81a0e33 0%,#0000 58%), radial-gradient(circle at 20% 62%,#f0715a3d 0%,#0000 52%), radial-gradient(circle at 52% 12%,#ee6a5233 0%,#0000 46%)',\n } })),\n // 字层:不参与滤镜(小方印上的字一糊就白做了)\n React.createElement('span', { style: { position: 'relative', color: '#fdf7ea', opacity: 0.95 } }, name.slice(0, 1)));\n}\n\n/** 三段:一行紧凑(行首朱砂小方印+标签+正文同一基线)。 */\nfunction inlineRows(st: PaperStyles, ctx: PaperCtx): React.ReactNode {\n // 末行吃回半个行距:行高 1.9 会在字形下留 ~6px 半行距,叠在纸底内垫上就是“底比顶空一截”。\n // 实测(像素扫描):不吃回时 顶 24/底 32;吃回 0.5em 后两端齐。\n const one = (key: string, name: string, text: string, last = false): React.ReactElement =>\n React.createElement(\n 'div',\n { key, style: { margin: last ? '0 0 -0.5em' : '0 0 0.5em', display: 'flex', alignItems: 'baseline', gap: '0.5em' } },\n inkMark(name),\n React.createElement('span', { style: { ...st.label, flex: '0 0 auto', display: 'inline', marginBottom: 0, fontSize: '0.8em', color: ctx.accent } }, name + ':'),\n React.createElement('span', { style: { ...st.text, flex: '1 1 auto', display: 'inline', margin: 0, fontSize: '0.9em' } }, text),\n );\n return React.createElement(React.Fragment, null,\n one('progress', '进展', ctx.progress),\n one('status', '状态', ctx.status),\n one('plan', '计划', ctx.plan, true));\n}\n\n/** 档位语(一行):该档金属渐变压进字里。 */\nfunction tierNode(ctx: PaperCtx): React.ReactNode {\n const metal = TIER_METAL[ctx.tierKey] ?? TIER_METAL['gold']!;\n return React.createElement('div', {\n style: {\n ...ctx.styles.tier,\n fontFamily: KAI,\n fontSize: '1.0625em',\n letterSpacing: '0.34em',\n textIndent: '0.34em',\n color: 'transparent',\n backgroundImage: metal.gradient,\n WebkitBackgroundClip: 'text',\n backgroundClip: 'text',\n },\n }, ctx.tierText);\n}\n\n/** 组装一件:纸底/头部/档位语/饰线/三段(定稿不覆盖纸底与头部,故两者缺席=用 `seal-scroll.ts` 的原样式)。 */\nexport function paperParts(ctx: PaperCtx): PaperParts {\n return {\n paper: undefined,\n head: undefined,\n tier: tierNode(ctx),\n orn: ornament(ctx.styles, ctx.ruler),\n body: inlineRows(ctx.styles, ctx),\n };\n}\n","/** 印章卷轴 · 纯组件(`title-seal.ts` 的配套件,不认任何一家)。\n *\n * 来源:原型定稿 2026-10-05(记录见 `docs/plugins/plugin-manager/印章原型-定稿.md` 第九节)。\n * 点印后弹出的那块:**外框逐档照抄对应那枚印的材质**(底色+边线都照抄,不许简化成\"一圈金属色\"),\n * **中间是纯纸**,内容固定三段——进展/状态/计划。\n *\n * 三条形状(逐值照原型锁死):\n * 外框:厚 1.125em(16px 下=18px,这是下限;只给 10px 整张卡会读成\"白底加一圈边\"),\n * 材质随 `role`×`tier` 九档之一,挂 `roughFrame` 糙边滤镜。\n * 纸:`bg-layer-2` 底+`border-l1` 描边,压在材质上(外框那层在纸下面,只露一圈)。\n * 字:标题居中 1em/字距 .16em;档位字 .66em/字距 .42em;三段标签 .66em/字距 .34em;\n * 正文 .84em/行高 1.9;段间 1px 细线+.94em 上距。\n *\n * 颜色分治(照 `title-seal.ts` 的先例):\n * 印那一份(外框材质、档位字色)**固定色**——纸变印不变,印不管纸面派生;\n * 纸那一份(纸底、纸边、标题、正文、段间线)走 DSH 主题别名。\n *\n * 糙边滤镜两条分开:印是小块,`scale 2.2` 就够;外框有几百像素宽,同一条会被稀释到看不出,\n * 故外框单用 `scale 5` 的 `roughFrame`。滤镜只挂外框那层,纸不挂(否则纸的直边也会被抖)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\nimport type { SealRole, SealTier } from './config-panel-contract.js';\n// 卷轴标题直接复用印本体(与外面那枚同组件同 props,UI 天然一致)。\n// 与 `seal-stamp.ts` 是渲染期互引(双方只在组件函数体内用对方,无模块求值期依赖)。\nimport { SealStamp } from './seal-stamp.js';\n// 定稿的纸面字排与关闭钮样子(原型期方案已删,只此一件)。\nimport { closeLook, paperParts } from './seal-paper-schemes.js';\n\n/** 糙边滤镜 id(文档作用域,故每页只许挂一次 `SealFilterDefs`)。 */\nconst ROUGH_EDGE = 'dshLifeSealRoughEdge';\nconst ROUGH_FRAME = 'dshLifeSealRoughFrame';\n/** 中等件(轴杆/纸筒/绦带这类几十像素宽的件)那条糙边:2.2 太细、5 会抖过头。 */\nconst ROUGH_METAL = 'dshLifeSealRoughMetal';\n/** 印泥那条(行首那几枚小印):同 2.2 的抖动,末尾再加一点高斯模糊——印泥压在纸上边缘会洇开,\n * 光有抖动边只会读成「毛边贴纸」。字住在滤镜之外,所以糊的只有泥、不是字。 */\nconst ROUGH_INK = 'dshLifeSealInkEdge';\n\n/** 印那条糙边滤镜 id(`seal-stamp.ts` 引用,定义只此一处)。 */\nexport const SEAL_ROUGH_EDGE_ID = ROUGH_EDGE;\n\n/** 中等件那条糙边 id(关闭钮引用;定义只此一处)。 */\nexport const SEAL_ROUGH_METAL_ID = ROUGH_METAL;\n\n/** 印泥那条糙边 id(纸面行首小印引用;定义只此一处)。 */\nexport const SEAL_ROUGH_INK_ID = ROUGH_INK;\n\n\n/** 档位话:标题下那一行不再印档位字,直接说档位所处的阶段(渲染期用,顶层可放行)。 */\nexport const TIER_TEXT: Record<SealTier, string> = {\n gold: '精雕细琢中',\n silver: '全打通中',\n copper: '基础建设中',\n};\n\n/** 卷轴展开动画名(文档作用域,印前缀防撞;`S.popInner` 求值期即用,必须住 `S` 之前)。 */\nconst UNROLL_ANIMATION = 'dshLifeSealUnroll';\n\n/** 一档材质:外框那一份是固定色(印),档位字色也是固定色。 */\ninterface Material {\n /** 外框底色+边线(逐值照该枚印)。 */\n readonly frame: React.CSSProperties;\n /** 档位字与标签的色(印派生,固定色)。 */\n readonly accent: string;\n /** 分隔线同族色。 */\n readonly ruler: string;\n /** 四角铆钉(只有插件章的整面金属带有)。 */\n readonly rivet?: React.CSSProperties;\n}\n\n/** 纸面四色是印自带的(羊皮纸底+ availability 墨色),不跟宿主主题跑——\n * 深色下宿主变量会把纸染黑,故这里写死定稿值;字族仍继承(定稿零字族)。 */\nconst PAPER_BG = '#fdfaf2';\nconst PAPER_EDGE = '#e6d9bd';\nconst LABEL_MAIN = '#42506b';\nconst LABEL_SUB = '#4b4335';\n\n/** 九档材质(3 角色 × 3 档)。键就是原型里的 `role`/`tier`。 */\nconst MATERIALS: Record<string, Record<string, Material>> = {\n skill: {\n copper: {\n frame: {\n background: 'linear-gradient(180deg,#8a4a28,#6e3418)',\n border: '3px solid #a5652f',\n boxShadow: 'inset 0 0 0 1px #f8f1e2aa, inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d',\n },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n },\n silver: {\n frame: {\n background: 'linear-gradient(180deg,#b73124,#8e1f14)',\n border: '3px double #eef3f6',\n boxShadow: 'inset 0 0 0 1px #fff, inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d',\n },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n },\n gold: {\n frame: {\n background: 'linear-gradient(180deg,#d34a35,#a32216)',\n border: '3px double #f5d97a',\n boxShadow: 'inset 0 0 0 1px #fff8, inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d',\n },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n },\n },\n help: {\n copper: {\n frame: { background: 'linear-gradient(180deg,#b06a3a,#7e3f1d)', border: '3px solid #a5652f', boxShadow: 'inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d' },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n },\n silver: {\n frame: { background: 'linear-gradient(180deg,#8f979e,#5f666d)', border: '3px double #e8eef2', boxShadow: 'inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d' },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n },\n gold: {\n frame: { background: 'linear-gradient(180deg,#c63d2a,#a32216)', border: '3px double #f5d97a', boxShadow: 'inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d' },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n },\n },\n plugin: {\n copper: {\n frame: { background: 'linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)', boxShadow: 'inset 0 1px 0 #d99a5e88, inset 0 -2px 5px #0006, 0 0 0 1px #5e2c12, inset 0 2px 0 #ffffff26' },\n accent: '#9a5f2a',\n ruler: '#c08145b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)', boxShadow: 'inset 0 -1px 1px #00000055,0 1px 2px #00000088' },\n },\n silver: {\n frame: { background: 'linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)', boxShadow: 'inset 0 1px 0 #ffffffdd, inset 0 -2px 5px #0005, 0 0 0 1px #6f767c, inset 0 2px 0 #ffffff33' },\n accent: '#6f767c',\n ruler: '#9aa0a6b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077' },\n },\n gold: {\n frame: { background: 'linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)', boxShadow: 'inset 0 1px 0 #fff8d8dd, inset 0 -2px 5px #0005, 0 0 0 1px #8a6a15' },\n accent: '#9c7a16',\n ruler: '#d8b338b3',\n rivet: { background: 'radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788' },\n },\n },\n};\n\nconst S = {\n /** 外框(`.frame`):绝对铺满,挂糙边滤镜;纸压在它上面,只露一圈。 */\n frame: {\n position: 'absolute',\n inset: 0,\n borderRadius: '1em',\n zIndex: 1,\n filter: 'url(#' + ROUGH_FRAME + ')',\n } as React.CSSProperties,\n /** 四角铆钉(只有插件章的整面金属带有)。 */\n rivet: {\n position: 'absolute',\n width: '0.69em',\n height: '0.69em',\n borderRadius: '50%',\n } as React.CSSProperties,\n /** 卷轴根:padding 就是外框厚度(.frame 铺满、纸在 padding 内)。 */\n scroll: {\n position: 'relative',\n fontSize: '1em',\n lineHeight: 1.7,\n borderRadius: '0.875em',\n padding: '1.125em',\n } as React.CSSProperties,\n /** 纸(`.paper`):纯纸,无纹理。 */\n paper: {\n position: 'relative',\n zIndex: 2,\n borderRadius: '0.25em',\n background: PAPER_BG,\n boxShadow: '0 1px 3px #3d241052, 0 0 0 1px ' + PAPER_EDGE + ', inset 0 1px 0 #fffefb',\n } as React.CSSProperties,\n inner: {\n position: 'relative',\n // 上下内垫同值(28px):底比顶多 2px 会读成“底部空一截”,三段内容也不需要对位字基线。\n padding: '1.75em 2em',\n } as React.CSSProperties,\n head: { textAlign: 'center', marginBottom: '0.5em' } as React.CSSProperties,\n /** 档位语(标题下那一行):档位字退役,改说档位话(金=精雕细琢中/银=全打通中/铜=基础建设中)。 */\n tier: {\n marginTop: '0.4375em',\n fontSize: '0.66em',\n letterSpacing: '0.42em',\n textIndent: '0.42em',\n fontWeight: 600,\n } as React.CSSProperties,\n orn: {\n display: 'flex',\n alignItems: 'center',\n gap: '0.5625em',\n margin: '1em 0 1.25em',\n } as React.CSSProperties,\n ornLine: { flex: '1 1 auto', height: 1 } as React.CSSProperties,\n ornDot: { width: '0.3125em', height: '0.3125em', transform: 'rotate(45deg)', opacity: 0.75, flex: '0 0 auto' } as React.CSSProperties,\n block: { margin: '0 0 0.9375em' } as React.CSSProperties,\n blockDivided: { margin: '0 0 0.9375em', paddingTop: '0.9375em', borderTop: '1px solid ' + PAPER_EDGE } as React.CSSProperties,\n blockLast: { margin: 0 } as React.CSSProperties,\n label: {\n fontSize: '0.66em',\n letterSpacing: '0.34em',\n textIndent: '0.34em',\n fontWeight: 600,\n marginBottom: '0.375em',\n } as React.CSSProperties,\n text: {\n margin: 0,\n fontSize: '0.84em',\n lineHeight: 1.9,\n letterSpacing: '0.02em',\n color: LABEL_SUB,\n } as React.CSSProperties,\n /** 卷轴浮层:卡片内绝对定位(卡片 `S.card` 是 relative 定位祖先),上沿悬在三枚章之下、左右各留半 em——\n * 不是 modal:无遮罩、不锁滚动,点章切换、点「关闭」收起。 */\n dialog: { position: 'absolute', left: '0.5em', right: '0.5em', top: '8.8em', zIndex: 10, display: 'flex', justifyContent: 'center', pointerEvents: 'auto' } as React.CSSProperties,\n /** 浮层内框:**宽随内容**(`max-content`,正好包住最长那一行;不是固定 25em 那块大纸)。\n * `maxWidth:100%` 是窄卡的兜底(收满可用宽、永不捅破卡片),此时长行换行、高度自己长。\n * 挂卷轴展开动画(从上而下舒卷,240ms;纯声明式,无 hook,纯函数可直测)。 */\n popInner: { position: 'relative', width: 'max-content', maxWidth: '100%', transformOrigin: '50% 0', animation: UNROLL_ANIMATION + ' 240ms ease-out' } as React.CSSProperties,\n close: {\n position: 'absolute',\n right: '-0.375em',\n top: '-2.25em',\n font: 'inherit',\n fontSize: '0.75em',\n color: '#fff',\n background: '#241f19b3',\n border: 'none',\n borderRadius: '0.875em',\n padding: '0.25em 0.875em',\n cursor: 'pointer',\n } as React.CSSProperties,\n} as const;\n\n/** 卷轴要显示的东西:角色 × 档位 + 标题 + 三段文案。 */\nexport interface SealScrollProps {\n /** 角色(技能章/HELP 章/插件章;决定外框是不是整面金属带、有没有铆钉)。 */\n readonly role: SealRole;\n /** 档位(决定外框材质)。 */\n readonly tier: SealTier;\n /** 标题(印文那一行,如 `技能`/`饼干记账 HELP`/`插件`)。 */\n readonly title: string;\n /** 进展。 */\n readonly progress: string;\n /** 状态。 */\n readonly status: string;\n /** 计划。 */\n readonly plan: string;\n}\n\nfunction materialOf(role: SealScrollProps['role'], tier: SealScrollProps['tier']): Material {\n const byTier = MATERIALS[role];\n const found = byTier?.[tier];\n if (!found) throw new Error('未知的印档:' + role + '/' + tier);\n return found;\n}\n\n/** 糙边滤镜定义:两条分开(印用 edge,卷轴外框用 frame),每页只挂一次。\n *\n * 挂在文档任意位置都行(`width/height=0` 不占位),同页重复挂会撞 id。\n * 印章交互(悬停上浮高亮/按压回缩/焦点环/级别 tooltip)也住这里:scoped 类名,纯 CSS、无 hook。\n * 悬停高亮须把糙边 url 原样带上(filter 单属性,另写会把糙边冲掉)。 */\nexport function SealFilterDefs(): React.ReactElement {\n return React.createElement(\n React.Fragment,\n null,\n React.createElement('style', null,\n '.dshLifeSealBtn{position:relative;transition:transform 160ms ease-out,filter 160ms ease-out}' +\n '.dshLifeSealBtn:hover{transform:translateY(-2px);filter:url(#' + ROUGH_EDGE + ') brightness(1.12)}' +\n '.dshLifeSealBtn:active{transform:translateY(0) scale(.96)}' +\n '.dshLifeSealBtn:focus-visible{outline:2px solid #f5d97a;outline-offset:2px}' +\n '.dshLifeSealBtn::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);background:#241f19ee;color:#f8f1e2;font-size:.72em;letter-spacing:.1em;padding:.4em .9em;border-radius:.5em;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 150ms ease;z-index:30}' +\n '.dshLifeSealBtn:hover::after,.dshLifeSealBtn:focus-visible::after{opacity:1}'),\n React.createElement(\n 'svg',\n { width: 0, height: 0, 'aria-hidden': true, focusable: false, style: { position: 'absolute' } },\n React.createElement(\n 'filter',\n { id: ROUGH_EDGE, key: ROUGH_EDGE },\n React.createElement('feTurbulence', { baseFrequency: 0.9, numOctaves: 2, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 2.2 }),\n ),\n React.createElement(\n 'filter',\n { id: ROUGH_FRAME, key: ROUGH_FRAME, x: '-6%', y: '-6%', width: '112%', height: '112%' },\n // 0.6 + 3 倍频:一条低频底噪叠两层细纹 ⇒ 金边碎成大小不一的斑块(颗粒感、斑点感)。\n // 位移 6.5 让四边四角碎到同一档;斑块感来自那条低频,纯高频(0.9/2)只会得到一条细金粉线。\n React.createElement('feTurbulence', { baseFrequency: 0.6, numOctaves: 3, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 6.5 }),\n ),\n // 中等件(轴杆/纸筒/绦带):同一套湍流,位移取 3.2。\n React.createElement(\n 'filter',\n { id: ROUGH_METAL, key: ROUGH_METAL, x: '-8%', y: '-14%', width: '116%', height: '128%' },\n React.createElement('feTurbulence', { baseFrequency: 0.9, numOctaves: 2, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 3.2 }),\n ),\n // 印泥:抖动(毛边)→ 洇开(纸上晕)→ **噪点蒙版**(吃墨不匀,有几处纸没吃透)。\n // 最后那步是「像印泥」的关键:平涂+高光只会读成塑料,真正像泥的是浓淡不匀的覆盖率。\n React.createElement(\n 'filter',\n { id: ROUGH_INK, key: ROUGH_INK, x: '-25%', y: '-25%', width: '150%', height: '150%' },\n React.createElement('feTurbulence', { type: 'fractalNoise', baseFrequency: 0.9, numOctaves: 2, result: 'n' }),\n React.createElement('feDisplacementMap', { in: 'SourceGraphic', in2: 'n', scale: 2.2, result: 'd' }),\n React.createElement('feGaussianBlur', { in: 'd', stdDeviation: 0.3, result: 'b' }),\n React.createElement('feTurbulence', { type: 'fractalNoise', baseFrequency: 0.75, numOctaves: 3, result: 'g' }),\n // 覆盖率底数 .35:不满不满也不到透——印泥是「吃墨不匀」,不是「纱网」。\n React.createElement('feColorMatrix', { in: 'g', type: 'matrix', values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .7 .45 0 0 .35', result: 'mask' }),\n React.createElement('feComposite', { in: 'b', in2: 'mask', operator: 'in' }),\n ),\n ),\n );\n}\n\n/** 一卷:外框材质 + 纯纸 + 进展/状态/计划三段。 */\nexport function SealScroll(props: SealScrollProps): React.ReactElement {\n const material = materialOf(props.role, props.tier);\n // 【已还原】框面噪点层(grain/fleck)撤掉:它们整体提亮了红底,框色与原版不一致。\n // 需要“碎金”效果时再按需加回(原型留档见 .scratch)。\n const frameChildren =\n props.role === 'plugin' && material.rivet\n ? [\n React.createElement('span', { key: 'tl', style: { ...S.rivet, ...material.rivet, left: '0.4375em', top: '0.4375em' } }),\n React.createElement('span', { key: 'tr', style: { ...S.rivet, ...material.rivet, right: '0.4375em', top: '0.4375em' } }),\n React.createElement('span', { key: 'bl', style: { ...S.rivet, ...material.rivet, left: '0.4375em', bottom: '0.4375em' } }),\n React.createElement('span', { key: 'br', style: { ...S.rivet, ...material.rivet, right: '0.4375em', bottom: '0.4375em' } }),\n ]\n : [];\n const line = (key: string, dir: string): React.ReactElement =>\n React.createElement('span', {\n key,\n style: { ...S.ornLine, background: 'linear-gradient(' + dir + 'deg,#0000,' + material.ruler + ')' },\n });\n const block = (key: string, name: string, text: string, divided: boolean, last = false): React.ReactElement =>\n React.createElement(\n 'div',\n { key, style: last ? (divided ? { ...S.blockLast, paddingTop: '0.9375em', borderTop: '1px solid ' + PAPER_EDGE } : S.blockLast) : divided ? S.blockDivided : S.block },\n React.createElement('div', { style: { ...S.label, color: material.accent } }, name),\n React.createElement('p', { style: S.text }, text),\n );\n /** 纸面字排:定稿件直出五件。 */\n const parts = paperParts({\n tierKey: props.tier,\n tierText: TIER_TEXT[props.tier],\n progress: props.progress,\n status: props.status,\n plan: props.plan,\n accent: material.accent,\n ruler: material.ruler,\n paperEdge: PAPER_EDGE,\n styles: {\n head: S.head, paper: S.paper, tier: S.tier, orn: S.orn, ornLine: S.ornLine, ornDot: S.ornDot,\n block: S.block, blockDivided: S.blockDivided, blockLast: S.blockLast, label: S.label, text: S.text,\n },\n });\n return React.createElement(\n 'div',\n { style: S.scroll },\n React.createElement('div', { style: { ...S.frame, ...material.frame } }, ...frameChildren),\n React.createElement(\n 'div',\n { style: parts.paper === undefined ? S.paper : parts.paper },\n React.createElement(\n 'div',\n { style: S.inner },\n React.createElement(\n 'div',\n { style: parts.head === undefined ? S.head : parts.head },\n React.createElement(SealStamp, { role: props.role, tier: props.tier, label: props.title }),\n parts.tier,\n ),\n parts.orn,\n parts.body,\n ),\n ),\n );\n}\n\n/** 卷轴浮层:popover + 点击别处关闭 + 卷轴展开动画 + 关闭。`open` 为假时不渲染(纯函数,不收自己的状态)。 */\nexport function SealScrollDialog(\n props: SealScrollProps & {\n readonly open: boolean;\n readonly onClose: () => void;\n /** 锚点覆盖(面板传:贴在点击那枚章的下方);缺席=整卡居中那套老样子。 */\n readonly anchor?: React.CSSProperties | undefined;\n },\n): React.ReactElement | null {\n if (!props.open) return null;\n const scroll: SealScrollProps = {\n role: props.role,\n tier: props.tier,\n title: props.title,\n progress: props.progress,\n status: props.status,\n plan: props.plan,\n };\n return React.createElement(\n React.Fragment,\n null,\n // 收卷层:全屏透明,只收“点别处即关”,不 dim、不锁滚动(无 role,不算 modal 遮罩)。\n React.createElement('div', { style: { position: 'fixed', inset: 0, zIndex: 9, background: 'transparent' }, 'aria-hidden': true, onClick: props.onClose }),\n React.createElement(\n 'div',\n { style: props.anchor === undefined ? S.dialog : { ...S.dialog, ...props.anchor }, role: 'dialog', 'aria-modal': false, 'aria-label': props.title },\n // 展开关键帧住在这里(scoped 名,宿主无样式表可借时唯一一条纯声明式动画路)。\n React.createElement('style', null, '@keyframes ' + UNROLL_ANIMATION + '{from{opacity:0;transform:translateY(-10px) scaleY(.7)}to{opacity:1;transform:none}}'),\n React.createElement(\n 'div',\n { style: S.popInner },\n // #1160 T1:按压收缩+焦点双环(卷轴浮层渲染在卡片内,样式由卡片那枚 <style> 罩住;印章本体仍豁免)。\n (() => {\n const look = closeLook(props.tier);\n // zIndex 20:关闭钮要压在卷轴(框+纸)之上,否则骑在边上的那几档会被红框吃掉半边。\n return React.createElement('button', { type: 'button', className: 'dshLifeSealBtn', style: { zIndex: 20, ...look.style }, 'data-ilife-press': 'seal-close', 'data-ilife-close': 'tie', onClick: props.onClose }, look.decor ?? null, look.label);\n })(),\n React.createElement(SealScroll, scroll),\n ),\n ),\n );\n}\n","/** 九枚印章 · 纯组件(`title-seal.ts` 的配套件,不认任何一家)。\n *\n * 来源:验收页 `.scratch/seal-9-restore-approve-v1.html` v2(逐值照 `.scratch/seal-9-roles-v6.html`:\n * 技能横条 1/HELP 旧版 2b/插件双铆 3,渐变、边线、铆钉、糙边、字号 padding 全锁)。\n * 印文:技能签统一“技能”、插件签统一“插件”(印面不带档位字,档位只看颜色边线);\n * HELP 签印产品名加空格加大写 HELP。点击弹卷(`seal-scroll.ts` 的 `SealScrollDialog`,调用方持 open),\n * 故印是 `button`(`aria-haspopup=\"dialog\"`),hover 不出东西。\n *\n * 字号一律 em(16px 下与原型 px 逐值对齐:1.3125em=21px、1.0625em=17px);\n * 糙边走印那条(`SEAL_ROUGH_EDGE_ID`,定义只在 `seal-scroll.ts`,此处引用)。\n *\n * 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。\n */\n\nimport * as React from 'react';\nimport { SEAL_ROUGH_EDGE_ID, TIER_TEXT } from './seal-scroll.js';\nimport type { SealRole, SealTier } from './config-panel-contract.js';\n\n/** 印的角色与档位见 `config-panel-contract.ts`(定义只那一处,此处引用)。 */\n\nexport interface SealStampProps {\n /** 角色:skill=技能横条/help=HELP 旧版 2b/plugin=插件双铆。 */\n readonly role: SealRole;\n /** 档位:copper=铜/silver=银/gold=金。 */\n readonly tier: SealTier;\n /** 印文那一行(技能/某 HELP/插件)。 */\n readonly label: string;\n readonly onSelect?: (() => void) | undefined;\n}\n\n/** 三角色底座(字号 padding 随角色,材质随档位)。 */\nconst ROLE_BASE: Record<SealRole, React.CSSProperties> = {\n skill: { fontSize: '1.0625em', padding: '0.5em 1.75em', letterSpacing: '.14em', borderRadius: '3px' },\n help: { fontSize: '1.0625em', padding: '0.5em 1em', letterSpacing: '.14em', borderRadius: '3px', color: '#f8f1e2' },\n plugin: { position: 'relative', justifyContent: 'center', fontSize: '1.0625em', padding: '0.5em 2em', letterSpacing: '.18em', borderRadius: '4px', color: '#f8f1e2' },\n};\n\n/** 九档印面(底色加边线都照抄对应那枚印,不许简化)。 */\nconst FACE: Record<SealRole, Record<SealTier, React.CSSProperties>> = {\n skill: {\n copper: { background: 'linear-gradient(180deg,#8a4a28,#6e3418)', color: '#f8f1e2', border: '3px solid #a5652f', boxShadow: 'inset 0 0 0 1px #f8f1e2aa' },\n silver: { background: 'linear-gradient(180deg,#b73124,#8e1f14)', color: '#f8f1e2', border: '3px double #eef3f6', boxShadow: 'inset 0 0 0 1px #fff' },\n gold: { background: 'linear-gradient(180deg,#d34a35,#a32216)', color: '#fff8e0', border: '3px double #f5d97a', boxShadow: 'inset 0 0 0 1px #fff8,0 4px 16px #c9a22755' },\n },\n help: {\n copper: { background: 'linear-gradient(180deg,#b06a3a,#7e3f1d)', border: '3px solid #a5652f' },\n silver: { background: 'linear-gradient(180deg,#8f979e,#5f666d)', border: '3px double #e8eef2' },\n gold: { background: 'linear-gradient(180deg,#c63d2a,#a32216)', border: '3px double #f5d97a' },\n },\n plugin: {\n copper: { background: 'linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)' },\n silver: { background: 'linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)' },\n gold: { background: 'linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)' },\n },\n};\n\n/** 插件整面金属带的描边(随档)。 */\nconst BAND_EDGE: Record<SealTier, React.CSSProperties> = {\n copper: { boxShadow: 'inset 0 1px 0 #d99a5e88,inset 0 -2px 5px #0006,0 0 0 1px #5e2c12' },\n silver: { boxShadow: 'inset 0 1px 0 #ffffffdd,inset 0 -2px 5px #0005,0 0 0 1px #6f767c' },\n gold: { boxShadow: 'inset 0 1px 0 #fff8d8dd,inset 0 -2px 5px #0005,0 0 0 1px #8a6a15,0 3px 14px #c9a22766' },\n};\n\n/** 插件左右铆钉(随档,两颗)。 */\nconst RIVET: Record<SealTier, React.CSSProperties> = {\n copper: { background: 'radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)', boxShadow: 'inset 0 -1px 1px #00000055,0 1px 2px #00000088' },\n silver: { background: 'radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077' },\n gold: { background: 'radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)', boxShadow: 'inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788' },\n};\n\n/** 一枚印章(button,点章弹卷)。未知组合直接抛错,不静默退化。 */\nexport function SealStamp(props: SealStampProps): React.ReactElement {\n const face = FACE[props.role][props.tier];\n if (face === undefined) throw new Error('未知印章组合:' + String(props.role) + '/' + String(props.tier));\n const style: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n cursor: 'pointer',\n fontWeight: 800,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n filter: 'url(#' + SEAL_ROUGH_EDGE_ID + ')',\n ...ROLE_BASE[props.role],\n ...face,\n };\n // 悬停 tooltip 讲清级别含义(档位话即完善进度);样式住 `SealFilterDefs` 旁那枚 scoped `<style>`,纯 CSS、无 hook。\n const tierName = props.tier === 'copper' ? '铜' : props.tier === 'silver' ? '银' : '金';\n const buttonProps = { type: 'button' as const, style, className: 'dshLifeSealBtn', 'aria-label': props.label, 'aria-haspopup': 'dialog' as const, 'data-tip': tierName + '章 · ' + TIER_TEXT[props.tier], onClick: props.onSelect };\n if (props.role !== 'plugin') return React.createElement('button', buttonProps, props.label);\n const band: React.CSSProperties = { position: 'absolute', top: '-1px', bottom: '-1px', left: '-1px', right: '-1px', zIndex: 1, borderRadius: '4px', ...BAND_EDGE[props.tier] };\n const rivet: React.CSSProperties = { position: 'absolute', zIndex: 3, width: '0.5em', height: '0.5em', borderRadius: '50%', top: '50%', transform: 'translateY(-50%)', ...RIVET[props.tier] };\n return React.createElement('button', buttonProps,\n React.createElement('span', { style: { ...band, background: (face.background as string) }, 'data-seal-band': '1' }),\n React.createElement('span', { style: { ...rivet, left: '0.5em' }, 'data-seal-rivet': 'left' }),\n React.createElement('span', { style: { ...rivet, right: '0.5em' }, 'data-seal-rivet': 'right' }),\n React.createElement('span', { style: { position: 'relative', zIndex: 2, textShadow: '0 1px 2px #00000088' } }, props.label),\n );\n}","/** 共用配置面板 · 视图半:一行怎么画(`Row`)、整面怎么画(`PanelBody`)。\n *\n * 本件是**纯函数**:吃一份状态与一组回调,回一棵 React 元素树,自己不留任何状态、不取任何数。\n * 为什么这么分:状态机与副作用住在 `config-panel.ts` 那一层,于是本件在 Node 里就能直测\n * (照 `directory-browser-ui.ts` 的既有作法:用例把组件当普通函数调,直接读回那棵树)。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX)。样式表 `S` 就在本件里——面板怎么画只有这一处。\n *\n * **外观真源=`docs/agents/配置区域视觉模型-v3.1.html`(#920 阶段二起)**:本件的 `S` 逐项照它的\n * CSS(`:32-205`)、行模板照它的 `rowShell`/`masterShell`/`cardShell`(`:349-451`)、状态徽标照它的\n * `badgeHTML`(`:544-552`)那七档。**颜色/间距/圆角/字号/层级一律照取值,不许自己重新设计**;\n * 与本件不同的只有下面这四条(每条都在这里写明为什么,改之前先读 #920 的证据件):\n * ① **字号一律 em,没有 px**:跨包锁 `test/panel-type-739.test.mjs` 钉死「零 `font:` 简写/零字族/\n * 字号一律相对单位」——宿主没有界面字号 token,绝对 px 会在别的窗口尺寸上失真。v3.1 的 px 按它\n * 自己已锁死的那两对锚点换算(`13.5px↔1.08em`、`11.5px↔0.92em` ⇒ **1px=0.08em**),一个不丢。\n * ② **值列不写等宽字族**:v3.1 的值列要 `font-family: var(--ic-mono)`,而 `#739 ①` 明令共用件零\n * `fontFamily`/零 `font:` 简写 ⇒ 值继承宿主字族。这是与 v3.1 的**一处已知不同**(#920 证据件有账)。\n * ③ **换行判据交给 CSS,不按字符数估宽**:v3.1 靠 JS 量文本宽在两套模板间两选一(审查 P0-4/P0-5:\n * 每列 6.048px、真阈值 40–43 列而注释按 34 估 ⇒ 9 行被无谓拆两行、快面一次性长高 194px)。\n * 本件没有 DOM 可量,故照审查给的修法把同一个决定交给排版引擎:行是 `flex-wrap` 容器,标签列定宽、\n * 值列 `flex-basis:auto`(=内容的自然宽)——装不下时**整行让位**(值落第二行、按钮仍在第一行行尾,\n * 与 v3.1 的 `.is-wide` 同形);值再长就在自己那一行里 `overflow-wrap:anywhere` 折下去。\n * **零省略号、零截断**(`white-space` 不用 `nowrap`、`text-overflow` 一次都不出现)。\n * ④ **画不出来的三样**用等价形态顶上:分组箭头 `::before`/`[open]` 旋转/keyframes 动画内联样式\n * 给不出 ⇒ 画静态「›」与静态环(形状与 v3.1 同,只少了动)。\n *\n * 三条定稿(v3)在本件落地:\n * ① **只读行的目录按钮不画**(不画一枚点了也没反应的死按钮);\n * ② 目录行的按钮字面**两档合一**,一律「浏览文件夹」(入口供不了时整枚不画);\n * ③ **每行一枚「复制」**:复制这一行完好的值文本;点下去按钮就地变「已复制」再变回,不弹提示。\n *\n * 对外只经 `config-panel-api.ts` 那一道门;本件自己不出门。\n */\n\nimport * as React from 'react';\nimport { ADVANCED_CHEV_MS, ADVANCED_CHEV_SHIFT_PX, ADVANCED_GROUP_TITLE, ADVANCED_OPEN_MAX_PX, ADVANCED_OPEN_MS, DISABLED_OPACITY, DISABLED_SHAKE_MS, FOCUS_RING_GAP_PX, FOCUS_RING_WIDTH_PX, HOVER_GLOW_EDGE_PERCENT, HOVER_GLOW_SOFT_PERCENT, HOVER_LIFT_PX, HOVER_SPOTLIGHT_RADIUS_PX, HOVER_WASH_PERCENT, INTERACTION_TRANSITION, LIQUID_GHOST_MS, LONGPRESS_MS, LONGPRESS_RING_HEIGHT_PX, NOTICE_FADE_MS, NOTICE_RISE_PX, PRESS_SCALE, SPOTLIGHT_FADE_MS, SPOTLIGHT_PEAK_PERCENT, TAB_DOT_PULSE_MS, TAB_HOVER_WASH_PERCENT, TAB_TRANSITION } from './config-panel-contract.js';\nimport type { ConfigItem, ConfigSurfaceReply } from './config-panel-contract.js';\nimport { DirectoryBrowserFromRow } from './directory-browser-ui.js';\nimport type { DirectoryRowBrowser } from './directory-browser-state.js';\nimport type { DirectoryRowEntry } from './directory-browser-api.js';\nimport { TitleBlock } from './title-seal.js';\nimport { SealStamp } from './seal-stamp.js';\nimport { SealFilterDefs, SealScrollDialog } from './seal-scroll.js';\nimport type { SealInstance } from './config-panel-contract.js';\n\n/** 面板的样式表:取值逐项照 v3.1(颜色走 DSH 主题别名,写死值只做回退;字号一律相对单位 em)。\n *\n * 键名一个都不许改:六家附加块真消费 `okText`/`error`/`rows`/`label`/`muted`/`hint`/`info`/\n * `bar`/`btn` 这九格(`plugin-memo-ilife/src/client.ts`、`plugin-schedule-ilife/src/client.ts` 的\n * `PanelStyleSlots`),跨包锁另钉 `card`/`input`/`btnPrimary`/`btnPick`/`summary`/`title`/`version`。 */\n/** #941/丁:**浅色档距自合成**——宿主在浅色下四层底同白(`bg-base = bg-layer-1/2/3`),\n * 拿不到可用的层级差,所以三处面(卡描边/沉底盒底/输入框描边)由我们自己按墨色派生。\n *\n * 取 `label-primary` 当墨色分母:浅色下它是近黑、深色下它是近白 ⇒ **同一条表达式两套主题都成立**\n * (浅色补出可用的档距,深色退化成今天那几档观感)。百分比来自维护者选定的方案丁:\n * 卡描边 19%/沉底盒底 9%/输入框描边 24%/骨架条 21%。这是本仓第一处\"自定值\",属真源增补\n * (v3.1 只有深色一套 token,从未覆盖浅色)。 */\nconst PANEL_INK = 'var(--dsw-alias-label-primary, #f9fafb)';\nconst inkMix = (percent: number, into?: string): string =>\n into === undefined\n ? `color-mix(in srgb, ${PANEL_INK} ${percent}%, transparent)`\n : `color-mix(in srgb, ${PANEL_INK} ${percent}%, ${into})`;\n/** 卡片描边:浅色 ≈`#cfcfcf` ⇔ 深色 ≈`#4a4b4d`。 */\nconst PANEL_CARD_EDGE = inkMix(19);\n/** 沉底盒底(母版行/高级组体/状态行):浅色 ≈`#e9e9e9` ⇔ 深色 ≈`#3a3b3d`。 */\nconst PANEL_SUNKEN_FILL = inkMix(9, 'var(--dsw-alias-bg-layer-1, #232324)');\n/** 输入框描边:浅色 ≈`#c4c4c4` ⇔ 深色 ≈`#5a5b5d`。 */\nconst PANEL_FIELD_EDGE = inkMix(24);\n/** 骨架条:浅色 ≈`#c9ccd2`(1.6:1)⇔ 深色 ≈`#404143`。 */\nconst PANEL_SKELETON_FILL = inkMix(21);\n\n/** T1 按压与焦点(#1160):原型 V1 的两个真实行为,样式只此一处生成。\n *\n * 用法:按钮加 `data-ilife-press`(按压+焦点双环),输入框加 `data-ilife-focus`\n *(仅焦点双环,文本框不收缩)。`PanelBody` 在卡片首位挂一枚 `<style>`,选择器按属性名收敛、\n * 只罩本卡内的这两类,不漏到宿主别的按钮上。数值全引契约四格(按压/隔离/宽度/过渡),\n * 颜色全走主题变量(隔离环取卡面底,焦点环取 `--ilife-focus` 退品牌色,永不写死蓝)。\n * 悬停/长按/折叠/复制变勾/提示/液态归后票,本函数只出按压与焦点。\n *\n * T2 追加(#1161):悬停聚光+淡洗+辉光+上浮、禁用摇头+禁止指针、长按 500ms 纯视觉环+回弹、\n * 无障碍三约束(触屏降级、键盘焦点可见、减少动态静态替代)——数值全引契约 T2 十格,详见函数内注释。\n *\n * T3 追加(#1162):高级折叠(220ms 分级入场+箭头旋转 200ms+悬停右移 2px)+轻提示渐显\n * (180ms 上浮 8px)+低端机(update: slow)静态兜底——数值全引契约 T3 六格;\n * 液态拉伸影子消散归 #1165(本函数不动页签,页签静态层见 tabInteractionCss)。 */\n// T2 焦点色变量(与 T1 行内字面逐字同值;T1 那一行保持字面不变——像素脚本按源码窗口断言它,改动会误报)。\nconst T2_FOCUS_VAR = 'var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))';\nexport function interactionCss(): string {\n return (\n '[data-ilife-press]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-focus]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-press]:active{transform:scale(' + String(PRESS_SCALE) + ')}' +\n '[data-ilife-press]:focus-visible,[data-ilife-focus]:focus-visible{outline:none;' +\n 'box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))}' +\n '[data-ilife-press]{position:relative;overflow:hidden}' +\n '[data-ilife-press]:hover:not(:disabled){background:color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_WASH_PERCENT) + '%, transparent);' +\n 'border-color:' + T2_FOCUS_VAR + ';' +\n 'box-shadow:0 0 0 1px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_EDGE_PERCENT) + '%, transparent),' +\n '0 4px 16px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_SOFT_PERCENT) + '%, transparent);' +\n 'transform:translateY(-' + String(HOVER_LIFT_PX) + 'px)}' +\n '[data-ilife-press]:active:hover:not(:disabled){transform:scale(' + String(PRESS_SCALE) + ')}' +\n '[data-ilife-press]:focus-visible:hover:not(:disabled){box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px ' + T2_FOCUS_VAR + ',' +\n '0 4px 16px color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(HOVER_GLOW_SOFT_PERCENT) + '%, transparent)}' +\n '[data-ilife-press]::before{content:\"\";position:absolute;inset:0;' +\n 'background:radial-gradient(' + String(HOVER_SPOTLIGHT_RADIUS_PX) + 'px circle at var(--mx,50%) var(--my,50%),' +\n 'color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(SPOTLIGHT_PEAK_PERCENT) + '%, transparent),transparent 65%);' +\n 'opacity:0;transition:opacity ' + String(SPOTLIGHT_FADE_MS / 1000) + 's ease;pointer-events:none}' +\n '[data-ilife-press]:hover:not(:disabled)::before{opacity:1}' +\n '[data-ilife-press]:disabled{cursor:not-allowed;opacity:' + String(DISABLED_OPACITY) + '}' +\n '[data-ilife-press]:disabled:hover{animation:ilifeShake ' + String(DISABLED_SHAKE_MS / 1000) + 's ease}' +\n '@keyframes ilifeShake{0%,100%{transform:translateX(0)}25%{transform:translateX(-2px)}75%{transform:translateX(2px)}}' +\n '[data-ilife-longpress]{position:relative;overflow:hidden}' +\n '[data-ilife-longpress]::after{content:\"\";position:absolute;left:0;bottom:0;height:' + String(LONGPRESS_RING_HEIGHT_PX) + 'px;width:0;' +\n 'background:' + T2_FOCUS_VAR + ';pointer-events:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:ilifeLongRing ' + String(LONGPRESS_MS / 1000) + 's linear forwards}' +\n '@keyframes ilifeLongRing{from{width:0}to{width:100%}}' +\n '[data-ilife-advanced=\"head\"]{transition:' + INTERACTION_TRANSITION + '}' +\n '[data-ilife-advanced=\"mark\"]{display:inline-block;transition:transform ' + String(ADVANCED_CHEV_MS / 1000) + 's ease}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"mark\"]{transform:rotate(90deg)}' +\n '[data-ilife-advanced=\"head\"]:hover [data-ilife-advanced=\"mark\"]{color:' + T2_FOCUS_VAR + ';transform:translateX(' + String(ADVANCED_CHEV_SHIFT_PX) + 'px)}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:ilifeFoldIn ' + String(ADVANCED_OPEN_MS / 1000) + 's ease}' +\n '@keyframes ilifeFoldIn{from{max-height:0;opacity:0}to{max-height:' + String(ADVANCED_OPEN_MAX_PX) + 'px;opacity:1}}' +\n '[data-ilife-notice=\"line\"]{animation:ilifeToastIn ' + String(NOTICE_FADE_MS / 1000) + 's ease}' +\n '@keyframes ilifeToastIn{from{opacity:0;transform:translateY(' + String(NOTICE_RISE_PX) + 'px)}to{opacity:1;transform:none}}' +\n '@media (hover:none){[data-ilife-press]:hover:not(:disabled){transform:none}[data-ilife-press]::before{display:none}}' +\n '@media (prefers-reduced-motion:reduce){[data-ilife-press],[data-ilife-focus]{transition:none}' +\n '[data-ilife-press]:disabled:hover{animation:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:none;width:100%}' +\n '[data-ilife-advanced=\"mark\"]{transition:none}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:none}' +\n '[data-ilife-notice=\"line\"]{animation:none}}' +\n '@media (update: slow){[data-ilife-press],[data-ilife-focus]{transition:none}' +\n '[data-ilife-press]::before{display:none}' +\n '[data-ilife-press]:disabled:hover{animation:none}' +\n '[data-ilife-longpress]:active:not(:disabled)::after{animation:none;width:100%}' +\n '[data-ilife-advanced=\"mark\"]{transition:none}' +\n '[data-ilife-advanced=\"group\"][open] [data-ilife-advanced=\"body\"]{animation:none}' +\n '[data-ilife-notice=\"line\"]{animation:none}}'\n );\n}\n\n/** T6 页签条交互(#1165,唯一生成处;载体 `client.ts` 页签条消费,不自画)。\n *\n * 原型 V1 页签四件:悬停淡洗 12%+辉光边框(无上浮、无聚光,按钮那套不串用)、圆点呼吸 .8s\n *(悬停驱动,scale 1→1.5/opacity 1→.6)、焦点双环(沿用 T1 2px+5px,跟主题色,同色页签可见)、\n * 选中态透明(glider 在下层给 pill,页签自身透明;减少动态时 CSS 以 !important 回实心 pill)。\n * 液态彗星式由载体 `liquidTo` 驱动(拉伸+影子消散+时长公式见契约 T6 tokens),本函数只出静态层:\n * glider-layer 定位+goo 滤镜引用、ghost 影子过渡(180ms)、glider/ghost 底色(主题变量)。\n * 数值全引契约 T6 四格(淡洗/呼吸/过渡/影子),颜色永不写死蓝(缺席退橙 #f6ad55)。\n * 低端机模糊降级(#1162):update: slow 下 glider 层(含 goo 模糊)关闭、选中态回实心 pill(与减少动态同形)。 */\nexport function tabInteractionCss(): string {\n return (\n '[data-ilife-tablist]{position:relative;isolation:isolate}' +\n '[data-ilife-glider-layer]{position:absolute;inset:0;z-index:0;filter:url(#ilife-goo);pointer-events:none}' +\n '[data-ilife-glider],[data-ilife-ghost]{position:absolute;top:0;left:0;border-radius:999px;background:' + T2_FOCUS_VAR + '}' +\n '[data-ilife-ghost]{opacity:0}' +\n '[data-ilife-tab]{position:relative;z-index:1;transition:' + TAB_TRANSITION + '}' +\n '[data-ilife-tab]:hover{background:color-mix(in srgb, ' + T2_FOCUS_VAR + ' ' + String(TAB_HOVER_WASH_PERCENT) + '%, transparent);' +\n 'border-color:' + T2_FOCUS_VAR + '}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:ilifeDotPulse ' + String(TAB_DOT_PULSE_MS / 1000) + 's ease infinite}' +\n '@keyframes ilifeDotPulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.5);opacity:.6}}' +\n '[data-ilife-tab]:focus-visible{outline:none;' +\n 'box-shadow:0 0 0 ' + String(FOCUS_RING_GAP_PX) + 'px var(--dsw-alias-bg-layer-1, #232324),' +\n '0 0 0 ' + String(FOCUS_RING_WIDTH_PX) + 'px ' + T2_FOCUS_VAR + '}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:transparent;border-color:transparent}' +\n '[data-ilife-ghost]{transition:opacity ' + String(LIQUID_GHOST_MS / 1000) + 's ease}' +\n '@media (hover:none){[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}}' +\n '@media (prefers-reduced-motion:reduce){[data-ilife-tablist] [data-ilife-glider-layer]{display:none}' +\n '[data-ilife-tab]{transition:none}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))!important;border-color:transparent!important}}' +\n '@media (update: slow){[data-ilife-tablist] [data-ilife-glider-layer]{display:none}' +\n '[data-ilife-tab]{transition:none}' +\n '[data-ilife-tab]:hover [data-ilife-health=\"tab-dot\"]{animation:none}' +\n '[data-ilife-tab][aria-selected=\"true\"]{background:var(--ilife-focus, var(--dsw-alias-brand-primary, #f6ad55))!important;border-color:transparent!important}}'\n );\n}\n\nconst S = {\n /** `.ic-card`:`background:var(--ic-surface)`(=bg-layer-1)+`border:1px solid var(--ic-line-strong)`\n * +`border-radius:12px`+`padding:14px`+`box-shadow:inset 0 1px 0 rgba(255,255,255,.06)`。\n * 基准字号**不钉在卡片上**(#739 ⑤:跟着宿主走)。 */\n card: {\n position: 'relative',\n padding: 14,\n borderRadius: 12,\n // #941:卡片的边界在浅色下原本等于没有(四层底同白)⇒ 改成按墨色派生的可见描边。\n border: '1px solid ' + PANEL_CARD_EDGE,\n background: 'var(--dsw-alias-bg-layer-1, #232324)',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.06)',\n } as React.CSSProperties,\n /** `.ic-head{display:flex;align-items:center;gap:10px;min-height:22px}`(徽标靠 `margin-left:auto` 右贴)。\n * #996:加 `flexWrap:wrap`——标题(`TitleBlock`,`flex:1 1 auto`+`minWidth:9em`)与签\n * (`flex-shrink:0`)在 390 宽下换行不挤(原型 A 行尾悬签);`gap`/`minHeight` 照 v3.1 不动。 */\n head: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 10, minHeight: 22 } as React.CSSProperties,\n /** 印覆盖层(1158 红框区):卡片右上绝对区,只罩标题+头两行(底栏永远罩不到);本身镂空穿透,印各自可点、缝隙漏过。 */\n sealOverlay: { position: 'absolute', top: 0, right: 0, width: '38%', height: '9em', pointerEvents: 'none', zIndex: 5 } as React.CSSProperties,\n /** `.ic-title{font-size:13.5px;font-weight:640}`——字号按 60 行那条锚点换算成 `1.08em`。\n * #996:卡片标题行改用 `TitleBlock`(见 `title-seal.ts`,原型 A 锁 1.15em/700+0.85em/400),\n * 这一格保留供附加块(`ConfigStyles` 键名不许改),头部本身不再用它。 */\n title: { fontSize: '1.08em', fontWeight: 640 } as React.CSSProperties,\n /** `.ic-file{margin-top:4px}` 的那 4px 挂在两行的公共容器上:`info` 自己一格 margin 都不写\n * (#743 ①:头部两行间距不一家一个数,故这一项一律不写 margin)。 */\n fileWrap: { marginTop: 4 } as React.CSSProperties,\n /** #939:骨架条(照真源 `.ic-sk`:`display:block;height:17px;border-radius:4px`)。\n * 高度按一行正文画——inline-block 跟着父级行高走会让骨架行比填充后的文字行高 4px、六家叠起来就是一片下移。\n * 底色走派生值(宿主专用骨架档 `bg-skeleton` 在浅色下只有 1.09:1,等于看不见)。 */\n skeleton: {\n display: 'block',\n height: 17,\n borderRadius: 4,\n background: PANEL_SKELETON_FILL,\n } as React.CSSProperties,\n /** #936:状态行那一块(照真源 `.ic-status`)——沉底面盒子,里面三行:状态行/路径行/官网行。 */\n statusBox: {\n marginTop: 11,\n padding: '10px 12px',\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n } as React.CSSProperties,\n /** `.ic-srow{display:flex;align-items:center;gap:8px;font-size:12px;flex-wrap:wrap}`(12px ⇒ 0.86em)。 */\n statusRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n fontSize: '0.86em',\n flexWrap: 'wrap',\n } as React.CSSProperties,\n /** `.ic-dot{width:8px;height:8px;border-radius:50%}`——底色按档位在组件里补。 */\n statusDot: { width: 8, height: 8, borderRadius: '50%', flex: '0 0 auto' } as React.CSSProperties,\n /** `.ic-sname{font-weight:600;white-space:nowrap}`。 */\n statusName: { fontWeight: 600, whiteSpace: 'nowrap' } as React.CSSProperties,\n /** `.ic-stxt{color:var(--ic-muted);min-width:0}`。 */\n statusText: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)', minWidth: 0 } as React.CSSProperties,\n /** `.ic-sfind{display:flex;align-items:center;gap:8px;margin-top:7px}`。 */\n statusFind: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 7 } as React.CSSProperties,\n /** `.ic-spath{font-size:10.5px;color:var(--ic-faint);white-space:nowrap;overflow:hidden}`\n * ——单行不折、超出裁掉(**不写省略号**:本件有一条\"零省略号\"的跨包锁)。\n * **不写 `fontFamily`**:v3.1 这一格要 `var(--ic-mono)`,但 `#739 ①` 明令共用件零字族(宿主没有界面字族\n * token,钉死会在别的窗口尺寸上失真)——与真源的同一处已知不同,账在 #920 证据件里。 */\n statusPath: {\n fontSize: '0.75em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n minWidth: 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n } as React.CSSProperties,\n /** `.ic-ver{font-size:10px;border:1px solid var(--ic-line-strong);border-radius:999px;padding:0 6px}`。 */\n statusVersion: {\n fontSize: '0.72em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 999,\n padding: '0 6px',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-url{display:inline-block;margin-top:7px;font-size:10.5px;color:var(--ic-faint)}`\n * ——字族同样继承宿主(`#739 ①`:共用件零 `fontFamily`),理由同上一格。 */\n statusUrl: {\n display: 'inline-block',\n marginTop: 7,\n fontSize: '0.75em',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n } as React.CSSProperties,\n /** `.ic-file b{color:var(--ic-faint);font-weight:400;margin-right:5px}`(「配置文件」「数据目录」那两个字)。 */\n fileLabel: {\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n fontWeight: 400,\n marginRight: 5,\n } as React.CSSProperties,\n /** `.ic-file{color:var(--ic-muted);font-size:11.5px;word-break:break-all}`(11.5px ⇒ `0.92em`)。 */\n info: {\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n fontSize: '0.92em',\n wordBreak: 'break-all',\n } as React.CSSProperties,\n /** `.ic-muted` 那一档(次要字)。 */\n muted: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)', fontSize: '0.92em' } as React.CSSProperties,\n /** 错误字:取 v3.1 的 `--ic-danger`;字号 `1em`(#739 钉死)。 */\n error: {\n marginTop: 8,\n color: 'var(--dsw-alias-state-error-primary, #e0685f)',\n fontSize: '1em',\n whiteSpace: 'pre-wrap',\n } as React.CSSProperties,\n /** 成功字:取 v3.1 的 `--ic-ok`;字号 `0.96em`(#739 钉死)。 */\n okText: {\n marginTop: 8,\n color: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n fontSize: '0.96em',\n } as React.CSSProperties,\n /** 行组的容器(六家附加块也拿它当外框;v3.1 没有对应类,故只给一个中性块)。 */\n rows: { display: 'block' } as React.CSSProperties,\n /** `.ic-row`:三列栅格 + `gap:4px 10px` + `padding:7px 0` + 一条 `.ic-line` 下边线。\n * 本件把它落成**换行弹性盒**(见文件头 ③):标签定宽在第一行,值装不下时整行让位,\n * 动作槽绝对定位贴在行尾(`.ic-row .ic-slot[data-slot^=\"a:\"]` 那一格)。 */\n row: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0',\n borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n } as React.CSSProperties,\n /** `.ic-row:last-child{border-bottom:0}`——内联样式没有 `:last-child`,由画它的人明说。 */\n rowLast: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0',\n } as React.CSSProperties,\n /** `.ic-under{margin:9px 0 0 8px;border-left:1px solid var(--ic-line-strong);padding-left:13px}`:\n * **跟随行**相对它的可改行(母版行)缩进 8+1+13=**22px**,左边挂一条线。\n *\n * 取值逐字照抄;只有一处形态差别要说清:`.ic-under` 在 v3.1 里是**一整块**(同一个可改行的一批\n * 跟随者共用一个 div、共一条左边线、块首还有 9px 上边距),而共用件手上只有「谁是跟随行」这一个\n * 集合(由六家交的 `followKeysOf` 钩子逐键探出),**没有**「这一行归哪一块」的分块归属\n * (同一个键可以同时挂在两张跟随表上,钩子只回一个去重后的并集)。故这里按**行**落这条缩进:\n * 缩进量与左边线同值,块首那 9px 上边距不落(落下去会在每两行之间多出一道 9px 的缝)。 */\n followerRow: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0 7px 13px',\n marginLeft: 8,\n borderLeft: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n } as React.CSSProperties,\n /** 跟随行的最后一行:同上,去掉下边线(照 `.ic-row:last-child{border-bottom:0}`)。 */\n followerRowLast: {\n position: 'relative',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '4px 10px',\n padding: '7px 0 7px 13px',\n marginLeft: 8,\n borderLeft: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n } as React.CSSProperties,\n /** `.ic-row .ic-k{font-size:11.5px;color:var(--ic-muted)}`;`--ic-kw:118px` 是标签列的定宽。 */\n rowKey: {\n flex: '0 0 auto',\n minWidth: 118,\n fontSize: '0.92em',\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n } as React.CSSProperties,\n /** `.ic-row .ic-v{font-size:11px;min-width:0}`(11px ⇒ `0.88em`)+ 文件头 ③ 的让位口径:\n * `flex:1 1 auto` ⇒ 装不下整行让位;`overflow-wrap:anywhere` ⇒ 折下去、零省略号;\n * `paddingRight` 给行尾那枚按钮(+列间距)让出宽度——就是 `.ic-row` 第三列那一格。 */\n rowValue: {\n flex: '1 1 auto',\n minWidth: 0,\n paddingRight: 56,\n fontSize: '0.88em',\n whiteSpace: 'normal',\n overflowWrap: 'anywhere',\n lineHeight: 1.5,\n } as React.CSSProperties,\n /** 行尾动作槽(复制/跟随标记):绝对定位贴行尾,行一换行它仍留在第一行行尾(=`.is-wide` 那一形)。 */\n rowActs: {\n position: 'absolute',\n right: 0,\n top: 7,\n display: 'flex',\n gap: 4,\n alignItems: 'center',\n } as React.CSSProperties,\n /** `.ic-follow{font-size:11px;color:var(--ic-attn);white-space:nowrap}`(11px ⇒ `0.88em`)。 */\n follow: {\n fontSize: '0.88em',\n color: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n whiteSpace: 'nowrap',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n } as React.CSSProperties,\n /** `.ic-follow::before{content:'↳';opacity:.8}`——内联样式给不出伪元素,画成真的那一个字符。 */\n followMark: { opacity: 0.8 } as React.CSSProperties,\n /** `.ic-master`:母版行那一块盒子(`--ic-sunken` 底 + `border-radius:8px` + `padding:11px 12px`\n * + `margin-top:11px` + 内高光)。 */\n master: {\n // #941:沉底盒的底改成按墨色派生——浅色下 `bg-layer-3` 与卡面同白(盒子看不见),派生值给出一档可见的面差。\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n padding: '11px 12px',\n marginTop: 11,\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.05)',\n } as React.CSSProperties,\n /** `.ic-master .ic-lab{font-weight:600;display:flex;align-items:center;gap:7px}`;\n * 字号**不写**(#739 ④:`label` 这一项该继承宿主字号)。 */\n label: { display: 'flex', alignItems: 'center', gap: 7, fontWeight: 600 } as React.CSSProperties,\n /** `.ic-chip{font-size:10.5px;border:1px solid var(--ic-accent);border-radius:999px;padding:0 6px}`(10.5px ⇒ `0.84em`)。 */\n chip: {\n fontSize: '0.84em',\n border: '1px solid var(--dsw-alias-brand-primary, #f9fafb)',\n borderRadius: 999,\n padding: '0 6px',\n color: 'var(--dsw-alias-brand-primary, #f9fafb)',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-master .ic-hint{color:var(--ic-muted);font-size:11.5px;margin-top:3px;line-height:1.5}`。 */\n hint: {\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n fontSize: '0.92em',\n marginTop: 3,\n lineHeight: 1.5,\n } as React.CSSProperties,\n /** `.ic-master input`:整行宽、`margin-top:7px`、`padding:5px 8px`、`--ic-bg` 底、`--ic-line-strong` 描边、\n * `border-radius:6px`。字号与字族**不写**(#739 ④/①:`input` 继承宿主)。 */\n input: {\n display: 'block',\n width: '100%',\n marginTop: 7,\n padding: '5px 8px',\n boxSizing: 'border-box',\n background: 'var(--dsw-alias-bg-base, #151517)',\n // #932/#941:描边换成按墨色派生的可见档——原 `border-l2` 在浅色下只有 1.26:1,等于没有边界\n //(维护者判\"不够明显\"),派生值给到 ≈1.6:1;深色下比原先略亮一档。\n border: '1px solid ' + PANEL_FIELD_EDGE,\n borderRadius: 6,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** `.ic-master input.is-dirty`:`border:2px solid var(--ic-attn)` + `padding:4px 7px`\n * + `box-shadow:0 0 0 3px <琥珀 22%>`;2px 描边与 5→4 的内边距对消,宽高不变\n * (审查「站得住」第 7 条)。 */\n inputDirty: {\n display: 'block',\n width: '100%',\n marginTop: 7,\n padding: '4px 7px',\n boxSizing: 'border-box',\n background: 'var(--dsw-alias-bg-base, #151517)',\n border: '2px solid var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderRadius: 6,\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n boxShadow: '0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 22%, transparent)',\n } as React.CSSProperties,\n /** 布尔档那枚方框(v3.1 的母版行只有文本框,这一格按输入框的同一套外边距对齐)。 */\n checkbox: { marginTop: 7 } as React.CSSProperties,\n /** `.ic-acts{display:flex;gap:5px;margin-top:7px;flex-wrap:wrap;align-items:center}`(母版行的按钮行)。 */\n acts: { display: 'flex', gap: 5, marginTop: 7, flexWrap: 'wrap', alignItems: 'center' } as React.CSSProperties,\n /** `.ic-btn.is-mini{font-size:11px;padding:2px 7px;color:var(--ic-muted)}`——`btnPick` 是锁钉死的那一格,\n * 字号**不写**(#739 ④:该继承宿主字号),形状照 v3.1。 */\n btnPick: {\n padding: '2px 7px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-secondary, #cfd3d6)',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-invalid{margin-top:7px;padding:7px 9px;border-radius:6px;font-size:11.5px;line-height:1.5;\n * background:color-mix(in srgb, var(--ic-attn) 16%, transparent);border:1px solid var(--ic-attn)}`。 */\n invalid: {\n marginTop: 7,\n padding: '7px 9px',\n borderRadius: 6,\n fontSize: '0.92em',\n lineHeight: 1.5,\n background: 'color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 16%, transparent)',\n border: '1px solid var(--dsw-alias-state-warn-primary, #e0a33e)',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n } as React.CSSProperties,\n /** `.ic-group{margin-top:14px}`(高级组默认收起)。 */\n advanced: { marginTop: 14 } as React.CSSProperties,\n /** `.ic-group > .ic-ghead{display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;\n * font-weight:600;padding:2px 0}`——字号**不写**(#739 ④:`summary` 该继承宿主字号)。 */\n summary: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n cursor: 'pointer',\n listStyle: 'none',\n fontWeight: 600,\n padding: '2px 0',\n } as React.CSSProperties,\n /** `.ic-ghead::before{content:'›';color:var(--ic-muted)}`——画成真的那一个字符(内联样式给不出伪元素)。 */\n groupMark: { color: 'var(--dsw-alias-label-secondary, #cfd3d6)' } as React.CSSProperties,\n /** #934:`.ic-gnote` 那条副文案的样式随之删除——副文案本身已按维护者裁定整条撤掉(零消费者的槽位不留)。 */\n /** `.ic-group > .ic-gbody{margin-top:9px;padding:11px 12px;background:var(--ic-sunken);\n * border-radius:8px;box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}`。 */\n groupBody: {\n marginTop: 9,\n padding: '11px 12px',\n // #941:同母版行——沉底面走派生值(浅色下 `bg-layer-3` 与卡面同白)。\n background: PANEL_SUNKEN_FILL,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 8,\n boxShadow: 'inset 0 1px 0 rgba(255,255,255,.04)',\n } as React.CSSProperties,\n /** `.ic-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:14px;padding-top:12px;\n * border-top:1px solid var(--ic-line)}` + **贴下沿吸住**(v3.1 的 `.ic-attnbar.is-sticky` 是它唯一的\n * 吸底口径:`position:sticky;bottom:6px;z-index:3`;本票按票面「贴下沿」取 0,好让底栏与下沿齐平)。 */\n bar: {\n display: 'flex',\n gap: 8,\n alignItems: 'center',\n flexWrap: 'wrap',\n marginTop: 14,\n paddingTop: 12,\n borderTop: '1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))',\n position: 'sticky',\n bottom: 0,\n zIndex: 3,\n background: 'var(--dsw-alias-bg-layer-1, #232324)',\n } as React.CSSProperties,\n /** `.ic-bar .ic-note{margin-left:auto;color:var(--ic-faint);font-size:11.5px}`(就绪态那半句)。 */\n barNote: {\n marginLeft: 'auto',\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n fontSize: '0.92em',\n } as React.CSSProperties,\n /** 有改动时底栏那半句(黄字,粘在按钮组左侧)。 */\n saveMsg: {\n marginLeft: 'auto',\n color: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontSize: '0.92em',\n } as React.CSSProperties,\n /** `.ic-btn{font-size:12px;padding:4px 10px;border-radius:6px;border:1px solid var(--ic-line-strong);\n * background:transparent;color:var(--ic-text)}`——字号**不写**(#739 ④)。 */\n btn: {\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, #f9fafb)',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** `.ic-btn.is-primary{background:var(--ic-accent);border-color:var(--ic-accent);color:var(--ic-accent-ink);\n * font-weight:500}`——`--ic-accent` 取 `--dsw-alias-brand-primary`,前景取 `label-primary-foreground`。 */\n btnPrimary: {\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-brand-primary, #f9fafb)',\n background: 'var(--dsw-alias-brand-primary, #f9fafb)',\n color: 'var(--dsw-alias-label-primary-foreground, #0f1115)',\n fontWeight: 500,\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n /** #934:原先这里有一格 `version`(各家附加块的版本行字面)。版本行已按维护者裁定整条撤掉\n *(那两家面板内的渲染点 + 六家各自那份字面副本),这一格随之删除:没人取的槽位不留。 */\n /** `.ic-badge{font-size:11px;line-height:1;padding:4px 9px;border-radius:999px;white-space:nowrap;\n * display:inline-flex;align-items:center;gap:6px;border:1px solid transparent}`(11px ⇒ `0.88em`)。 */\n badge: {\n fontSize: '0.88em',\n lineHeight: 1,\n padding: '4px 9px',\n borderRadius: 999,\n whiteSpace: 'nowrap',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n border: '1px solid transparent',\n } as React.CSSProperties,\n /** `.ic-badge .ic-bd{width:6px;height:6px;border-radius:50%;background:currentColor}`。 */\n badgeDot: { width: 6, height: 6, borderRadius: '50%', background: 'currentColor' } as React.CSSProperties,\n /** `.ic-spin{width:11px;height:11px;border-radius:50%;border:1.5px solid rgba(0,0,0,.25);\n * border-top-color:currentColor}`——旋转那半要 `@keyframes`(内联样式给不出),故只画环。 */\n badgeSpinner: {\n width: 11,\n height: 11,\n borderRadius: '50%',\n border: '1.5px solid rgba(0,0,0,.25)',\n borderTopColor: 'currentColor',\n display: 'inline-block',\n } as React.CSSProperties,\n /** `.ic-badge.is-dirty`:`--ic-attn` 实底 + `--ic-attn-ink` 深字(那 3:1 是设计专门解过的对比度)。 */\n badgeDirty: {\n color: '#241703',\n background: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderColor: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontWeight: 600,\n } as React.CSSProperties,\n /** `.ic-badge.is-busy`:与 dirty 同色(写的时候还在那一档上)。 */\n badgeBusy: {\n color: '#241703',\n background: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n borderColor: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n fontWeight: 600,\n } as React.CSSProperties,\n /** `.ic-badge.is-ok{color:var(--ic-ok);border-color:var(--ic-ok);background:color-mix(in srgb, var(--ic-ok) 14%, transparent)}`。 */\n badgeOk: {\n color: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n borderColor: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n background: 'color-mix(in srgb, var(--dsw-alias-state-success-primary, #4ec9a0) 14%, transparent)',\n } as React.CSSProperties,\n /** `.ic-badge.is-bad{color:var(--ic-danger-ink);background:var(--ic-danger);border-color:var(--ic-danger)}`。 */\n badgeBad: {\n color: '#fff5f4',\n background: 'var(--dsw-alias-state-error-primary, #e0685f)',\n borderColor: 'var(--dsw-alias-state-error-primary, #e0685f)',\n fontWeight: 600,\n } as React.CSSProperties,\n};\n\n/** 样式表的形状(附加块照面板的视觉写,不各写一套内联样式)。 */\nexport type ConfigStyles = typeof S;\n\n/** 附加块拿到的那两格。 */\nexport interface PanelParts {\n /** 面板自己的样式表。 */\n readonly styles: ConfigStyles;\n /** 配置面整面回执;还没读到(读取中/读取失败)时为 null。 */\n readonly reply: ConfigSurfaceReply | null;\n}\n\n/** 一行点「复制」之后就地显示成哪一档字面。 */\nexport type CopyState = 'idle' | 'done' | 'failed';\n\n/** 复制那一枚按钮的字面(定稿 v3 第三条:就地变,不弹提示)。 */\nexport function copyLabelOf(state: CopyState | undefined): string {\n if (state === 'done') return '已复制';\n if (state === 'failed') return '复制失败';\n return '复制';\n}\n\n/** 面板状态徽标那七档(v3.1 的 `SKILL_STATES` 与 `badgeHTML` 逐档对应)。\n *\n * 七档的落点各不相同:`readFailed` 不画徽标——它把整面换成「一整屏错误 + 恰两枚按钮」那一支;\n * `ready`(就绪)也不画(v3.1 的 `badgeHTML` 在就绪态回空串);其余五档画在卡片标题行右端。 */\nexport type PanelBadge = 'ready' | 'dirty' | 'busy' | 'saved' | 'invalid' | 'writeFailed' | 'readFailed';\n\n/** 算徽标要看的几格事实(全是面板已经拿到的东西,不另取数)。 */\nexport interface BadgeFacts {\n readonly readFailed: boolean;\n readonly busy: boolean;\n readonly writeFailed: boolean;\n /** 技能侧报出「配置里有值用不了」这一事实(#915 第二步落地前恒为 false,见票面「遗留出口」)。 */\n readonly invalid: boolean;\n readonly dirtyCount: number;\n readonly saved: boolean;\n}\n\n/** 状态 → 徽标那一档(v3.1 `paint()` 里的先后次序:读取失败/写入中/写入失败/值非法/有改动/已保存/就绪)。 */\nexport function panelBadgeOf(facts: BadgeFacts): PanelBadge {\n if (facts.readFailed) return 'readFailed';\n if (facts.busy) return 'busy';\n if (facts.writeFailed) return 'writeFailed';\n if (facts.invalid) return 'invalid';\n if (facts.dirtyCount > 0) return 'dirty';\n if (facts.saved) return 'saved';\n return 'ready';\n}\n\n/** 徽标那一档画的字(v3.1 `badgeHTML` 的字面,只把「已保存 · 刚刚」的演示时刻换成档名)。 */\nexport function badgeTextOf(kind: PanelBadge, dirtyCount: number): string {\n if (kind === 'dirty') return `${dirtyCount} 项未保存`;\n if (kind === 'busy') return '正在写入';\n if (kind === 'saved') return '已保存';\n if (kind === 'invalid') return '配置里有值用不了';\n if (kind === 'writeFailed') return '写入失败';\n return '';\n}\n\n/** 徽标那一档的底色(v3.1 的四条 `.ic-badge.is-*`)。 */\nfunction badgeStyleOf(kind: PanelBadge): React.CSSProperties {\n if (kind === 'dirty' || kind === 'invalid') return { ...S.badge, ...S.badgeDirty };\n if (kind === 'busy') return { ...S.badge, ...S.badgeBusy };\n if (kind === 'saved') return { ...S.badge, ...S.badgeOk };\n if (kind === 'writeFailed') return { ...S.badge, ...S.badgeBad };\n return S.badge;\n}\n\n/** 画卡片标题行右端那枚状态徽标(就绪态与读取失败那一支不画)。 */\nexport function PanelBadgeView(props: { readonly kind: PanelBadge; readonly dirtyCount: number }): React.ReactElement | null {\n const text = badgeTextOf(props.kind, props.dirtyCount);\n if (text === '') return null;\n return React.createElement(\n 'span',\n { style: badgeStyleOf(props.kind) },\n props.kind === 'busy'\n ? React.createElement('span', { style: S.badgeSpinner })\n : React.createElement('span', { style: S.badgeDot }),\n text,\n );\n}\n\nexport interface RowProps {\n readonly item: ConfigItem;\n readonly value: string;\n readonly disabled: boolean;\n readonly onChange: (key: string, next: string) => void;\n /** 目录行才有:入口三态(`native`/`browse`/`none`)。 */\n readonly browser?: DirectoryRowEntry | null | undefined;\n /** 脏行:这一行与刚读到的整面不一致时画标记。 */\n readonly dirty?: boolean;\n /** 跟随行:只读派生行在触发键变脏时给「将跟随更新」态。 */\n readonly follow?: boolean;\n /** 点这一行的「复制」:把该行的值文本交出去。\n * 定稿 v3 要求每行一枚,所以按钮**一律画**;这一格缺席时按钮 `disabled` 而不是点了就炸\n * (面板那边永远给这一格,缺席只可能是别的调用点少交了东西)。 */\n readonly onCopy?: ((key: string, text: string) => void) | undefined;\n /** 复制那一枚按钮当刻的字面(不给=「复制」)。 */\n readonly copyState?: CopyState | undefined;\n /** 技能侧报出的「这一行真用不了」(#915 第二步):给了就原样画 `message`,不比较、不合成。\n * 缺席=这一行没问题,什么都不画。 */\n readonly alert?: { readonly code: string; readonly message: string } | null | undefined;\n /** 这一行是所在那一组的最后一行(v3.1 的 `.ic-row:last-child{border-bottom:0}`;\n * 内联样式没有 `:last-child`,行序是客户端常量,故由画整面的人算好交进来)。 */\n readonly last?: boolean | undefined;\n /** **跟随行**:这一行跟着某个可改行变(静态关系,与当刻脏不脏无关)⇒ 照 v3.1 的 `.ic-under`\n * 相对那个可改行缩进 22px、左边挂一条线。只读行才用得上(可改行自己是母版行,不缩进)。 */\n readonly nested?: boolean | undefined;\n}\n\n/** 一行怎么画(照 v3.1 的两套行模板):\n *\n * **只读行**(`.ic-row`)=标签—值同行、值装不下整行让位、行尾一枚「复制」——**不画输入框**\n * (值来自技能侧解析,页面上本来就不给改),也不画那行说明(v3.1 的 `rowShell` 里没有它)。\n * **可改行**(`.ic-master`)=标签 +「可改」小徽标 + 说明 + 整行输入框 + 按钮行。\n *\n * 目录档=文本框 + 一枚唤起目录选择的按钮;入口三态是 `none`(命名空间拿不到、或这条路已被拒)\n * **就不画按钮**,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。\n * 只读行:目录入口**一枚都不画**(定稿 v3 ①)。 */\nexport function Row(props: RowProps): React.ReactElement {\n const { item } = props;\n const readonly = item.readonly === true;\n const disabled = props.disabled || readonly;\n const copy = React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled: props.onCopy === undefined,\n // #1160 T1:按压收缩+焦点双环(纯视觉,不进状态机;禁用态浏览器不触发 :active)。\n // #1161 T2:长按 500ms 纯视觉环(只做视觉标记,不注册任何动作回调、不进状态机分支)。\n 'data-ilife-press': 'copy',\n 'data-ilife-longpress': 'copy',\n onClick: () => props.onCopy?.(item.key, props.value),\n },\n copyLabelOf(props.copyState),\n );\n const follow =\n props.follow === true\n ? React.createElement(\n 'span',\n { style: S.follow },\n React.createElement('span', { style: S.followMark }, '↳'),\n '将跟随更新',\n )\n : null;\n\n if (readonly) {\n /** 跟随行落 `.ic-under` 那一档缩进;其余按是不是本组最后一行挑有没有下边线。 */\n const rowStyle =\n props.nested === true ? (props.last === true ? S.followerRowLast : S.followerRow) : props.last === true ? S.rowLast : S.row;\n return React.createElement(\n 'div',\n { style: rowStyle },\n React.createElement('span', { style: S.rowKey }, item.title),\n React.createElement('span', { style: S.rowValue }, props.value),\n React.createElement('span', { style: S.rowActs }, follow, copy),\n );\n }\n\n const onChange = (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.value);\n const control =\n item.control === 'switch'\n ? React.createElement('input', {\n type: 'checkbox',\n style: S.checkbox,\n checked: props.value === 'true',\n disabled,\n // #1160 T1:键盘焦点双环(文本框不收缩,只给焦点)。\n 'data-ilife-focus': 'input',\n onChange: (e: React.ChangeEvent<HTMLInputElement>) =>\n props.onChange(item.key, e.target.checked ? 'true' : 'false'),\n })\n : React.createElement('input', {\n style: props.dirty === true ? S.inputDirty : S.input,\n type: item.control === 'number' ? 'number' : 'text',\n value: props.value,\n disabled,\n spellCheck: false,\n // #1160 T1:键盘焦点双环(文本框不收缩,只给焦点)。\n 'data-ilife-focus': 'input',\n onChange,\n });\n /** 目录入口:只读行不画(定稿 v3 ①),供不了(`none`/缺席)也不画。 */\n const entry = props.browser ?? null;\n const browse =\n item.control === 'directory' && entry !== null && entry.mode !== 'none'\n ? React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled,\n // #1160 T1:按压收缩+焦点双环(只读行不画本枚,见定稿 v3 ①)。\n // #1161 T2:长按纯视觉环(同上)。\n 'data-ilife-press': 'browse',\n 'data-ilife-longpress': 'browse',\n // 回这枚 Promise 是有意的:React 不看 onClick 的返回值,而用例能直接 await 它。\n onClick: () => entry.onOpen(item.key),\n },\n '浏览文件夹',\n )\n : null;\n return React.createElement(\n 'div',\n { style: S.master },\n React.createElement(\n 'div',\n { style: S.label },\n item.title,\n React.createElement('span', { style: S.chip }, '可改'),\n ),\n React.createElement('div', { style: S.hint }, item.hint),\n control,\n React.createElement('div', { style: S.acts }, browse, copy),\n props.alert == null\n ? null\n : React.createElement('div', { style: S.invalid }, props.alert.message),\n );\n}\n\n/** 面板状态:读取中/就绪/整面读取失败。 */\nexport type PanelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly surface: ConfigSurfaceReply }\n | { readonly kind: 'failed'; readonly message: string };\n\n/** 整面的全部输入:状态 + 行表 + 各档回调(本件只读它、只回调)。 */\nexport interface PanelBodyProps {\n /** 卡片标题(各家自己的产品名)。 */\n readonly title: string | undefined;\n /** 右区三枚签(1145:各家交自家三枚 HELP+技能+插件;缺席=一行都不印,现红签已退役)。 */\n readonly seals?: readonly SealInstance[] | undefined;\n /** 家下标(1158:六家各取 0-5 之一,决定自家三枚倾角与槽位)。 */\n readonly sealSeed?: number | undefined;\n /** 点开的那枚下标(弹卷开合住状态层,本件只读;缺席=全关)。 */\n readonly selectedSeal?: number | null | undefined;\n /** 点章(本件只回调,状态在 `config-panel.ts`)。 */\n readonly onSealSelect?: ((index: number) => void) | undefined;\n /** 关卷。 */\n readonly onSealClose?: (() => void) | undefined;\n readonly items: readonly ConfigItem[];\n readonly state: PanelState;\n readonly draft: Readonly<Record<string, string>>;\n readonly busy: boolean;\n /** 成功提示(已保存/已重置)。 */\n readonly notice: string | null;\n /** 写入失败那一支:落点在底栏紧下面(与「就地失败」分开摆)。 */\n readonly writeError: string | null;\n /** 就地失败那一支(选目录那类)。 */\n readonly error: string | null;\n readonly picking: boolean;\n readonly browseRow: DirectoryRowBrowser | null;\n readonly rowEntry: DirectoryRowEntry | null;\n readonly dirtyKeys: readonly string[];\n readonly followKeys: readonly string[];\n /** **静态**跟随行名单(由各家交的 `followKeysOf` 钩子逐键探出来的,与当刻脏不脏无关):\n * 名单里的只读行按 v3.1 的 `.ic-under` 缩进;空缺=一行都不缩进。 */\n readonly followerKeys?: readonly string[] | undefined;\n readonly copy: { readonly key: string; readonly ok: boolean } | null;\n /** 技能侧报出的「配置里有值用不了」那一格事实(#915 第二步落地前缺席=不亮那一档)。 */\n readonly invalid?: boolean | undefined;\n readonly onCopy: (key: string, text: string) => void;\n readonly onChange: (key: string, next: string) => void;\n readonly onSave: () => void;\n readonly onReset: () => void;\n readonly onRetry: () => void;\n /** 自家附加块(版本行/状态行):给了就画在面板主体之后、动作条之前。 */\n readonly extra?: ((parts: PanelParts) => React.ReactNode) | undefined;\n}\n\n/** 应用内浏览器对话框的文案(面板自带,逐条对应图上那几个位置)。 */\nconst BROWSER_LABELS = {\n title: '选择文件夹',\n close: '关闭',\n up: '上一级',\n pathPlaceholder: '直接填绝对路径,回车即进入',\n go: '转到',\n showHidden: (n: number) => '显示隐藏目录(' + n + ')',\n empty: '这个目录里没有子目录。',\n loading: '正在读取',\n newFolder: '新建文件夹',\n createConfirm: '创建',\n createCancel: '取消',\n select: '选',\n selected: '已选',\n open: '选定这个目录',\n cancel: '取消',\n willPick: '将选定:',\n} as const;\n\n/** 画整面(四支控件、三支错误落位、底栏、附加块槽都在这里)。 */\nexport function PanelBody(props: PanelBodyProps): React.ReactElement {\n const { state, title } = props;\n const surface = state.kind === 'ready' ? state.surface : null;\n const product = title ?? '';\n const seals = props.seals ?? [];\n const seed = props.sealSeed ?? 0;\n /** 三行定序(六家一致:HELP 上/技能中/插件下;HELP 永远 z 最上)。 */\n const order = [0, 1, 2];\n /** 三行行高(上/中/下;纵向净空各留约 10px,糙边抖动也碰不到)。 */\n const TOPS = ['0.3em', '3.4em', '6.2em'];\n /** 轻微倾角(±0.5°~±2.5°,方向随机):家下标×槽位哈希,纯函数、每次同值,不过大。 */\n const tiltOfSlot = (slot: number): string => {\n const q = (seed * 5 + slot * 11 + 3) % 10;\n return String((q % 2 === 0 ? -1 : 1) * (0.5 + Math.floor(q / 2) * 0.5));\n };\n /** 右间距(三枚全部居右收敛,各家各行 0.2em~1.1em 错开,永不出面板右边界)。 */\n const padRightOfSlot = (slot: number): string => String(0.2 + ((seed * 7 + slot * 3 + 1) % 6) * 0.18) + 'em';\n const zOfRole = (role: string): number => role === 'help' ? 4 : role === 'skill' ? 3 : 2;\n const overlay = seals.length === 0 ? null : React.createElement(\n 'div',\n { style: S.sealOverlay },\n React.createElement(SealFilterDefs, {}),\n ...order.map((dataIndex, slot) => React.createElement(\n 'span',\n { key: seals[dataIndex].role + ':' + seals[dataIndex].tier, style: { position: 'absolute', top: TOPS[slot], right: padRightOfSlot(slot), zIndex: zOfRole(seals[dataIndex].role), pointerEvents: 'auto', display: 'inline-flex', transform: 'rotate(' + tiltOfSlot(slot) + 'deg)' } },\n // 章面缩到 80%:印本体(九档锁死值)不动,只在用法处压一层字号——印内 em 全跟下来,3px 边线原样保留。\n React.createElement('span', { style: { fontSize: '0.8em', display: 'inline-flex' } },\n React.createElement(SealStamp, { role: seals[dataIndex].role, tier: seals[dataIndex].tier, label: seals[dataIndex].label, onSelect: props.onSealSelect === undefined ? undefined : props.onSealSelect.bind(null, dataIndex) })),\n )),\n );\n const openSeal = props.selectedSeal === null || props.selectedSeal === undefined ? null : seals[props.selectedSeal] ?? null;\n /** 卷轴浮层跟着**被点的那枚章**:贴在该行下方一小段(1.9em = 章高约 1.5em + 0.4em 缝),\n * 右缘与那枚章的右缘对齐,再往左让 1.25em 给绦带(关闭钮骑在浮层右上方,不让它越出卡片)。 */\n const openSlot = props.selectedSeal ?? -1;\n const anchor = openSlot < 0 || openSlot >= order.length ? undefined : {\n top: 'calc(' + TOPS[openSlot] + ' + 1.9em)',\n right: 'calc(' + padRightOfSlot(openSlot) + ' + 1.25em)',\n left: 'auto',\n justifyContent: 'flex-end',\n maxWidth: 'calc(100% - ' + padRightOfSlot(openSlot) + ' - 1.75em)',\n } as React.CSSProperties;\n const dialog = openSeal === null ? null : React.createElement(SealScrollDialog, {\n open: true, onClose: props.onSealClose ?? (() => undefined),\n role: openSeal.role, tier: openSeal.tier, title: openSeal.label,\n progress: openSeal.progress, status: openSeal.status, plan: openSeal.plan,\n anchor,\n });\n const headDataDir = surface === null ? '' : surface.resolved?.['dbDir'] ?? surface.dataDir;\n const ready = state.kind === 'ready';\n const dirtyCount = ready ? props.dirtyKeys.length : 0;\n const badge = panelBadgeOf({\n readFailed: state.kind === 'failed',\n busy: props.busy,\n writeFailed: props.writeError !== null,\n invalid: props.invalid === true,\n dirtyCount,\n saved: props.notice !== null,\n });\n const head = React.createElement(\n 'div',\n { style: S.head },\n React.createElement(TitleBlock, { product, purpose: '配置' }),\n React.createElement(PanelBadgeView, { kind: badge, dirtyCount }),\n dialog,\n );\n /** #939:这一块的高度由**客户端常量**决定、不由回执时序决定——加载期就把两行连标签一起画出来,\n * 值位画骨架条(`.ic-sk`),回执到了**原地填值** ⇒ 回执零位移(原先加载期只画一行小灰字、\n * 回执到了才长出两行,整块下移 21px)。\n *\n * 判断式按 `state.kind`:**不许**照 `surface === null` 写——它同时覆盖失败态,照写会让失败屏长出两行假骨架。 */\n const loading = state.kind === 'loading';\n /** #981:骨架宽度分档——底座仍是 `S.skeleton`(高度/圆角/底色唯一定义地),这里只覆盖宽度。\n * 配置文件行 68%(宽屏上限 420px)、数据目录行 46%(上限 300px),两行不等即错位感。 */\n const fileValue = (text: string | null, skeletonStyle: React.CSSProperties = S.skeleton): React.ReactNode =>\n text === null ? React.createElement('span', { style: skeletonStyle, 'data-ilife-skeleton': 'value' }) : text;\n const fileRow = (\n label: string,\n text: string | null,\n skeletonStyle: React.CSSProperties,\n tail?: React.ReactNode,\n ): React.ReactElement =>\n React.createElement(\n 'div',\n { style: S.info },\n React.createElement('b', { style: S.fileLabel }, label),\n fileValue(text, skeletonStyle),\n tail === undefined ? null : tail,\n );\n const fileLines =\n !loading && surface === null\n ? null\n : React.createElement(\n 'div',\n { style: S.fileWrap },\n fileRow(\n '配置文件',\n surface === null ? null : surface.path,\n { ...S.skeleton, width: '68%', maxWidth: 420 },\n // #939:这一句原先独占第三行、首次运行时会多 17px ⇒ 挂到同一行的行尾,两行高度恒定。\n surface !== null && surface.created\n ? React.createElement('span', { style: S.muted }, '(配置文件刚按默认值生成)')\n : null,\n ),\n fileRow('数据目录', surface === null ? null : headDataDir, { ...S.skeleton, width: '46%', maxWidth: 300 }),\n );\n\n const parts: PanelParts = { styles: S, reply: surface };\n const extra = props.extra === undefined ? null : props.extra(parts);\n\n if (state.kind === 'failed') {\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n fileLines,\n React.createElement('div', { style: S.error }, state.message),\n extra,\n overlay,\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement('button', { style: S.btn, type: 'button', 'data-ilife-press': 'retry', 'data-ilife-longpress': 'retry', onClick: props.onRetry }, '重试'),\n React.createElement('button', { style: S.btn, type: 'button', 'data-ilife-press': 'reset', 'data-ilife-longpress': 'reset', onClick: props.onReset }, '重置为默认'),\n ),\n // #1160 T1:按压+焦点双环的样式只此一枚 <style>,罩本卡内 data-ilife-press/focus 两类。\n // 放末位:首位会把既有形状判据 kidsOf(card)[0]===head 顶掉(920 红),末位不影响任何既有断言。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n }\n\n const common = props.items.filter((item) => item.tier === 'common');\n const advanced = props.items.filter((item) => item.tier === 'advanced');\n /** 还没读到整面(`loading`)时**框架照画**:行表与控件形状是客户端常量,不必等任何回执。\n * 值先空着、控件与三枚底栏键一律不可用;回执到了由 `ready` 那一支把值填上(见 `config-panel.ts`)。 */\n const frozen = props.busy || !ready;\n const followers = props.followerKeys ?? [];\n const renderRow = (item: ConfigItem, last: boolean) =>\n React.createElement(Row, {\n key: item.key,\n item,\n value: props.draft[item.key] ?? '',\n disabled: frozen,\n onChange: props.onChange,\n browser: props.rowEntry,\n dirty: ready && props.dirtyKeys.includes(item.key),\n follow: ready && props.followKeys.includes(item.key),\n onCopy: ready ? props.onCopy : undefined,\n copyState: props.copy !== null && props.copy.key === item.key ? (props.copy.ok ? 'done' : 'failed') : 'idle',\n alert: ready ? surface?.alerts?.[item.key] ?? null : null,\n last,\n nested: followers.includes(item.key),\n });\n /** 每组最后那一行才免掉下边线(`.ic-row:last-child`);母版行自己不带线,不必管。 */\n const lastIndexOf = (list: readonly ConfigItem[]) => {\n for (let i = list.length - 1; i >= 0; i -= 1) if (list[i].readonly === true) return i;\n return -1;\n };\n const commonLast = lastIndexOf(common);\n const advancedLast = lastIndexOf(advanced);\n\n const dirty = ready && dirtyCount > 0;\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n fileLines,\n React.createElement(\n 'div',\n { style: S.rows },\n common.map((item, index) => renderRow(item, index === commonLast)),\n ),\n advanced.length === 0\n ? null\n : React.createElement(\n 'details',\n { style: S.advanced, 'data-ilife-advanced': 'group' },\n React.createElement(\n 'summary',\n { style: S.summary, 'data-ilife-advanced': 'head' },\n React.createElement('span', { style: S.groupMark, 'data-ilife-advanced': 'mark' }, '›'),\n ADVANCED_GROUP_TITLE,\n ),\n React.createElement(\n 'div',\n { style: S.groupBody, 'data-ilife-advanced': 'body' },\n advanced.map((item, index) => renderRow(item, index === advancedLast)),\n ),\n ),\n extra,\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement(\n 'button',\n { style: dirty ? S.btnPrimary : S.btn, type: 'button', disabled: frozen || !dirty, 'data-ilife-press': 'save', 'data-ilife-longpress': 'save', onClick: props.onSave },\n props.busy ? '处理中' : dirty ? `保存(${dirtyCount} 项未保存)` : '保存',\n ),\n React.createElement('button', { style: S.btn, type: 'button', disabled: frozen, 'data-ilife-press': 'reset', 'data-ilife-longpress': 'reset', onClick: props.onReset }, '重置为默认'),\n React.createElement('button', { style: S.btn, type: 'button', disabled: frozen, 'data-ilife-press': 'retry', 'data-ilife-longpress': 'retry', onClick: props.onRetry }, '重新读取'),\n // 那半句话是**最后一个孩子**:v3.1 的 `.ic-bar .ic-note{margin-left:auto}` 靠它把说明顶到行尾,\n // 三枚按钮留在左边(顺序反了就会成「说明靠左、按钮靠右」,与真源相反)。\n dirty\n ? React.createElement('div', { style: S.saveMsg }, `浏览改动后请点保存(${dirtyCount} 项未保存),保存后跟随项自动更新`)\n : ready\n ? React.createElement('div', { style: S.barNote }, '没有未保存的改动')\n // #939:原先加载期在头部独占一行「配置读取中」;搬到底栏这一格——不删(首屏没有第二个\"正在读\"的表示),\n // 但也不占头部那两行的高度(`config-panel-908` 仍按文字断言它在这张卡里)。\n : React.createElement('div', { style: S.barNote }, '配置读取中'),\n ),\n props.picking ? React.createElement('div', { style: S.muted }, '已唤起系统文件夹对话框:选中后自动填上,取消则不动。') : null,\n props.browseRow !== null\n ? React.createElement(DirectoryBrowserFromRow, { open: true, row: props.browseRow, labels: BROWSER_LABELS })\n : null,\n props.writeError !== null\n ? React.createElement('div', { style: S.error, role: 'alert' }, props.writeError)\n : null,\n props.error !== null ? React.createElement('div', { style: S.error }, props.error) : null,\n props.notice !== null ? React.createElement('div', { style: S.okText, 'data-ilife-notice': 'line' }, props.notice) : null,\n overlay,\n // #1160 T1:按压+焦点双环的样式只此一枚 <style>,放末位(首位会顶掉 kidsOf(card)[0]===head 的既有判据)。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n}\n\n/** #936:**状态行**(照真源 v3.1 的 `.ic-status`)——两家(备忘/作息)共用这一件,各自只交数据。\n *\n * 形状(逐值照真源):\n * ① 盒子:`padding:10px 12px` + 沉底面 + `1px` 描边 + 圆角 `8px`;\n * ② 第一行:状态点(8px 圆,底色按档位)+ 名字(600)+ 一行短句;动作按钮靠 `margin-left:auto` 顶到行尾;\n * ③ 第二行:路径(等宽、单行不折、超出裁掉)+ 版本胶囊(`version === null` 就不画——真源骨架期就是 `hidden`);\n * ④ 官网行(等宽、可点新窗口)。\n *\n * 骨架期(`path === null`):值位画骨架条(同一个 `.ic-sk` 口径),两枚按钮照画 ⇒ 与就绪态**同高**、\n * 填充时不跳(真源 `planRows()` 的口径)。 */\nexport type StatusTone = 'ok' | 'warn' | 'bad' | 'pending';\n\n/** 状态点的底色:三档状态色 + 默认灰(照真源 `.is-ok/.is-warn/.is-bad` 与 `.ic-dot` 的默认档)。 */\nconst STATUS_DOT_COLOR: Record<StatusTone, string> = {\n ok: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n warn: 'var(--dsw-alias-state-warn-primary, #e0a33e)',\n bad: 'var(--dsw-alias-state-error-primary, #e0685f)',\n pending: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n};\n\nexport interface StatusBlockProps {\n /** 名字(真源 `.ic-sname`),屏上逐字显示。 */\n readonly name: string;\n /** 档位:点色与默认灰。 */\n readonly tone: StatusTone;\n /** 一行短句(真源 `.ic-stxt`)。 */\n readonly text: string;\n /** 第二行的路径;`null` ⇒ 画骨架条(加载期)。 */\n readonly path: string | null;\n /** 版本胶囊里的版本号;`null`/不传 ⇒ 不画胶囊。 */\n readonly version?: string | null | undefined;\n /** 动作按钮(0-2 枚;第一枚顶到行尾)。 */\n readonly actions?: readonly { readonly text: string; readonly onPress: () => void }[] | undefined;\n /** 官网行:显示文字 + 跳转目标(面板不自己拼地址);不给 ⇒ 不画这一行。 */\n readonly link?: { readonly text: string; readonly href: string } | undefined;\n}\n\nexport function StatusBlock(props: StatusBlockProps): React.ReactElement {\n const actions = props.actions ?? [];\n return React.createElement(\n 'div',\n { style: S.statusBox, 'data-ilife-status': 'block' },\n React.createElement(\n 'div',\n { style: S.statusRow },\n React.createElement('span', { style: { ...S.statusDot, background: STATUS_DOT_COLOR[props.tone] } }),\n React.createElement('span', { style: S.statusName }, props.name),\n React.createElement('span', { style: S.statusText }, props.text),\n ...actions.map((action, index) =>\n React.createElement(\n 'button',\n {\n key: action.text,\n style: index === 0 ? { ...S.btnPick, marginLeft: 'auto' } : S.btnPick,\n type: 'button',\n // #1160 T1:按压收缩+焦点双环(状态行动作钮同口径)。\n 'data-ilife-press': 'status-action',\n 'data-ilife-longpress': 'status-action',\n onClick: action.onPress,\n },\n action.text,\n ),\n ),\n ),\n React.createElement(\n 'div',\n { style: S.statusFind },\n props.path === null\n // #981:状态行路径位恢复真源定宽 180px(v3.1 `:422`),不跟面板走。\n ? React.createElement('span', { style: { ...S.skeleton, width: 180 }, 'data-ilife-skeleton': 'statusPath' })\n : React.createElement('span', { style: S.statusPath }, props.path),\n props.version === undefined || props.version === null || props.version === ''\n ? null\n : React.createElement('span', { style: S.statusVersion }, props.version),\n ),\n props.link === undefined\n ? null\n : React.createElement(\n 'a',\n {\n style: S.statusUrl,\n href: props.link.href,\n target: '_blank',\n rel: 'noreferrer',\n // #1160 T1:键盘焦点双环(链接只给焦点,不收缩)。\n 'data-ilife-focus': 'status-link',\n },\n props.link.text,\n ),\n );\n}\n","/** 配置体检:各家技能回给面板的那份报告的形状(唯一真相在技能侧,本件只是面板侧镜像)。\n *\n * 为什么形状这样定(票 #706「开工前的形状裁定」第一条):**判据住技能侧**。\n * 每条检查项的有效值只有技能源码算得出来(例:`resolveDbDir()` 里「空串=按默认落点」),\n * 插件包对技能的全部能力是 spawn `cmd_read` 与读 stdout 信封,看不到那些算式。所以:\n *\n * 技能侧出**判据**(每条给红/黄/绿 + 一句话 + 去哪修);\n * 插件包出**透传**(把技能回执原样交回,不重写一个字);\n * 总管出**总览与渲染**(零特例:不认任何一家,只认下面这个形状)。\n *\n * 于是「同一份数据、不许两处各算一遍」是结构上的性质:面板一次取数拿到六份报告,\n * 总览一行与各家那张表都从同一个快照读(见 `health-panel.ts` 的单一状态源)。\n *\n * 三档判据的语义(照检查表 `docs/research/check-table-671-life-panel-20260917.html`):\n * · `red`=现在用不了,必须去修;\n * · `yellow`=现在这状态能用,但有人真去用那个功能时会撞上(例:照片目录没配);\n * · `green`=正常。\n */\n\n/** 三档判据。 */\nexport type HealthStatus = 'red' | 'yellow' | 'green';\n\n/** 一条检查项的回执。 */\nexport interface HealthItem {\n /** 稳定标识:配置项就是它在配置文件里的键路径(`db.dir`),非配置项用点式名(`xunji.cli`)。 */\n readonly id: string;\n /** 页面上那一行的中文标题(技能侧定,面板不重写)。 */\n readonly title: string;\n readonly status: HealthStatus;\n /** 一句话:现在是什么状态,带上路径或门槛这类实证。 */\n readonly message: string;\n /** 去哪修。`green` 时可为空。 */\n readonly action: string;\n /** 这个值从哪来(只陈述,不判好坏):`配置文件`/`默认值`/`环境`。非取值类检查项可省。 */\n readonly source?: string;\n}\n\n/** 一家的体检报告。 */\nexport interface HealthReport {\n /** 技能名(`calorie`/`bill`/…),等于该家配置文件的主体名。 */\n readonly skill: string;\n /** 实际生效的那份配置文件路径。 */\n readonly configPath: string;\n /** 实际生效的数据目录。 */\n readonly dataDir: string;\n readonly items: readonly HealthItem[];\n /** #936:飞书 CLI 那一格(**可选**——只有作息那种\"状态行数据从体检出\"的家才带;旧技能包不带)。\n * 形状与技能侧体检回执的 `lark` 格同形:档位 + 路径 + 短版本号。 */\n readonly lark?: { readonly tier: string; readonly cliPath: string | null; readonly version: string | null } | undefined;\n}\n\n/** 体检出口:技能侧在**进分派层之前**拦下的那一类只读命令名(照三个配置命令的先例)。\n *\n * 载荷:入参 `{}`;回包 `{report}`(技能侧 envelope 的 `data`)。**只报不改**:\n * 不建目录、不写文件、不落默认配置——凡是要建目录才算得出的结论,一律当「不在」报。 */\nexport const HEALTH_CHECK_KEY = 'config.check' as const;\n\n/** 面板第二段参数(与各家的端点名同值,和各家的 `RPC_CHANNEL` 配成一次调用)。 */\nexport const HEALTH_ENDPOINT = 'config.check' as const;\n\n/** 三档排序:数字大的更严重(总览一行取**最严重**那档当那一家的灯)。 */\nconst RANK: Readonly<Record<HealthStatus, number>> = { green: 0, yellow: 1, red: 2 };\n\n/** 一家的总判据:全部项里最严重的那一档;一条都没有时回 `null`(不冒充绿)。 */\nexport function worstStatus(items: readonly HealthItem[]): HealthStatus | null {\n let worst: HealthStatus | null = null;\n for (const item of items) {\n if (worst === null || RANK[item.status] > RANK[worst]) worst = item.status;\n }\n return worst;\n}\n\n/** 三档计数(面板那一行「红 N 黄 N」用的就是它)。 */\nexport function countByStatus(items: readonly HealthItem[]): Readonly<Record<HealthStatus, number>> {\n const out = { red: 0, yellow: 0, green: 0 };\n for (const item of items) out[item.status] += 1;\n return out;\n}\n\n/** 回执校验:面板只渲染自己认得的形状,认不出即报「回执异常」,不猜、不返空冒充正常。 */\nexport function isHealthReport(raw: unknown): raw is HealthReport {\n if (typeof raw !== 'object' || raw === null) return false;\n const report = raw as Partial<HealthReport>;\n if (typeof report.skill !== 'string' || report.skill.length === 0) return false;\n if (typeof report.configPath !== 'string' || typeof report.dataDir !== 'string') return false;\n if (!Array.isArray(report.items)) return false;\n return report.items.every((item: unknown) => {\n if (typeof item !== 'object' || item === null) return false;\n const row = item as Partial<HealthItem>;\n return typeof row.id === 'string' && typeof row.title === 'string'\n && (row.status === 'red' || row.status === 'yellow' || row.status === 'green')\n && typeof row.message === 'string' && typeof row.action === 'string';\n });\n}\n","/** 面板到各家的配置体检:一次取数、六份报告(纯异步函数:无 DOM、无 react;传输口注入,便于单测)。\n *\n * 三条纪律:\n * · **只走各家自己的电话**(`rpc.call(CARRIER_BASE, <电话名>, {method, payload}, …)`)——总管不认任何\n * 一家的端点、也不加任何单品的依赖;电话名与端点名都是各家契约件公开的事实。\n * ⚠ **载体形状**(#735 真机踩到):第一段必须是**载体基段** `/api`,第二段是那家的电话名\n * (=它注册在 `/api<通道>` 上的那一段),端点名写进载荷的 `method`。写错第一段就是 404,\n * 而 404 只落成「那一家取不到数」,屏上看起来像按钮坏了(先例:总管自己的电话在 #678 栽过同一处,\n * 见 `update-contract.ts` 的 `CARRIER_BASE`)。\n * · **一次取数,六份报告**:总览那行与各家那张表读的是同一份结果(见 `health-panel.ts` 的单一状态源),\n * 不许两处各算一遍。\n * · 有界超时:一家不回不该拖死整屏(技能侧 spawn 上限 20 s,这里给 25 s)。\n */\nimport { HEALTH_ENDPOINT, isHealthReport } from './health-contract.js';\nimport type { HealthReport } from './health-contract.js';\nimport { CARRIER_BASE } from './update-contract.js';\nimport type { RpcCallFace } from './dsh-ctx.js';\n\n/** 传输口:与面板共用同一张脸(`connection.rpc.call` 的四参形状,见 dsh-ctx 的 RpcCallFace)。 */\nexport type HealthCallFace = RpcCallFace;\n\n/** 一家的电话名:通道名去掉前导斜杠那一段(各家宿主半注册在 `/api` + 通道名,信封 `method` 就是它)。 */\nexport function phoneOf(channel: string): string {\n return channel.startsWith('/') ? channel.slice(1) : channel;\n}\n\n/** 整批取数的时限(毫秒),**不是**一家一份:六家的活是在**同一个宿主线程**上排队干的。\n *\n * 为什么不是「技能侧 spawn 上限 20 s + 一点余量」(票 #740 对抗式审查第二轮逮到):\n * 各家的宿主半用的是 `spawnSync`(各单品的 `src/bridge.ts` 里 `SPAWN_TIMEOUT_MS = 20_000`),\n * 它**阻塞**宿主的事件循环——六通电话同时打进来,真正干活的是一个接一个串起来的。\n * 于是「六家之和」才是这一批的真实上界:按一家 20 s 算最坏 120 s,常见情形(一家 1~3 s)约 10 s。\n * 60 s 取两者之间:给常见情形留足余量,又不至于让一次卡死把按钮按成两分钟不动。\n * 单家的错照样逐家报(那家页签里的体检表会写「体检超时(60s)…」)。 */\nexport const HEALTH_TIMEOUT_MS = 60_000 as const;\n\n/** 一家的一行:报告取到了就是 `report`,取不到就是 `error`(缺席的包不进这张表)。 */\nexport interface HealthFetchRow {\n /** 该家插件包名(=爱生活页签槽 id,也是缺席判定的那个 join 键)。 */\n readonly id: string;\n readonly report: HealthReport | null;\n readonly error: string | null;\n}\n\n/** 一次取数的结果:每家一行(顺序照页签顺序)。 */\nexport interface HealthFetchResult {\n readonly rows: readonly HealthFetchRow[];\n /** 取数过程本身的错(通道缺席之类),不是某一家的问题。 */\n readonly error: string | null;\n}\n\n/** 账本条目里本票要用的那一段(页签槽注册时写进 options)。 */\nexport interface HealthTabRow {\n readonly id: string;\n readonly channel: string;\n}\n\nfunction timeoutText(): string {\n return '体检超时(' + String(Math.round(HEALTH_TIMEOUT_MS / 1000)) + 's):技能没在时限内回话。';\n}\n\nasync function fetchOne(call: HealthCallFace, row: HealthTabRow): Promise<{ readonly report: HealthReport | null; readonly error: string | null }> {\n try {\n const result = await call(\n CARRIER_BASE,\n phoneOf(row.channel),\n { method: HEALTH_ENDPOINT, payload: {} },\n AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n );\n if (!result || typeof result !== 'object' || typeof (result as { ok?: unknown }).ok !== 'boolean') {\n return { report: null, error: '回执信封异常(非 ok 信封)。' };\n }\n if (!result.ok) {\n const error = (result as { error?: { code?: unknown; message?: unknown } }).error;\n const message = typeof error?.message === 'string' && error.message.length > 0 ? error.message : '体检没跑起来。';\n return { report: null, error: message };\n }\n const value = (result as { value?: unknown }).value;\n if (!isHealthReport(value)) return { report: null, error: '体检报告形状认不出(技能包与总管版本对不上?)。' };\n return { report: value, error: null };\n } catch (error) {\n if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {\n return { report: null, error: timeoutText() };\n }\n return { report: null, error: '体检没跑起来:' + (error instanceof Error ? error.message : String(error)) };\n }\n}\n\n/** 取六家的体检报告(缺席的包不在此列,由调用方按账本判断)。\n *\n * `onRow`(票 #741):某一家一落定就回调一次——面板据此**按家增量**画,不再等齐全部。\n * 六家的电话是并发打出去的,但宿主里干活的只有一个线程(各单品宿主半 `spawnSync`),\n * 所以真实到达顺序就是它们被串行干完的顺序;先回来的那家先画出来,用户才看得见进度。 */\nexport async function loadHealthReports(\n call: HealthCallFace | null,\n tabs: readonly HealthTabRow[],\n onRow?: (row: HealthFetchRow) => void,\n): Promise<HealthFetchResult> {\n if (typeof call !== 'function') return { rows: [], error: '宿主连接缺席:connection.rpc.call 不可用' };\n const rows = await Promise.all(tabs.map(async (tab): Promise<HealthFetchRow> => {\n const one = await fetchOne(call, tab);\n const row: HealthFetchRow = { id: tab.id, report: one.report, error: one.error };\n // 回调里的异常不许把整批拖下水:这是画图的旁路,不是取数本身。\n try {\n onRow?.(row);\n } catch {\n /* 旁路失败不影响整批结果 */\n }\n return row;\n }));\n return { rows, error: null };\n}\n","/** 配置体检的状态源:**一份快照**喂两处(面板顶部那行总览 + 各家页签那张表)。\n *\n * 为什么是一个 hook 而不是两处各取一次(票 #706 验收第三条「同一份数据,不许两处各算一遍」):\n * 这一处取数、这一处存状态,总览与各家表都从它读 —— 那条验收因此是**结构上的性质**,不是靠自觉。\n *\n * 取数是**按需**的(点「体检一次」才跑):没有轮询、没有定时器(静态门那条「无数据轮询」照旧)。\n */\nimport * as React from 'react';\nimport { loadHealthReports } from './health-fetch.js';\nimport type { HealthCallFace, HealthFetchRow, HealthTabRow } from './health-fetch.js';\nimport { worstStatus } from './health-contract.js';\nimport type { HealthReport, HealthStatus } from './health-contract.js';\n\n/** 一家的状态:没跑过/正在跑/有报告/这一家出错。 */\nexport type HealthPhase = 'idle' | 'running' | 'ready' | 'failed';\n\nexport interface HealthPanelFace {\n /** 一次取数的结果:包名 → 该家的状态(缺席的包不在表里)。 */\n readonly rows: Readonly<Record<string, { readonly phase: HealthPhase; readonly report: HealthReport | null; readonly error: string | null }>>;\n /** 取数过程本身的错(通道缺席之类)。 */\n readonly error: string | null;\n /** 正在跑。 */\n readonly running: boolean;\n /** 跑一次(六家一起)。 */\n run(): void;\n}\n\nconst IDLE = { phase: 'idle' as HealthPhase, report: null, error: null };\n\n/** 一家的灯:没报告=`null`(不冒充绿)。 */\nexport function lightOf(row: { readonly report: HealthReport | null } | undefined): HealthStatus | null {\n return row?.report ? worstStatus(row.report.items) : null;\n}\n\n/** 体检状态源:挂载时不自动跑(体检要读盘、要起子进程,按需跑),点一次跑六家。\n *\n * **按家增量**(票 #741):某一家一落定就画那一家——不再等全部齐了才一次性 setRows。\n * 取数口仍然只有一处(`loadHealthReports`),它把「一家好了」的回调交给这里 patch 状态。 */\nexport function useHealthPanel(getCall: () => HealthCallFace | null, tabs: readonly HealthTabRow[]): HealthPanelFace {\n const [rows, setRows] = React.useState<HealthPanelFace['rows']>({});\n const [error, setError] = React.useState<string | null>(null);\n const [running, setRunning] = React.useState(false);\n const run = React.useCallback(() => {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('run:tabs=' + String(tabs.length));\n setRunning(true);\n setError(null);\n const targets = tabs;\n setRows(Object.fromEntries(targets.map((tab) => [tab.id, { ...IDLE, phase: 'running' as HealthPhase }])));\n void (async () => {\n const patch = (row: HealthFetchRow): void => {\n // 函数式更新:这家落定时别家可能也刚落定,直接读旧 state 会丢掉那一次。\n const next = { phase: (row.report ? 'ready' : 'failed') as HealthPhase, report: row.report, error: row.error };\n setRows((previous) => ({ ...previous, [row.id]: next }));\n // 只记条数与第一条的错(渲染台的调试图;浏览器里没人写它,写入失败无所谓)。\n try {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('row=' + row.id + ' ok=' + String(row.report !== null) + ' err=' + String(row.error));\n } catch {\n /* 调试图写不进去不影响正经事 */\n }\n };\n const loaded = await loadHealthReports(getCall(), targets, patch);\n try {\n const diag = (globalThis as { __T706_RUN__?: string[] }).__T706_RUN__;\n if (Array.isArray(diag)) diag.push('rows=' + String(loaded.rows.length) + ' err=' + String(loaded.error) + ' first=' + String(loaded.rows[0]?.error ?? 'null'));\n } catch {\n /* 调试图写不进去不影响正经事 */\n }\n setError(loaded.error);\n setRunning(false);\n })();\n }, [getCall, tabs]);\n const face: HealthPanelFace = { rows, error, running, run };\n // 出图页的取数口(渲染台的 hooks 替身没有真事件环,页内点按钮那条路不落定取数)。\n // 只在有人预先挂了 `__T706_FACE_SINK__` 时写一次,浏览器里没人挂它,等于没有这一行。\n const sink = (globalThis as { __T706_FACE_SINK__?: (value: HealthPanelFace) => void }).__T706_FACE_SINK__;\n if (typeof sink === 'function') {\n try {\n sink(face);\n } catch {\n /* 出图口的错不影响正经渲染 */\n }\n }\n return face;\n}\n\n/** 汇总行该显示哪一句错(票 #735):取数过程本身的错优先;**一家报告都没回来**时,把第一条家错误顶上来。\n *\n * 为什么要有这一格:各家的错只画在那家页签里的体检表上,一行摘要看不见 ⇒ 用户点完只见「体检中…」闪一下、\n * 接着仍旧「还没体检」,会以为按钮坏了(真机上就是这么被误判的;那次六家全 404,汇总行一声不吭)。\n * 有报告回来时不顶:那时各家那张表里已经把错说清楚了,摘要行只管读数。 */\nexport function summaryErrorOf(face: Pick<HealthPanelFace, 'rows' | 'error'>): string | null {\n if (face.error !== null) return face.error;\n const rows = Object.values(face.rows);\n if (rows.some((row) => row.report !== null)) return null;\n return rows.find((row) => row.error !== null)?.error ?? null;\n}\n","/** 配置体检的两个画法:面板顶部那行总览(六家一盏灯)+ 各家那张表。\n *\n * 面板侧纪律(沿 `client.ts`/`update-panel.ts` 既有口径):`createElement` 手写(禁 JSX)、内联 style、\n * 颜色走 DSH 主题别名(深浅主题自适应,写死值只做回退)、不 import 任何单品包、不 import 技能实现。\n *\n * 判据与文案**全部来自各家技能的报告**,本件一个字都不重写——只按三档上色、按状态挑文案。\n */\nimport * as React from 'react';\nimport { countByStatus, worstStatus } from './health-contract.js';\nimport type { HealthItem, HealthReport, HealthStatus } from './health-contract.js';\nimport { TitleBlock } from './title-seal.js';\nimport { interactionCss } from './config-panel-view.js';\n\n/** 三档的配色(绿走主题别名;红黄用实色,理由见下)。\n *\n * 红与黄为什么必须落在**不同明度带**上:这两档要一起出现在同一行灯里,用户扫一眼要能分开\n * 「红了(用不了)」与「黄了(能用但要撞上)」。评审连着两轮点「黄灯偏暗与红相近」,所以:\n * · 红=深红(暗、重);\n * · 黄=高饱和的琥珀(亮、跳)——它在白底上的对比度仍然够(`#b45309` 对白 ≈ 4.9:1)。\n * 绿不给底色:它是「正常」,不该和要处理的那两档抢注意力。 */\nexport const STATUS_COLOR: Readonly<Record<HealthStatus, string>> = {\n red: '#c0392b',\n yellow: '#e08a00',\n green: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n};\n\n/** 整行底色的浅一档:饱和降一档,免得红黄行「刺眼」压过正文(评审第二轮的扣分点)。 */\n/** 红黄两档整行上底的底色。红灯要**明显重于**黄灯:复评实测原先两个底色亮度几乎一样\n * (红 `#FBF3F3` 对黄 `#FCF3E5`,只差蓝通道),最严重那一档反而最淡——红色提到 0.14。 */\nconst STATUS_TINT: Readonly<Record<HealthStatus, string>> = {\n red: 'rgba(192, 57, 43, 0.14)',\n yellow: 'rgba(224, 138, 0, 0.10)',\n green: 'transparent',\n};\n\n/** 档位词(灯里那几个字)用的色:红黄要**够深**才读得清,故另取一枚比灯更深的值。 */\nexport const STATUS_TEXT: Readonly<Record<HealthStatus, string>> = {\n red: '#a5281b',\n yellow: '#8a5200',\n green: 'var(--dsw-alias-state-success-primary, #4ec9a0)',\n};\n\nconst STATUS_LABEL: Readonly<Record<HealthStatus, string>> = { red: '红', yellow: '黄', green: '绿' };\n\n/** 档位的**形状**标记:颜色之外的第二条读数(灰度截图、色弱视角下也分得开谁要处理)。 */\nconst STATUS_MARK: Readonly<Record<HealthStatus, string>> = { red: '✕', yellow: '!', green: '✓' };\n\n/** 面板上的文字色与分隔线(与 `client.ts` 同一套别名)。 */\nconst INK = 'var(--dsw-alias-label-primary, inherit)';\nconst INK_DIM = 'var(--dsw-alias-label-secondary, #9a9a9a)';\nconst BORDER = 'var(--dsw-alias-border-l1, rgba(128,128,128,.35))';\n\nexport const HEALTH_STYLE = {\n box: {\n padding: '10px 12px',\n marginBottom: 10,\n borderRadius: 10,\n border: '1px solid ' + BORDER,\n } as React.CSSProperties,\n headRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, marginBottom: 4 } as React.CSSProperties,\n head: { fontSize: 13, fontWeight: 700, color: INK } as React.CSSProperties,\n /** 顶部那一行汇总(票 #732):一行装下「配置体检 + 一句总账 + 体检按钮」,三样都不独占行。\n * 它替掉了原先「标题行 + 单独一行的按钮 + 一排灯按钮 + 两行图例」(同屏实测 153px → 一行)。 */\n summaryRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n marginBottom: 10,\n } as React.CSSProperties,\n summaryText: { flex: '1 1 auto', minWidth: 0, fontSize: 12.5, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } as React.CSSProperties,\n /** 各家那张表里的说明行(「还没体检:点上面那个…」那几句)。 */\n meta: { color: INK_DIM, fontSize: 12, lineHeight: 1.7, marginTop: 12 } as React.CSSProperties,\n btn: {\n border: '1px solid ' + BORDER,\n background: 'transparent',\n color: INK,\n borderRadius: 8,\n flex: '0 0 auto',\n padding: '3px 10px',\n fontSize: 12.5,\n fontWeight: 600,\n cursor: 'pointer',\n } as React.CSSProperties,\n error: { color: STATUS_COLOR.red, fontSize: 12, lineHeight: 1.7, marginTop: 6 } as React.CSSProperties,\n} as const;\n\n/** 值得一眼看见的那两档(档位名与整行文字都上色;绿行保持淡)。 */\nfunction isAttention(status: HealthStatus): boolean {\n return status === 'red' || status === 'yellow';\n}\n\n/** 「去哪修」那一行的正文:各家交回来的 action 里若已自带「去哪修:」,这里剥掉一层——\n * 面板已经带了这个前缀,两边都写就会印成「去哪修:去哪修:…」。只剥开头那一次,别的一字不动。 */\nexport function actionText(action: string): string {\n const trimmed = action.trim();\n return trimmed.startsWith('去哪修:') ? trimmed.slice(4).trim() : trimmed;\n}\n\n/** 报文里能扫出来的每一段路径(盘符绝对路径;非 Windows 形状的退回 `/` 打头那一支)。 */\nfunction rawPathsIn(text: string): readonly string[] {\n return text.match(/[A-Za-z]:\\/[^\\s()「」,。;]+/g) ?? text.match(/\\/[^\\s()「」,。;]+/g) ?? [];\n}\n\n/** 一段落点的**根**(盘符):`C:/a/b` → `C:/`。用来把「同一个盘、但根下不同枝」的落点分开算,\n * 免得一个 D 盘上的旁证(比如「配了但目录不在:D:/media」)把 C 盘那几条的缩写全拖没。 */\nfunction rootOf(path: string): string {\n const drive = /^[A-Za-z]:\\//.exec(path);\n return drive === null ? '/' : drive[0];\n}\n\n/** 一段落点的公共目录前缀(按 `/` 切、整段比):`C:/a/b/c` + `C:/a/b/d` → `C:/a/b`。 */\nfunction commonDirOf(paths: readonly string[]): string {\n if (paths.length < 2) return '';\n const split = paths.map((path) => path.replace(/[.,。;]+$/, '').split('/'));\n const first = split[0] as string[];\n let size = 0;\n while (size < first.length && split.every((parts) => parts[size] === first[size])) size += 1;\n return size < 1 ? '' : first.slice(0, size).join('/');\n}\n\n/** 一份报文里**按根分开**的落点前缀(长的在前):只给「同一个根下至少两条落点」的根算前缀——\n * 这个根下只有一条落点时不缩(没有第二条可比,缩出来反而是瞎猜)。\n * 为什么要分根:一家报里常有别的盘的旁证(备忘录用 D 盘上那份媒体目录),只算一个总前缀,\n * 一条旁证就能把整张表的缩写全拖没(#706 出图复评:备忘录那张表整片没缩、记账那张缩了)。 */\nexport function dirPrefixesOf(report: HealthReport): readonly string[] {\n return dirPrefixesIn(pathsOf(report).join(' '));\n}\n\n/** `dirPrefixesOf` 的正文版:一段文字里(报文正文 + 表头两条混在一起)按根分组取公共目录前缀。 */\nexport function dirPrefixesIn(text: string): readonly string[] {\n const paths = rawPathsIn(text);\n const roots = [...new Set(paths.map(rootOf))];\n const prefixes: string[] = [];\n for (const root of roots) {\n const prefix = commonDirOf(paths.filter((path) => rootOf(path) === root));\n if (prefix !== '') prefixes.push(prefix);\n }\n // 长的在前:一个落点命中多条前缀时,最长的那个才是它真正的目录(缩短后不歧义)。\n return prefixes.sort((a, b) => b.length - a.length);\n}\n\n/** 那一行「配置文件 … · 数据目录 …」+ 各条报文里的长路径,都缩成「公共前缀 + 短尾巴」。 */\nfunction shorten(path: string, prefixes: readonly string[]): string {\n const hit = prefixes.find((prefix) => path.startsWith(prefix + '/'));\n return hit === undefined ? path : '…/' + path.slice(hit.length + 1);\n}\n\n/** 一家里所有会印出来的路径。 */\nexport function pathsOf(report: HealthReport): readonly string[] {\n const out: string[] = [report.configPath, report.dataDir];\n for (const item of report.items) out.push(item.message);\n return out;\n}\n\n/** 把报文里那一长串路径按公共前缀缩短(只动打印,不动事实)。\n * 只按**扫得出来的那一段段落点**去缩,不做全串替换:既不误伤长路径,也不会把一段路劈成半截。 */\nexport function shortenPathsIn(text: string, prefixes: readonly string[]): string {\n const candidates = rawPathsIn(text);\n if (candidates.length === 0) return text;\n // 长的在前:先缩长的那条,缩完它自己就挡住了它的父目录,不会二次缩。\n const longestFirst = [...new Set(candidates.map((path) => path.replace(/[.,。;]+$/, '')))].sort(\n (a, b) => b.length - a.length,\n );\n let out = text;\n for (const path of longestFirst) {\n const hit = prefixes.find((prefix) => path.startsWith(prefix + '/'));\n if (hit === undefined) continue;\n out = out.split(hit + '/').join('…/');\n }\n return out;\n}\n\n/** 一盏灯:一家一名一档(数到几个红黄绿)。落点是那家页签(票 #732:面板不再单开一排灯按钮)。 */\nexport interface HealthLightRow {\n readonly id: string;\n readonly title: string;\n readonly status: HealthStatus | null;\n readonly counts: Readonly<Record<HealthStatus, number>>;\n /** 这一家交没交体检通道(页签槽 `options.channel`)。false=这家没装/没接上:圆点保持缺席态,不许点。 */\n readonly hasChannel: boolean;\n}\n\n/** 从六份报告数出那一行的灯(每家按最严重那一档上色)。 */\nexport function lightsOf(\n tabs: readonly { readonly id: string; readonly title: string; readonly hasChannel?: boolean }[],\n reports: Readonly<Record<string, HealthReport | undefined>>,\n): readonly HealthLightRow[] {\n return tabs.map((tab) => {\n const report = reports[tab.id];\n const items = report?.items ?? [];\n return {\n id: tab.id,\n title: tab.title,\n status: report ? worstStatus(items) : null,\n counts: countByStatus(items),\n // 缺席与「装了但还没体检」在报告上都长成 null,分开只能靠这一格(票 #732)。\n hasChannel: tab.hasChannel ?? true,\n // 这里曾短期印过「最严重那一项的名字」当区分度,**已撤回**:实测六家常坏在同一条通用检查项上\n // (都是「数据目录」),印出来六家一模一样,是噪声不是区分度(出图复评实测逐字抄了六个「数据目录」)。\n // 「哪一项坏了」本来就由那家自己那张表回答,灯上不再重复。\n };\n });\n}\n\n/** 一截读数:数字本身(`2`/`3`),颜色即档位(`status` 决定色);`—`(缺席没报告)。`status` 为 null 的那些走淡色。 */\nexport interface HealthCountSeg {\n readonly status: HealthStatus | null;\n readonly text: string;\n}\n\n/** 把一份计数拆成**按档分色**的几截。红黄各自一截、各自上色,互不串色;\n * 数字本身不带档位字(#951):颜色即档位,胶囊形状由调用方配 `COUNT_CAPSULE`/`TAB_NOTE_STYLE`。 */\nexport function countSegsOf(light: HealthLightRow): readonly HealthCountSeg[] {\n if (light.status === null) return [{ status: null, text: '—' }];\n const segs: HealthCountSeg[] = [];\n for (const status of ['red', 'yellow'] as const) {\n const count = light.counts[status];\n if (count > 0) segs.push({ status, text: String(count) });\n }\n return segs.length > 0 ? segs : [{ status: 'green', text: String(light.counts.green) }];\n}\n\n/** 几截读数之间那个分隔符:不许染档位色(它不属于任何一档)。 */\nconst SEG_SEP = ' · ';\n\n/** 一家的读数**写成一行**(纯文本版:给页签上那枚小数字用)。 */\nfunction countText(light: HealthLightRow): string {\n return countSegsOf(light).map((piece) => piece.text).join(SEG_SEP);\n}\n\n/** 数字胶囊的形状(#951):页签那枚 `TAB_NOTE_STYLE` 的同形,汇总与表头共用。\n * 颜色不进这一格,调用方按档配 `STATUS_TEXT[status]`——颜色即档位,字上不再写档位名。 */\nexport const COUNT_CAPSULE_STYLE: React.CSSProperties = {\n border: '1px solid ' + BORDER,\n borderRadius: 999,\n padding: '0 6px',\n margin: '0 2px',\n fontSize: 11,\n fontWeight: 700,\n} as const;\n\n/** 汇总行那一句:绿要显眼(它是「正常」这件事本身),没通道的那几家不冒充绿。\n *\n * 「没有体检出口」与「还没跑过体检」是**两件事**(票 #735):前者那颗「体检一次」按下去不会有任何反应,\n * 必须自己说出来——两态共用一句「还没体检」时,用户与诊断都会被引到「点了没跑」上去。 */\nfunction overallText(lights: readonly HealthLightRow[]): React.ReactElement {\n const pending = lights.filter((light) => !light.hasChannel).length;\n const withChannel = lights.filter((light) => light.hasChannel);\n const tail = pending > 0 ? ' · ' + String(pending) + ' 家没有体检出口' : '';\n if (withChannel.length === 0) {\n const what = lights.length === 0\n ? '还没有任何一家登记体检出口'\n : String(lights.length) + ' 家都没有体检出口(通道名没登记上)';\n return React.createElement('span', { style: { color: STATUS_TEXT.red, fontWeight: 700 } }, '没有可体检的家:' + what);\n }\n const allGreen = withChannel.every((light) => light.status === 'green');\n if (allGreen && pending === 0) {\n return React.createElement('span', { style: { color: STATUS_COLOR.green, fontWeight: 700 } }, '六家正常');\n }\n const parts: React.ReactElement[] = [];\n for (const status of ['red', 'yellow'] as const) {\n let count = 0;\n for (const light of lights) count += light.counts[status];\n if (count === 0) continue;\n if (parts.length > 0) parts.push(React.createElement('span', { key: status + '-sep', style: { color: INK_DIM } }, SEG_SEP));\n parts.push(\n React.createElement(\n 'span',\n { key: status, style: { ...COUNT_CAPSULE_STYLE, color: STATUS_TEXT[status] } },\n String(count),\n ),\n );\n }\n if (parts.length === 0) {\n // 一份报告都没跑过;有出口的已经全绿、只是还有没出口的家时,不许把「还没体检」安到它们头上。\n return React.createElement('span', { style: { color: INK_DIM } }, (allGreen ? '有出口的都正常' : '还没体检') + tail);\n }\n parts.push(React.createElement('span', { key: 'todo', style: { color: INK } }, ' 要处理' + tail));\n return React.createElement('span', null, parts);\n}\n\n/** 那枚小数字用的底色(比灯色浅一档,压在页签上不抢眼)。 */\nconst STATUS_DOT: Readonly<Record<HealthStatus, string>> = {\n red: STATUS_COLOR.red,\n yellow: STATUS_COLOR.yellow,\n green: STATUS_COLOR.green,\n};\n\n/** 页签圆点的颜色与语义(票 #732):从「实心/空心=装没装」改成「颜色=体检档位」。 */\nexport const TAB_DOT = {\n /** 没装(页签槽账本里没有这一家)。 */\n absent: STATUS_DOT.green,\n /** 装了、但还没跑过体检。 */\n unchecked: INK_DIM,\n} as const;\n\n/** 页签圆点该用什么色:装了按档位(红黄绿),没装/没跑过走缺席态。 */\nexport function tabDotColor(light: HealthLightRow): string {\n if (!light.hasChannel) return TAB_DOT.absent;\n return light.status === null ? TAB_DOT.unchecked : STATUS_DOT[light.status];\n}\n\n/** 页签上那枚小数字(票 #732):红黄那几家,家名后带一条计数;全绿与没跑过不带。 */\nexport const TAB_NOTE_STYLE: React.CSSProperties = {\n marginLeft: 5,\n border: '1px solid ' + BORDER,\n borderRadius: 999,\n padding: '0 5px',\n fontSize: 11,\n fontWeight: 700,\n} as const;\n\n/** 那枚小数字写什么:只报「要处理」的那两档;一家里红黄都有时先报红那一档(圆点已经按最严重的档上色了)。 */\nexport function tabNote(light: HealthLightRow): HealthCountSeg | null {\n if (!light.hasChannel || light.status === null || light.status === 'green') return null;\n return countSegsOf(light)[0] ?? null;\n}\n\n/** 顶部那一行汇总(票 #732:不再一家一盏灯按钮,一行总账 + 一个体检按钮)。\n *\n * 与页签的分工:这行只答「一共几条要处理」;「哪几家、各几条」由页签的圆点与小数字答;\n * 「具体哪一条坏了、去哪修」由那家页签里的 `HealthTable` 答。 */\nexport function HealthSummaryLine(props: {\n readonly lights: readonly HealthLightRow[];\n readonly running: boolean;\n readonly error: string | null;\n readonly onRun: () => void;\n}): React.ReactElement {\n return React.createElement(\n 'div',\n { style: HEALTH_STYLE.summaryRow, 'data-ilife-health': 'summary' },\n React.createElement('div', { style: HEALTH_STYLE.head }, '配置体检'),\n React.createElement(\n 'div',\n { style: HEALTH_STYLE.summaryText },\n // 一家都没有出口这一态由 overallText 自己说(#735):这里不再用一句「还没体检」把两件事糊成一件。\n props.error !== null ? React.createElement('span', { style: { color: STATUS_TEXT.red } }, props.error) : overallText(props.lights),\n ),\n React.createElement(\n 'button',\n {\n type: 'button',\n style: props.running ? { ...HEALTH_STYLE.btn, opacity: 0.55, cursor: 'default' } : HEALTH_STYLE.btn,\n disabled: props.running,\n onClick: props.onRun,\n // 「只看不改」那三条口径从屏上撤下来了(它原先和两排圆点的图例挤在一行),\n // 但它**仍然要说出口**:按钮悬停是它能待的地方(票 #732;测试按产物里的字符串守着)。\n title: '对六家各跑一次配置体检(只看不改:不建目录、不改配置、不自动重置)',\n // #1163 T4:体检按钮复用共享交互缝(按压+悬停+焦点+禁用+长按纯视觉环),tokens 只引共享层。\n // 长按纯视觉:CSS 驱动 0→100%(0.5s),零回调零状态,不进状态机分支;折叠/复制/提示/液态本面不适用。\n 'data-ilife-press': 'health-run',\n 'data-ilife-longpress': 'health-run',\n },\n props.running ? '体检中…' : '体检一次',\n ),\n // #1163 T4:同一生成处 interactionCss(),一枚 style 罩本行内两类属性;更新面无渲染点(见票内豁免证据)。\n React.createElement('style', { 'data-ilife-interaction': 't1' }, interactionCss()),\n );\n}\n\n/** 正正常常的那些:绿条只留「名字」,一句话与「去哪修」都不印(票面验收第一条:正常的收成一行)。 */\nfunction isAttentionItem(item: HealthItem): boolean {\n return isAttention(item.status);\n}\n\n/** 表头那组数字(#951):与页签/汇总同口径——只印数字胶囊,颜色即档位,不印档位字。 */\nfunction headerCounts(report: HealthReport): React.ReactElement {\n const counts = countByStatus(report.items);\n const status = worstStatus(report.items);\n const segs = countSegsOf({ id: 'header', title: '', status, counts, hasChannel: true });\n return React.createElement(\n 'div',\n { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 } },\n segs.map((seg, index) => seg.status === null\n ? React.createElement('span', { key: index, style: { color: INK_DIM } }, seg.text)\n : React.createElement(\n 'span',\n { key: index, style: { ...COUNT_CAPSULE_STYLE, color: STATUS_TEXT[seg.status] } },\n seg.text,\n )),\n );\n}\n\n/** 一家那张表:**要处理的**(红黄)一条一块、整行上底带一句话与「去哪修」;\n * **正常的**(绿)收成一行并列的小字,点得到、扫得完(票面验收第一条:正常的收成一行,有问题的才展开)。 */\nexport function HealthTable(props: {\n readonly title: string;\n readonly phase: 'idle' | 'running' | 'ready' | 'failed';\n readonly report: HealthReport | null;\n readonly error: string | null;\n}): React.ReactElement {\n const { report } = props;\n const prefixes: readonly string[] = report ? dirPrefixesOf(report) : [];\n return React.createElement(\n 'div',\n { style: HEALTH_STYLE.box, 'data-ilife-health': 'table' },\n React.createElement(\n 'div',\n { style: HEALTH_STYLE.headRow },\n React.createElement(TitleBlock, { product: props.title, purpose: '体检' }),\n report ? headerCounts(report) : null,\n ),\n props.phase === 'idle'\n ? React.createElement('div', { style: HEALTH_STYLE.meta }, '还没体检:点上面那个「体检一次」。')\n : null,\n props.phase === 'running' ? React.createElement('div', { style: HEALTH_STYLE.meta }, '正在体检…') : null,\n props.error !== null ? React.createElement('div', { style: HEALTH_STYLE.error }, props.error) : null,\n report\n ? React.createElement(\n 'div',\n null,\n // 行内落点缩成「…/短尾」是为了不把每行撑成两行;但「去哪修:先建这个目录」要照着建,\n // 就得把**它到底在哪儿**说清楚(出图复评五份里两份点名「照屏修不了」)。\n // 措辞有讲究:不能写成「下面行内的「…/」都接在这里」——行里本来就有个「…/data」,\n // 接上基目录会拼成「…/data/data」,自相矛盾(复评原话)。故只**点明缩写代表哪一层**。\n report.dataDir !== ''\n ? React.createElement('div', { style: { color: INK_DIM, fontSize: 11.5, marginTop: 2 } },\n '落点根目录:' + report.dataDir\n + (report.configPath !== '' ? '(配置文件 ' + report.configPath + ')' : ''))\n : null,\n report.items.filter(isAttentionItem).map((item) =>\n React.createElement(\n 'div',\n {\n key: item.id,\n 'data-ilife-health': 'item',\n 'data-status': item.status,\n // 红黄两档整行上底:扫一眼先看见「要处理的」。\n style: {\n padding: '7px 8px',\n margin: '2px 0',\n borderTop: '1px solid ' + BORDER,\n borderRadius: 6,\n fontSize: 13,\n lineHeight: 1.75,\n background: STATUS_TINT[item.status],\n },\n },\n React.createElement(\n 'div',\n { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6 } },\n // 档位**不只靠颜色**认:形状标记(✕/!)+档位字,色弱与灰度截图下也分得开。\n // 原先这里还有一个同色的实心圆点,与标记、档位字三者同义同色(复评两份点名「没有主次」),\n // 且页签那排(`client.ts:349`)也用同样的圆点表示「这家装没装」——同屏两套圆点语义,\n // 故本件的条目里**去掉圆点**,把圆点这一格语汇留给页签。\n React.createElement('span', {\n style: { color: STATUS_TEXT[item.status], fontWeight: 700, fontSize: 13, lineHeight: 1 },\n 'aria-hidden': 'true',\n }, STATUS_MARK[item.status]),\n React.createElement('span', { style: { fontWeight: 650, color: INK } }, item.title),\n React.createElement('span', {\n style: { color: STATUS_TEXT[item.status], fontWeight: 700 },\n }, STATUS_LABEL[item.status]),\n // 来源那一格**只在它不只是「默认值」时印**:四行全印「来自默认值」等于零区分度,\n // 而且「值从哪来」这件事正文里已经写全了(复评原话:「这四个字零区分度」)。\n item.source && item.source !== '默认值'\n ? React.createElement('span', { style: { color: INK_DIM } }, '· 来自' + item.source)\n : null,\n ),\n React.createElement('div', { style: { color: INK }, title: item.message }, shortenPathsIn(item.message, prefixes)),\n item.action.length > 0\n ? React.createElement('div', { style: { color: INK_DIM } }, '去哪修:' + actionText(item.action))\n : null,\n ),\n ),\n // 正常的那一档:一条都不用点开,名字排成一行(`data-ilife-health=\"ok\"` 供判据核对「一条不少」)。\n // 给它**一条绿边+一层极淡底**,让它读起来是「一组正常项」,而不是四行问题之后的一句脚注\n // (复评原话:「占多数的那一档在视觉上几乎退成脚注」)——条目仍然收成一行(票面第一条)。\n report.items.some((item) => !isAttentionItem(item))\n ? React.createElement(\n 'div',\n {\n 'data-ilife-health': 'ok',\n style: {\n display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '2px 10px',\n marginTop: 8, padding: '6px 8px',\n borderLeft: '3px solid ' + STATUS_COLOR.green,\n borderRadius: 6,\n fontSize: 12, lineHeight: 1.6, color: INK_DIM,\n },\n },\n // 绿档也要**看得见**:只给一行灰字时,五份出图复评里三份独立指到「全屏没有绿色、\n // 绿=正常这一档等于不存在」。故这一档前面加一个绿点(与红黄同一个「点」语汇),\n // 说明它们就是正常项——条目**仍然收成一行**(票面第一条:正常的收成一行)。\n React.createElement('span', {\n style: {\n width: 8, height: 8, borderRadius: '50%', display: 'inline-block',\n background: STATUS_COLOR.green, flex: '0 0 auto',\n },\n 'aria-hidden': 'true',\n }),\n React.createElement('span', { style: { fontWeight: 650, color: INK } }, '正常 ' + String(countByStatus(report.items).green) + ' 条'),\n React.createElement('span', { style: { color: INK_DIM } }, '(下列各项都正常工作)'),\n report.items.filter((item) => !isAttentionItem(item)).map((item) =>\n React.createElement('span', { key: item.id, 'data-ilife-health': 'ok-item' }, '· ' + item.title),\n ),\n )\n : null,\n )\n : null,\n );\n}\n","/** 「本机根清单」的取数接线(客户端半):把一通电话变成图上那一行可跳转的根。\n *\n * **不认识宿主**:载体基段、通道名(第二段)、电话名全由调用方传进来——各家从自己的宿主镜像里拿\n * 那几个字面量(和 `openRowBrowser` 的 `refusalCode` 同一条规矩)。本件只做两件事:\n * 发一次调用、把回执信封拆成根清单;**任何失败都当空清单**(图上少画一行,不报错、不挡路)。\n */\n\nimport type { RootKind, RootRow } from './directory-browser-contract.js';\n\n/** 取数口:`connection.rpc.call` 的形状,每次现取(连接后到也不永久缺席)。 */\nexport type RootsCall = (\n base: string,\n endpoint: string,\n payload: unknown,\n signal?: AbortSignal,\n) => Promise<unknown>;\n\n/** 认一认某个值像不像一个根(认不出的行丢掉:图的干净比多画一行重要)。 */\nfunction isRootRow(raw: unknown): raw is RootRow {\n if (typeof raw !== 'object' || raw === null) return false;\n const row = raw as { path?: unknown; kind?: unknown };\n return typeof row.path === 'string' && row.path !== '' && typeof row.kind === 'string';\n}\n\n/** 信封 → 根清单(纯函数,**永不抛**):`{ok:true, value:{roots:[…]}}` 之外的形状一律当空。 */\nexport function readRootsAnswer(raw: unknown): readonly RootRow[] {\n const answer = (typeof raw === 'object' && raw !== null ? raw : {}) as { ok?: unknown; value?: unknown };\n if (answer.ok !== true) return [];\n const value = (typeof answer.value === 'object' && answer.value !== null ? answer.value : {}) as { roots?: unknown };\n if (!Array.isArray(value.roots)) return [];\n return value.roots.filter(isRootRow).map((row) => ({ path: row.path, kind: row.kind as RootKind }));\n}\n\n/** 建一个「根清单取数器」:给调用口与三个名字,回一个 `() => Promise<RootRow[]>`。\n *\n * 界面按需调它(开图一次),不在渲染里调,也不缓存到这里——缓存是宿主半的事。 */\nexport function createRootsSource(input: {\n readonly getCall: () => unknown;\n readonly base: string;\n readonly endpoint: string;\n readonly method: string;\n}): () => Promise<readonly RootRow[]> {\n return async () => {\n const call = input.getCall();\n if (typeof call !== 'function') return [];\n try {\n return readRootsAnswer(\n await (call as RootsCall)(input.base, input.endpoint, { method: input.method, payload: {} }),\n );\n } catch {\n return [];\n }\n };\n}\n","/** dsh-life-pack client 适配器(六边形:browser 侧;爱生活单卡方案)。\n *\n * 真实 DSH 契约(出处见 docs/agents/dsh-client-contract.md §5/§11):\n * - 本文件经 tsdown 打成 loader 工厂包(browser/CJS + 注册包裹),classic 执行\n * 只注册,副作用全在 factory 内;传递闭包禁 node 内建与 ESM 语法(回路\n * test/client-bundle-48.test.mjs 看门)。\n * - inject 短名 ['slots','connection']:本包现在有 RPC(装与更新的能力由宿主半提供,\n * 电话名与轮询间隔由宿主转交,面板不写死),读了 ctx.connection 就必须声明 'connection'(#48 血例)。\n * - 爱生活页签条走 slot 驱动(settings-plugins 先例,禁纯 useState 手画 tab):\n * section 声明 children 爱生活页签槽,各技能往里注册自家技能设置页;\n * tab 行从 ledger 投影(entries+getVersion+subscribe),面板用 props.renderSlot\n * 按 `{only}` 投影(一次只挂载一个)+ visited 缓存;ledger 无某技能=缺席,\n * 显示推荐安装只读文本(不返空冒充)。\n * - 组件 React.createElement 手写(禁 JSX),无数据轮询、无 document/window/process。\n * - 面板外壳两处(票 #679):标题行右上角两个入口(星/气泡,悬停出说明文字,\n * 窄窗口折到标题下方);页签面板之后是底部「作者其他插件」引流卡(四行,点开新窗口)。\n * 文案与网址都取自 nav.ts 的两张静态表,本文件只画不算。\n */\n\nimport * as React from 'react';\nimport { MANAGER_PLUGIN, MANAGER_TABS, MORE_PLUGINS, PANEL_LINKS, recoFor } from './nav.js';\nimport type { ManagerTab } from './nav.js';\nimport { CONFIG_TAB_SLOT } from './update-contract.js';\nimport { managerCallAdapter, mountLifeBatchEntry } from './update-dialog.js';\nimport { tabInteractionCss } from './config-panel-view.js';\nimport { LIQUID_BASE_MS, LIQUID_DIST_FACTOR, LIQUID_EASE, LIQUID_GHOST_MS, LIQUID_MAX_MS, LIQUID_MIN_MS, LIQUID_STRETCH_X, LIQUID_STRETCH_Y } from './config-panel-contract.js';\nimport { summaryErrorOf, useHealthPanel } from './health-panel.js';\nimport { HealthSummaryLine, HealthTable, STATUS_TEXT, TAB_DOT, TAB_NOTE_STYLE, lightsOf, tabDotColor, tabNote } from './health-view.js';\nimport { HEALTH_ENDPOINT } from './health-contract.js';\nimport type {\n ClientCtx,\n ConfigTabRow,\n LifePackSectionProps,\n RpcCallFace,\n SlotLedgerEntry,\n} from './dsh-ctx.js';\n\nexport const inject = ['slots', 'connection'];\n\n/** 爱生活页签槽名:定义在 `update-contract.ts`(宿主判「已装产物有没有注册代码」也用这个名字,一处定义)。 */\nexport { CONFIG_TAB_SLOT } from './update-contract.js';\n\n/** 共用件「目录浏览器」从包门转出(票 #744):六个单品插件的设置页要用的就是这两样——\n * 「开图接线 + 入口三态判定」(纯逻辑,`./directory-browser` 子路径)与\n * 「对话框组件」(`./directory-browser-ui` 子路径,吃 React)。\n *\n * 六家的 client 束**不能**从本条 `./client` 取:这一条是 loader 工厂包\n * (`window.__ModuleLoader__.load(...)` 的注册壳),`require` 拿到的是空 exports。\n * 所以对外要用的是那两个普通子路径;这里只是同源转出,供本包内部与用例使用。 */\nexport { DirectoryBrowserFromRow } from './directory-browser-ui.js';\nexport type { DirectoryBrowserLabels, DirectoryBrowserProps } from './directory-browser-ui.js';\nexport { createBrowseController, createDirectoryRowBrowser, rowsOf, canGoUp, targetOf, entryPath } from './directory-browser-state.js';\nexport type { BrowseController, BrowseState, DirectoryRowBrowser } from './directory-browser-state.js';\nexport { pickerModeOf, readPickAnswer } from './directory-browser-contract.js';\nexport type { DirectoryBrowseFace, DirectoryListing, PickOutcome, RootKind, RootRow } from './directory-browser-contract.js';\nexport { createRootsSource, readRootsAnswer } from './directory-browser-roots.js';\n\n/** 包名 → 本家那条客户端通道(票 #735)。取值面是导航表那份镜像,不再从页签槽账本的自定义选项里读:\n * 装机槽位面不透传自定义键,读了恒是空串(详见 `nav.ts` 上 `ManagerTab.channel` 的注释)。 */\nconst CHANNEL_BY_PLUGIN = new Map(MANAGER_TABS.map((tab) => [tab.plugin, tab.channel]));\n\n/** 总管视觉(内联 style;颜色走 DSH 主题别名,深浅主题自适应,写死值只做回退;与技能面板同语言)。 */\nconst S = {\n headRow: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: 8,\n margin: '2px 0 8px',\n } as React.CSSProperties,\n head: { fontSize: 15, fontWeight: 700, flex: '0 0 auto', color: 'var(--dsw-alias-label-primary, inherit)' } as React.CSSProperties,\n headActions: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 2,\n marginLeft: 'auto',\n flex: '0 0 auto',\n } as React.CSSProperties,\n iconLink: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 26,\n height: 26,\n borderRadius: 6,\n border: '1px solid transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n textDecoration: 'none',\n fontSize: 14,\n lineHeight: 1,\n } as React.CSSProperties,\n /** #933:页签格里「配置卡 + 体检卡」这一列的堆叠间距(唯一口径,别再给某一张卡加 margin)。 */\n tabStack: { display: 'flex', flexDirection: 'column', gap: 10 } as React.CSSProperties,\n /** #937:标题行里那枚版本胶囊(形状照真源 v3.1 的 `.ic-ver`:10px 字、1px 描边、999 圆角)。 */\n versionCapsule: {\n fontSize: 10.5,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary, #adb2b8)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))',\n borderRadius: 999,\n padding: '0 7px',\n whiteSpace: 'nowrap',\n flex: '0 0 auto',\n fontWeight: 400,\n } as React.CSSProperties,\n tablist: { display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 10 } as React.CSSProperties,\n tab: {\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n borderRadius: 999,\n padding: '4px 12px',\n fontSize: 13,\n cursor: 'pointer',\n } as React.CSSProperties,\n tabActive: {\n background: 'var(--dsw-alias-brand-primary, #0a84ff)',\n borderColor: 'transparent',\n // 前景取**与 brand-primary 成对**的宿主别名(#931):写死 `#fff` 会在深色主题下撞色\n // (宿主的 brand-primary 在深色是近白 `#f9fafb`,白字压上去只有 1.045:1)。\n color: 'var(--dsw-alias-label-primary-foreground, #0f1115)',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border-l1, rgba(128,128,128,.45))',\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n cmd: {\n display: 'block',\n marginTop: 8,\n padding: '8px 10px',\n borderRadius: 8,\n background: 'var(--dsw-alias-bg-base, rgba(128,128,128,.12))',\n color: 'var(--dsw-alias-label-primary, inherit)',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n userSelect: 'all',\n } as React.CSSProperties,\n moreCard: {\n marginTop: 16,\n padding: '10px 12px',\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))',\n } as React.CSSProperties,\n moreTitle: {\n fontSize: 13,\n fontWeight: 700,\n marginBottom: 4,\n color: 'var(--dsw-alias-label-primary, inherit)',\n } as React.CSSProperties,\n moreRow: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '2px 8px',\n padding: '7px 4px',\n textDecoration: 'none',\n color: 'inherit',\n } as React.CSSProperties,\n morePkg: {\n flex: '0 1 auto',\n minWidth: 0,\n overflowWrap: 'anywhere',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n fontWeight: 650,\n } as React.CSSProperties,\n moreDesc: {\n flex: '1 1 180px',\n minWidth: 0,\n fontSize: 11.5,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n moreIcon: {\n flex: '0 0 auto',\n marginLeft: 'auto',\n display: 'inline-flex',\n color: 'var(--dsw-alias-label-secondary, #9a9a9a)',\n } as React.CSSProperties,\n /** 更新入口挂点:只占位,按钮本体由上游入口件渲染(票 1170);批量弹窗壳亦由上游提供,本包零自家样式。 */\n updateEntrySlot: {\n display: 'inline-flex',\n alignItems: 'center',\n } as React.CSSProperties,\n};\n\n/** 更新区(票 1170 薄接线,完全上游 UI):标题行挂上游入口按钮,点开即上游 dialog 七目标批量面板;缺席页签仍显示静态推荐,不进批量 targets。 */\n\n/** 标签解析(resolveSlotLabel 同形:thunk 跟活,无则空字串;见 slots lib:27-29)。 */\nfunction resolveLabel(label: SlotLedgerEntry['options']['label']): string {\n if (typeof label === 'function') return label();\n return label ?? '';\n}\n\n/** 外链图标(行尾那个;纯装饰,语义在链接自己的 aria-label/文字上)。 */\nfunction ExternalIcon(): React.ReactElement {\n return React.createElement(\n 'svg',\n {\n viewBox: '0 0 24 24',\n width: 13,\n height: 13,\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.9,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n 'aria-hidden': 'true',\n focusable: 'false',\n },\n React.createElement('path', { d: 'M13.5 4.5H19.5V10.5' }),\n React.createElement('path', { d: 'M19.5 4.5L11 13' }),\n React.createElement('path', { d: 'M18 14.5V18A2 2 0 0 1 16 20H6A2 2 0 0 1 4 18V8A2 2 0 0 1 6 6H9.5' }),\n );\n}\n\n/** 标题行右上角两件:星(去本仓点 Star)+ 气泡(去本仓开 issue),悬停出说明文字。\n *\n * 三件并排里的第三件「检查更新」住隔壁票(#678):本票只留位子——它就接在本组件之前,\n * 不画一个点了没反应的假按钮。窄窗口靠 headRow 的 flexWrap 折到标题下方,\n * 标题与两件都是 flex:'0 0 auto',谁也不挤谁。\n */\nfunction PanelActions(): React.ReactElement {\n return React.createElement(\n 'div',\n { style: S.headActions },\n PANEL_LINKS.map((link) =>\n React.createElement(\n 'a',\n {\n key: link.key,\n href: link.url,\n target: '_blank',\n rel: 'noreferrer',\n title: link.tip,\n 'aria-label': link.tip,\n style: S.iconLink,\n },\n React.createElement('span', { 'aria-hidden': 'true' }, link.glyph),\n ),\n ),\n );\n}\n\n/** 底部「作者其他插件」引流卡:四行(包名 + 一句说明 + 行尾外链图标),点开新窗口。\n *\n * 卡片在页签面板之后,六页签切换它都在(它不属于任何一个页签)。\n */\nfunction MorePluginsCard(): React.ReactElement {\n return React.createElement(\n 'div',\n { style: S.moreCard },\n React.createElement('div', { style: S.moreTitle }, '作者其他插件'),\n MORE_PLUGINS.map((row) =>\n React.createElement(\n 'a',\n {\n key: row.pkg,\n href: row.url,\n target: '_blank',\n rel: 'noreferrer',\n title: row.pkg + ' 的 GitHub 仓库',\n style: S.moreRow,\n },\n React.createElement('span', { style: S.morePkg }, row.pkg),\n React.createElement('span', { style: S.moreDesc }, row.desc),\n React.createElement('span', { style: S.moreIcon }, React.createElement(ExternalIcon, null)),\n ),\n ),\n );\n}\n\n/** 总管自述版本(票 #737;#937 改口):**不手写**,问宿主——宿主读自己这份已安装包的 `package.json`。\n *\n * 三态(#937):`loading`(首帧就画胶囊占位 `…`)/`ready`(读到,画包名 · 版本)/`missing`(读不到,画「版本未知」)。\n * **失败要重试**:原先只取一次、一失败就永远停在未知(#937 查出来的真缺陷);这里按 1s/3s/8s 退避重试三次。\n * 与卡路里 #130 同一条路:面板是浏览器产物、禁 node 内建,读盘只许在宿主半。 */\n/** 读中 / 读不到 两种胶囊文案(#937:占位符用 `…`,读不到给人话,不把 `unknown` 印给人看)。 */\n/** 构建期注入的版本号(tsdown `define`,值取自本包 `package.json` 的 `version`;票 #986)。\n *\n * 为什么烙进产物而不是问宿主:这行字是**标签**,延迟不该依赖宿主可用性——此前走\n * `ilife-manager.version` 电话,面板要先过一次往返,接不上还要按退避表等,首帧只能显示占位。\n * 漂移(改了包描述文件却忘了重建产物)由构建门咬住:产物里的注入值必须等于包版本。 */\ndeclare const __LIFE_PACK_VERSION__: string;\n\n/** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */\nconst MANAGER_VERSION_TEXT = MANAGER_PLUGIN + ' · ' + __LIFE_PACK_VERSION__;\n\n/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */\nfunction LifePackSection(props: LifePackSectionProps & { getCall: () => RpcCallFace | null }): React.ReactElement {\n const tabsId = React.useId();\n const tabRefs = React.useRef<Array<HTMLButtonElement | null>>([]);\n const rows: ConfigTabRow[] = props.useTabs((value) => value);\n const present = new Set(rows.map((r) => r.id));\n // 配置体检(#706):一张表六份报告,顶部那行汇总与各家那张表都从它读(同一份数据)。\n // 通道名的来源见 CHANNEL_BY_PLUGIN(#735:账本那一格读不到,改取导航表那份镜像)。\n const healthTabs = React.useMemo(\n () => rows.filter((row) => row.channel.length > 0).map((row) => ({ id: row.id, channel: row.channel })),\n [rows],\n );\n const health = useHealthPanel(() => props.getCall(), healthTabs);\n /** 六家的灯(票 #732):按**页签槽账本**那一排过(装了但没体检通道的也在内,它显缺席态)。 */\n const lights = React.useMemo(\n () => lightsOf(\n rows.map((row) => ({ id: row.id, title: row.label, hasChannel: row.channel.length > 0 })),\n Object.fromEntries(Object.entries(health.rows).map(([id, row]) => [id, row.report ?? undefined])),\n ),\n [rows, health.rows],\n );\n const lightOf = (id: string) => lights.find((light) => light.id === id);\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined);\n /** 批量入口挂载位(一颗按钮看七家聚合;0.5.4 #49 到达,弹窗 dialog 由入口件内置,开关态亦归上游)。 */\n const entryRef = React.useRef<HTMLSpanElement | null>(null);\n const callReady = props.getCall() !== null;\n /** 入口件挂载:连接后到才挂(取用器现取),卸载即 unmount(只停入口轮询)。 */\n React.useEffect(() => {\n const mount = entryRef.current;\n if (mount === null || !callReady) return;\n const entry = mountLifeBatchEntry(mount, managerCallAdapter(props.getCall()));\n return () => {\n entry.unmount();\n };\n // getCall 本身是取用器(引用稳定),只跟连接就绪态重跑。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [callReady]);\n const [visitedIds, setVisitedIds] = React.useState<ReadonlySet<string>>(() => new Set());\n const active = MANAGER_TABS.some((t) => t.plugin === activeId)\n ? (activeId as string)\n : MANAGER_TABS[0].plugin;\n React.useEffect(() => {\n setVisitedIds((previous) => {\n if (previous.has(active)) return previous;\n return new Set([...previous, active]);\n });\n }, [active]);\n /** 预热:section 一挂载,就把其余几家**错峰**挂上(隐藏面板照常挂载、只切 `hidden`),\n * 让各家那一次配置读提前跑掉——用户点哪个页签,多半值已经在了。\n * 错峰(每 150ms 一家)是为了不让六家子进程同时抢开工:首帧那一口气优先留给框架。\n * 只在页签账本非空时起一次(`rows` 要等各家 slot 注入后才齐)。\n *\n * **一次性**(p10 定时器门的例外依据):每个页签只挂一次(`warmed` 闸门 + 一批固定数量的定时器),\n * 不是轮询、也不是重试——它没有「次数」可言;卸载即 `clearTimeout` 清干净。\n * 不做轮询是本意:这里只补一次提前量,取数与刷新仍旧由各家用例自己的通道走。 */\n const warmed = React.useRef(false);\n const tabCount = rows.length;\n React.useEffect(() => {\n if (warmed.current || tabCount === 0) return;\n warmed.current = true;\n const STAGGER_MS = 150;\n const timers = MANAGER_TABS\n .filter((tab) => rows.some((row) => row.id === tab.plugin))\n .map((tab, index) => setTimeout(() => {\n setVisitedIds((previous) => (previous.has(tab.plugin) ? previous : new Set([...previous, tab.plugin])));\n }, STAGGER_MS * (index + 1)));\n return () => {\n for (const timer of timers) clearTimeout(timer);\n };\n }, [tabCount]);\n function labelFor(tab: ManagerTab): string {\n const row = rows.find((r) => r.id === tab.plugin);\n return row && row.label.length > 0 ? row.label : tab.title;\n }\n void 0;\n function onTabKeyDown(event: React.KeyboardEvent, index: number): void {\n let nextIndex: number | undefined;\n switch (event.key) {\n case 'ArrowRight': nextIndex = (index + 1) % MANAGER_TABS.length; break;\n case 'ArrowLeft': nextIndex = (index - 1 + MANAGER_TABS.length) % MANAGER_TABS.length; break;\n case 'Home': nextIndex = 0; break;\n case 'End': nextIndex = MANAGER_TABS.length - 1; break;\n default: return;\n }\n event.preventDefault();\n const next = MANAGER_TABS[nextIndex];\n setActiveId(next.plugin);\n tabRefs.current[nextIndex]?.focus();\n }\n /** T6 液态彗星式滑移(#1165):glider 在下层走 pill,页签自身透明(选中态内联透明+CSS 兜底)。\n *\n * 原型 `liquidTo` 原式:时长 200+距离×0.35、夹 220–380ms、缓动 cubic-bezier(.3,1.1,.4,1);\n * 滑移中 glider 在 45% 处拉伸 1.28/.86(Web Animations,失败则 CSS 过渡仍送到位);\n * ghost 影子留在起点、180ms 消散(彗星式,非桥接残影)。首挂只摆位不动。\n * 无 document/window/process:只经 refs 量量(offset*)与写样式;减少动态由 CSS 媒体查询接管(本 effect 不判断)。 */\n const gliderRef = React.useRef<HTMLSpanElement | null>(null);\n const ghostRef = React.useRef<HTMLSpanElement | null>(null);\n const prevTabRect = React.useRef<{ readonly left: number; readonly top: number; readonly width: number; readonly height: number } | null>(null);\n React.useEffect(() => {\n const index = MANAGER_TABS.findIndex((tab) => tab.plugin === active);\n const el = index < 0 ? null : tabRefs.current[index];\n const glider = gliderRef.current;\n const ghost = ghostRef.current;\n if (el === null || el === undefined || glider === null || ghost === null) return;\n const next = { left: el.offsetLeft, top: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight };\n const prev = prevTabRect.current;\n prevTabRect.current = next;\n const place = (rect: { readonly left: number; readonly top: number; readonly width: number; readonly height: number }): void => {\n glider.style.left = String(rect.left) + 'px';\n glider.style.top = String(rect.top) + 'px';\n glider.style.width = String(rect.width) + 'px';\n glider.style.height = String(rect.height) + 'px';\n };\n if (prev === null || (prev.left === next.left && prev.top === next.top && prev.width === next.width)) {\n place(next);\n return;\n }\n const dist = Math.abs(next.left - prev.left);\n const dur = Math.min(LIQUID_MAX_MS, Math.max(LIQUID_MIN_MS, LIQUID_BASE_MS + dist * LIQUID_DIST_FACTOR));\n ghost.style.transition = 'none';\n ghost.style.left = String(prev.left) + 'px';\n ghost.style.top = String(prev.top) + 'px';\n ghost.style.width = String(prev.width) + 'px';\n ghost.style.height = String(prev.height) + 'px';\n ghost.style.opacity = '1';\n void ghost.offsetWidth;\n ghost.style.transition = 'opacity ' + String(LIQUID_GHOST_MS / 1000) + 's ease';\n ghost.style.opacity = '0';\n glider.style.transition = 'none';\n place(prev);\n void glider.offsetWidth;\n glider.style.transition =\n 'left ' + String(dur) + 'ms ' + LIQUID_EASE + ',top ' + String(dur) + 'ms ' + LIQUID_EASE + ',width ' + String(dur) + 'ms ' + LIQUID_EASE;\n place(next);\n try {\n glider.animate(\n [\n { transform: 'scaleX(1) scaleY(1)' },\n { transform: 'scaleX(' + String(LIQUID_STRETCH_X) + ') scaleY(' + String(LIQUID_STRETCH_Y) + ')', offset: 0.45 },\n { transform: 'scaleX(1) scaleY(1)', offset: 0.8 },\n { transform: 'scaleX(1) scaleY(1)' },\n ],\n { duration: dur, easing: 'ease-out' },\n );\n } catch {\n // 无 Web Animations 也无妨:left/top/width 的 CSS 过渡仍把滑移送到位\n }\n }, [active]);\n return React.createElement(\n 'div',\n null,\n React.createElement(\n 'div',\n { style: S.headRow },\n React.createElement('div', { style: S.head }, '爱生活'),\n // #986:版本号构建期注入,首帧就是真值;不再有读中/读不到两态(漂移由构建门守)。\n React.createElement(\n 'span',\n { style: S.versionCapsule, 'data-ilife-version': 'capsule' },\n MANAGER_VERSION_TEXT,\n ),\n React.createElement(\n 'div',\n { style: S.headActions },\n React.createElement('span', {\n style: S.updateEntrySlot,\n ref: (element: HTMLSpanElement | null) => {\n entryRef.current = element;\n },\n }),\n React.createElement(PanelActions, null),\n ),\n ),\n React.createElement(HealthSummaryLine, {\n lights,\n running: health.running,\n // #735:取数失败也要在这一行看得见(各家的错只画在那家页签里的表上,摘要行沉默=用户以为按钮坏了)。\n error: summaryErrorOf(health),\n onRun: health.run,\n }),\n React.createElement(\n 'div',\n { role: 'tablist', 'aria-label': '爱生活技能页签', style: { ...S.tablist, position: 'relative', isolation: 'isolate' }, 'data-ilife-tablist': 't6' },\n // T6(#1165):一枚页签样式+goo 滤镜+glider 滑块层;页签按钮本身仍按 slot 账本+MANAGER_TABS 映射(机制不动,只加表现)。\n React.createElement('style', { 'data-ilife-tabs': 't6' }, tabInteractionCss()),\n React.createElement(\n 'svg',\n { width: 0, height: 0, style: { position: 'absolute' }, 'aria-hidden': 'true' },\n React.createElement(\n 'defs',\n null,\n React.createElement(\n 'filter',\n { id: 'ilife-goo' },\n React.createElement('feGaussianBlur', { in: 'SourceGraphic', stdDeviation: 6, result: 'b' }),\n React.createElement('feColorMatrix', { in: 'b', values: '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 22 -11' }),\n ),\n ),\n ),\n React.createElement(\n 'span',\n { 'data-ilife-glider-layer': 't6', 'aria-hidden': 'true' },\n React.createElement('span', {\n 'data-ilife-ghost': 't6',\n ref: (element: HTMLSpanElement | null) => {\n ghostRef.current = element;\n },\n }),\n React.createElement('span', {\n 'data-ilife-glider': 't6',\n ref: (element: HTMLSpanElement | null) => {\n gliderRef.current = element;\n },\n }),\n ),\n MANAGER_TABS.map((tab, index) => {\n const selected = tab.plugin === active;\n const light = lightOf(tab.plugin);\n // 圆点从「实心/空心=装没装」换成「颜色=体检档位」(票 #732):装了就是实心、\n // 颜色按档位走;没装的保持缺席态、不许点(点它只会跳到一张缺席卡)。\n const dotColor = light === undefined\n ? (present.has(tab.plugin) ? TAB_DOT.unchecked : TAB_DOT.absent)\n : tabDotColor(light);\n const note = light === undefined ? null : tabNote(light);\n return React.createElement(\n 'button',\n {\n key: tab.plugin,\n ref: (element: HTMLButtonElement | null) => {\n tabRefs.current[index] = element;\n },\n id: tabsId + '-tab-' + tab.plugin,\n type: 'button',\n role: 'tab',\n 'data-ilife-tab': 'tab',\n 'aria-selected': selected,\n 'aria-controls': tabsId + '-panel-' + tab.plugin,\n tabIndex: selected ? 0 : -1,\n // T6(#1165):选中态自身透明、glider 在下层给 pill(原型 V1 同形);减少动态时 CSS 以 !important 回实心 pill。\n style: selected ? { ...S.tab, ...S.tabActive, background: 'transparent', borderColor: 'transparent' } : S.tab,\n onClick: () => {\n setActiveId(tab.plugin);\n },\n onKeyDown: (event: React.KeyboardEvent) => {\n onTabKeyDown(event, index);\n },\n title: light === undefined\n ? '这一家没装(或产物没注册页签槽)'\n : light.title + ':' + (light.hasChannel\n ? (light.status === null ? '还没体检' : '体检读数见本页签下方的表')\n : '没有配置体检出口'),\n },\n React.createElement('span', {\n 'data-ilife-health': 'tab-dot',\n style: {\n width: 6,\n height: 6,\n borderRadius: '50%',\n background: dotColor,\n display: 'inline-block',\n flex: '0 0 auto',\n },\n }),\n labelFor(tab),\n note === null\n ? null\n : React.createElement(\n 'span',\n {\n 'data-ilife-health': 'tab-note',\n // 红黄各自那一截带**自己**的档位色,不许一个色管两档(#706 复评逮过的缺陷)。\n style: { ...TAB_NOTE_STYLE, color: note.status === null ? undefined : STATUS_TEXT[note.status] },\n },\n note.text,\n ),\n );\n }),\n ),\n MANAGER_TABS.filter((tab) => tab.plugin === active || visitedIds.has(tab.plugin)).map((tab) => {\n const selected = tab.plugin === active;\n return React.createElement(\n 'div',\n {\n key: tab.plugin,\n id: tabsId + '-panel-' + tab.plugin,\n role: 'tabpanel',\n 'aria-labelledby': tabsId + '-tab-' + tab.plugin,\n // #933:这一格**不能**用 `hidden` 属性——UA 的 `[hidden]{display:none}` 会输给下面内联的\n // `display:flex`,六个预热页签会全露出来。显式写 display 才是唯一口径(未选中的整格不画)。\n style: selected ? undefined : { display: 'none' },\n },\n present.has(tab.plugin)\n ? React.createElement(\n // #933:配置卡与体检卡之间的空隙由**容器**给(原先两张卡边线直接相接、间距 0)。\n // 取 10=壳里块间距的既有口径(与 `S.head`/`S.tablist` 那几处同值)。\n 'div',\n { style: S.tabStack },\n props.renderSlot(CONFIG_TAB_SLOT, {}, { only: tab.plugin }) as React.ReactNode,\n // 那家的体检表:与总览那行读同一份快照(`health.rows`),不是各算一遍。\n // #1069:体检头只留去 `·` 的分层标题,**不带签**(签只留配置头;负责人 2026-10-03 定)。\n React.createElement(HealthTable, {\n title: labelFor(tab),\n phase: health.rows[tab.plugin]?.phase ?? 'idle',\n report: health.rows[tab.plugin]?.report ?? null,\n error: health.rows[tab.plugin]?.error ?? null,\n }),\n )\n : (() => {\n const reco = recoFor(tab);\n return React.createElement(\n 'div',\n { style: S.reco },\n reco.hint,\n React.createElement('span', { style: S.cmd }, reco.installCmd),\n );\n })(),\n );\n }),\n React.createElement(MorePluginsCard, null),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器:每次取数时现取(connection 后到也不永久缺席),透传给面板组件。\n const getCall = (): RpcCallFace | null => (ctx.connection?.rpc?.call as RpcCallFace | undefined) ?? null;\n // ledger 观测源(settings-plugins:1744-1767 同形;无 locale 面声明,故只订 ledger)。\n let tabsVersion = -1;\n let tabs: ConfigTabRow[] = [];\n const sectionInjected = () => ({\n hooks: {\n tabs: {\n getSnapshot: () => {\n const version = ctx.slots.getVersion(CONFIG_TAB_SLOT);\n if (version !== tabsVersion) {\n tabsVersion = version;\n tabs = ctx.slots\n .entries(CONFIG_TAB_SLOT)\n .map((entry) => ({\n id: entry.options.id ?? '',\n order: entry.options.order ?? 0,\n label: resolveLabel(entry.options.label),\n // #735 配置体检的通道:取自导航表(账本那一格读不到)。表里没有这家 ⇒ 空串 ⇒\n // 这家没有体检出口(灯显缺席态、按钮不会为它取数)。\n channel: CHANNEL_BY_PLUGIN.get(entry.options.id ?? '') ?? '',\n }))\n .sort((a, b) => a.order - b.order);\n }\n return tabs;\n },\n subscribe: (listener: () => void) => ctx.slots.subscribe(CONFIG_TAB_SLOT, listener),\n },\n },\n });\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register(\n {\n name: 'settings.section',\n id: 'dsh-life-pack',\n order: 21,\n label: () => '爱生活',\n inject: sectionInjected,\n children: { 'ilife.config-tab': { kind: 'list', scope: 'root' } },\n },\n (props: LifePackSectionProps) => React.createElement(LifePackSection, { ...props, getCall }),\n ),\n );\n}\n"],"x_google_ignoreList":[2,3,4,5,6,7,8,9,10,11,12,13],"mappings":"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,SAAS,aAAa;IAC/C,OAAO;KACL,QAAQ;MACN,MAAM;MACN,OAAO,OAAO,GAAG,KAAK,MAAM,UAAU,GAAG,SAAS,cAAc;MAChE,QAAQ,MAAM,UAAU;KAC1B;KACA,gBAAgB;KAChB,cAAc;KACd,eAAe;KACf,YAAY,SAAS,OAAO;KAC5B,WAAW;KACX;IACF;GACF;GACA,IAAI,SAAS,cAAc,SAAS,eAClC,OAAO;IACL,QAAQ;KACN,MAAM;KACN,OAAO,sBAAsB,SAAS,cAAc,kCAAkC,SAAS,eAAe;KAC9G,QAAQ;IACV;IACA,gBAAgB;IAChB,cAAc,gBAAgB,SAAS;IACvC,eAAe;IACf,YAAY,SAAS,OAAO;IAC5B,WAAW;IACX;GACF;GAEF,OAAO;IACL,QAAQ;KAAE,MAAM;KAAQ,OAAO;KAAgE,QAAQ;IAAG;IAC1G,gBAAgB;IAChB,cAAc;IACd,eAAe;IACf,YAAY;IACZ,WAAW;IACX;GACF;EACF;EACA,MAAM,mBAAmB;GACvB;GACA;GAGA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GAEA;GAEA;GAGA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;EACF,CAAC,CAAC,KAAK,IAAI;EACX,MAAM,sBAAsB;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAC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"}
|