dsh-life-pack 0.2.1 → 0.2.2
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 +12 -3
- package/dist/client.js.map +1 -1
- package/dist/nav.d.ts +1 -1
- package/dist/nav.js +1 -1
- package/dist/update-view.d.ts +6 -0
- package/dist/update-view.d.ts.map +1 -1
- package/dist/update-view.js +11 -0
- package/dist/update-view.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -357,6 +357,15 @@ window.__ModuleLoader__.load({
|
|
|
357
357
|
if (installed === running) return "⚠️ 新版 " + installed + " 已装好,重启宿主后生效。";
|
|
358
358
|
return "⚠️ 新版 " + installed + " 已装好,正在跑的还是 " + running + ",重启宿主后生效。";
|
|
359
359
|
}
|
|
360
|
+
/** 面板展示的那条可复制命令(用户会照着敲的那一条)。
|
|
361
|
+
*
|
|
362
|
+
* 缺席包给人人都会用的**单命令双包**口径(#674 冻结的装入口径:不带版本号、不带任何开关);
|
|
363
|
+
* 已装包给更新包回包里那条(`--save-exact ... --registry=` 是它 README 第 9 节的冻结合同,
|
|
364
|
+
* 本仓不动它——要改只能提上游)。规范词见 docs/plugins/plugin-manager/t678-缺席即装与检查更新.md。 */
|
|
365
|
+
function manualForDisplay(target, manual) {
|
|
366
|
+
if (isAbsent(target)) return dualInstallCmd(target.packageName);
|
|
367
|
+
return manual;
|
|
368
|
+
}
|
|
360
369
|
/** 版本行两行文字:插件包(本家) + 技能包(随插件,不单独查更新)。 */
|
|
361
370
|
function versionLines(target, snapshot) {
|
|
362
371
|
const running = snapshot?.runningVersion ?? target.runningVersion ?? "unknown";
|
|
@@ -651,7 +660,7 @@ window.__ModuleLoader__.load({
|
|
|
651
660
|
const row = rows[target.key];
|
|
652
661
|
const snapshot = row.outcome?.snapshot ?? null;
|
|
653
662
|
const verdict = snapshot ? verdictOf(target, snapshot) : null;
|
|
654
|
-
const manual = row.failure?.manual ?? row.outcome?.manual ?? null;
|
|
663
|
+
const manual = manualForDisplay(target, row.failure?.manual ?? row.outcome?.manual ?? null);
|
|
655
664
|
const busy = row.phase === "installing";
|
|
656
665
|
return react.createElement("div", {
|
|
657
666
|
key: target.key,
|
|
@@ -678,7 +687,7 @@ window.__ModuleLoader__.load({
|
|
|
678
687
|
const target = props.target;
|
|
679
688
|
const row = target ? props.face.rows[target.key] : null;
|
|
680
689
|
const busy = row?.phase === "installing";
|
|
681
|
-
const manual = row?.failure?.manual ?? row?.outcome?.manual ?? props.fallbackCommand;
|
|
690
|
+
const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;
|
|
682
691
|
let reason = row?.failure?.message ?? null;
|
|
683
692
|
if (target && reason === null && row?.outcome && row.outcome.snapshot.blockedReason !== null) reason = verdictOf(target, row.outcome.snapshot).text;
|
|
684
693
|
return react.createElement("div", { style: PANEL_STYLE.reco }, react.createElement("div", null, target ? "未安装[" + target.packageName + "],装上后这个页签就能用了。" : "这个页签对应的插件还没装。"), target ? react.createElement("button", {
|
|
@@ -940,7 +949,7 @@ window.__ModuleLoader__.load({
|
|
|
940
949
|
setActiveId(next.plugin);
|
|
941
950
|
tabRefs.current[nextIndex]?.focus();
|
|
942
951
|
}
|
|
943
|
-
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("div", { style: S.headActions }, react.createElement(CheckUpdateButton, { face }), react.createElement(PanelActions, null))), react.createElement("div", { style: S.meta }, react.createElement("div", null, "总开关 · 开关(缺省启用,只读)"), react.createElement("div", null, "总管 dsh-life-pack · 0.2.
|
|
952
|
+
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("div", { style: S.headActions }, react.createElement(CheckUpdateButton, { face }), react.createElement(PanelActions, null))), react.createElement("div", { style: S.meta }, react.createElement("div", null, "总开关 · 开关(缺省启用,只读)"), react.createElement("div", null, "总管 dsh-life-pack · 0.2.2")), react.createElement(UpdateResults, { face }), react.createElement("div", {
|
|
944
953
|
role: "tablist",
|
|
945
954
|
"aria-label": "爱生活技能页签",
|
|
946
955
|
style: S.tablist
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["React","React"],"sources":["../src/nav.ts","../src/update-contract.ts","../src/update-client.ts","../src/update-view.ts","../src/update-panel.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 readonly title: string;\n /** 提供该 tab 的单品插件包名(仅文档级引用,不 import)。 */\n readonly plugin: 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 同步,release 时一起 bump;设置页总设置区版本行唯一来源)。 */\nexport const MANAGER_VERSION = '0.2.1' as const;\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: 'memo', slotId: 'ilife:memo', order: 70, title: '备忘录', plugin: 'dsh-memo-ilife' },\n { skill: 'calorie', slotId: 'ilife:calorie', order: 75, title: '卡路里', plugin: 'dsh-calorie' },\n { skill: 'schedule', slotId: 'ilife:schedule', order: 80, title: '作息', plugin: 'dsh-schedule-ilife' },\n { skill: 'home', slotId: 'ilife:home', order: 85, title: '居家', plugin: 'dsh-home-ilife' },\n { skill: 'chef', slotId: 'ilife:chef', order: 90, title: '大厨', plugin: 'dsh-chef' },\n { skill: 'bill', slotId: 'ilife:cookie', order: 95, title: '记账', plugin: 'dsh-bill-ilife' },\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: '去 GitHub 点 Star', 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 * 分工:本文件只管「两侧都认的常量与文案」;宿主半见 `update-host.ts`,面板见 `client.ts`。\n * 电话名分两类:\n * - 七个**更新电话组**(每组三个 `updateStatus` / `updateCheck` / `updateInstall`)由更新包\n * 自己拼(`update.phoneNames`),面板侧取值见构建期派生的 `update-phones.ts`;\n * - 两个**总管自有电话**(装上缺席包、取版本行数据)是本包定义的,见 `MANAGER_ACTIONS`。\n *\n * 原因码的人话文案逐条抄更新包 README 第 8 节「用户该做什么」列与第 12 节排错表;\n * 手工命令形状抄第 9 节(`commands.ts:104-122` 的 `manualCommand`)。\n */\nimport type { BlockedReason } from 'dsh-plugin-update';\n\n/** 载体:宿主注册 `connection.fetch.register` 的路径 / 面板 `connection.rpc.call` 的两段参数。 */\nexport const MANAGER_RPC = {\n /** 单段通道名,须 match `/^\\/[A-Za-z0-9._~-]+$/`(cookbook §6)。 */\n channel: '/ilife-manager',\n /** 面板侧第二段参数(等于注册路径去掉 `/api` 前缀后那一段)。 */\n endpoint: 'ilife-manager',\n /** 宿主侧注册的完整路径(`/api` 是 DSH 公开载体)。 */\n path: '/api/ilife-manager',\n} as const;\n\n/** 总管自有电话名:更新包的三个动作名是冻结集合(`config.ts:14`),这里另起名字不撞车。 */\nexport const MANAGER_ACTIONS = {\n /** 装上缺席的目标包:入参 `{packageName, version}`,回包 `{packageName, version}`。 */\n install: 'ilife-manager.install',\n /** 七个更新目标的表:入参 `{}`,回包 `{targets: [{key, title, packageName, phones, runningVersion, installedVersion, skill}]}`。\n *\n * 电话名为什么由宿主转交而不是面板侧写死:更新包的三个电话名只有它自己知道\n * (`update.phoneNames`,`host.ts:335`);面板侧既不能 import 更新包(浏览器产物纯度门)\n * 也不该把名字再写一份(写两份必然走散)。宿主把名字表连同版本行一起交出去,\n * 面板一行字面量都不留。 */\n targets: 'ilife-manager.targets',\n} as const;\n\n/** 更新包默认官方源(`config.ts:20`,总管侧拼手工命令时用同一个值)。 */\nexport const DEFAULT_REGISTRY = 'https://registry.npmjs.org/' as const;\n\n/** 装不了 / 装不成的人话原因表:八种 `blockedReason` + 过程错误码(更新包 README 第 8、12 节)。 */\nconst REASON_TEXT: Record<string, string> = {\n 'unknown-profile': '使用范围认不出(名字非法或目录不存在):先检查使用范围名与目录,这种情形不给手工命令。',\n 'source-install': '当前是从源码装的,不是按版本号装的:想走更新,先按版本号重装一次。',\n 'invalid-installation': '已装的包不完整(名字对不上、版本非法、入口文件缺失):重装当前版本,修好已装目录再查。',\n 'installation-changed': '安装位置在使用中途变了:重新打开宿主再查一次;还出现就重装。',\n 'pending-restart': '新版已装到磁盘,正在跑的还是旧版:重启宿主后生效。',\n 'registry-conflict': '本地声明的版本与磁盘实际版本互相矛盾:打开使用范围的清单,把目标包名那行改成版本号再试。',\n 'incompatible-node': '新版要求的 Node 与当前运行的 Node 对不上:先把 Node 升到 22 或更高,再查。',\n 'recovery-required': '上次安装被打断,留下一个半截任务:重新点一次安装;一直出现就查宿主日志。',\n 'check-failed': '查新版失败(联网不通或官方源不可达):检查网络与官方源地址,再查一次。',\n 'invalid-release': '远端发行信息不合规(名字对不上、版本非法、包地址或完整性校验不过):换个版本再查。',\n 'check-expired': '上次查新版的凭证过期了:重新查一次再点安装(不要重试旧编号)。',\n 'update-busy': '同一个使用范围同时只装一个:等当前任务结束再点。',\n 'install-failed': '安装没跑成:按下面那条手工命令在终端执行一次。',\n 'bad-request': '回执异常(请求形状不对):把宿主日志里的事件名报给作者。',\n internal: '回执异常(总管内部错误):把宿主日志里的事件名报给作者。',\n};\n\n/** 原因码 → 人话(认不出的码不猜、原样回码并说明这是未知原因)。 */\nexport function reasonText(code: string): string {\n return REASON_TEXT[code] ?? '未知原因(' + code + '):把宿主日志里的事件名报给作者。';\n}\n\n/** 手工兜底命令(更新包 README 第 9 节形状):使用范围名含特殊字符时加引号。 */\nexport function manualInstallCommand(input: {\n profileName: string | null;\n packageName: string;\n version: string;\n registryUrl?: string;\n}): string | null {\n const name = typeof input.profileName === 'string' ? input.profileName.trim() : '';\n if (!name || name.length > 255 || name.startsWith('-') || ['.', '..', 'node_modules'].includes(name)) return null;\n const version = typeof input.version === 'string' && /^\\d+\\.\\d+\\.\\d+$/.test(input.version) ? input.version : '';\n if (!version) return null;\n const registry = input.registryUrl ?? DEFAULT_REGISTRY;\n const profileArg = /^[A-Za-z0-9_.-]+$/.test(name) ? name : JSON.stringify(name);\n return 'dsh plugin --profile ' + profileArg + ' add --save-exact ' + input.packageName + '@' + version + ' --registry=' + registry;\n}\n\n/** 八种装不了原因码(给门禁与测试对表用;值就是 `BlockedReason` 全集)。 */\nexport const BLOCKED_REASONS: readonly BlockedReason[] = [\n 'unknown-profile',\n 'source-install',\n 'invalid-installation',\n 'installation-changed',\n 'pending-restart',\n 'registry-conflict',\n 'incompatible-node',\n 'recovery-required',\n];\n","/** 面板到宿主的三条流程(纯异步函数:无 DOM、无 react;传输口由调用方注入,便于单测)。\n *\n * 三条流程与更新包 README 的对应关系:\n * - 检查更新:`updateCheck`(第 3 节第 3 步的 `host.call(UPD_CHECK, {})`);\n * - 装上更新:`updateCheck` 拿凭证 → `updateInstall` 提交 → **按间隔轮询 `updateStatus`**\n * 直到任务离开 `installing`/`verifying`(第 3 节第 3 步与第 9 节:安装是后台跑,\n * 调用方不干等;凭证过期 `check-expired` 是正常错误码,重查一次再提交,见第 11 节);\n * - 装上缺席的包:走总管自有的 `ilife-manager.install`(更新包的三个电话只管已装的包)。\n */\nimport { MANAGER_ACTIONS, MANAGER_RPC, reasonText } from './update-contract.js';\nimport type { CheckOutcome, TargetInfo } from './update-view.js';\n\n/** 传输口(DSH 载体 `connection.rpc.call` 的形状,cookbook §6)。 */\nexport type CallFace = (\n channel: string,\n endpoint: string,\n payload: unknown,\n signal?: AbortSignal,\n) => Promise<\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: { readonly code: string; readonly message: string; readonly details: Record<string, unknown> } }\n>;\n\nexport interface CallFailure {\n readonly ok: false;\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n /** 能给的话给一条可复制的手工命令(更新包每次回包都带,见其 README 第 9 节)。 */\n readonly manual: string | null;\n}\n\nexport type CallOutcome<T> = { readonly ok: true; readonly value: T } | CallFailure;\n\n/** 轮询上限:1 秒一次、最多 900 次(覆盖更新包 15 分钟的安装时限)。 */\nconst MAX_POLL_TRIES = 900;\n/** 轮询默认间隔(只有在宿主没给 `pollMs` 时才用;正常取值由宿主转交更新包的 panelPollMs)。 */\nconst FALLBACK_POLL_MS = 1000;\nlet requestSeq = 0;\n\nfunction failure(code: string, details: Record<string, unknown> = {}, manual: string | null = null): CallFailure {\n return { ok: false, code, message: reasonText(code), details, manual };\n}\n\n/** 取数间隔:settle 即清 timer。定时器有界重试的例外依据(p10 静态门三件套):\n * 这里有清理(clearTimeout)、有次数上限(MAX_POLL_TRIES)、间隔取自宿主转交的更新包\n * `panelPollMs`(不写死);用途是更新包 README 第 3 节要求的状态轮询(安装是后台跑,\n * 面板每秒问一次进度),不是无界重试。 */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n clearTimeout(timer);\n resolve();\n }, ms);\n });\n}\n\nasync function callManager<T>(call: CallFace | null, method: string, payload: Record<string, unknown>): Promise<CallOutcome<T>> {\n if (typeof call !== 'function') return failure('internal', { detail: '宿主连接缺席:connection.rpc.call 不可用' });\n let result: Awaited<ReturnType<CallFace>>;\n try {\n result = await call(MANAGER_RPC.channel, MANAGER_RPC.endpoint, { method, payload });\n } catch (error) {\n return failure('check-failed', { detail: String((error as Error)?.message ?? error) });\n }\n if (!result || typeof result !== 'object' || typeof (result as { ok?: unknown }).ok !== 'boolean') {\n return failure('internal', { detail: '回执信封异常(非 ok 信封)' });\n }\n if (result.ok) return { ok: true, value: result.value as T };\n const error = (result as { error?: { code?: unknown; details?: unknown } }).error;\n const code = typeof error?.code === 'string' ? error.code : 'internal';\n const details = error && typeof error.details === 'object' && error.details !== null ? (error.details as Record<string, unknown>) : {};\n return failure(code, details);\n}\n\n/** 取七个更新目标的表(宿主转交更新包的三个电话名与版本行,面板不写死任何电话名)。 */\nexport async function loadTargets(call: CallFace | null): Promise<CallOutcome<{ targets: TargetInfo[]; pollMs: number }>> {\n const out = await callManager<{ targets?: TargetInfo[]; pollMs?: number }>(call, MANAGER_ACTIONS.targets, {});\n if (!out.ok) return out;\n const targets = Array.isArray(out.value?.targets) ? out.value.targets : [];\n const pollMs = typeof out.value?.pollMs === 'number' && out.value.pollMs > 0 ? out.value.pollMs : FALLBACK_POLL_MS;\n return { ok: true, value: { targets, pollMs } };\n}\n\n/** 查一家:默认查新版(`updateCheck`,顺带回最新版本与凭证);给 `phoneName` 时查那个电话。 */\nexport async function checkTarget(\n call: CallFace | null,\n target: TargetInfo,\n phoneName?: string,\n): Promise<CallOutcome<CheckOutcome>> {\n const name = phoneName ?? target.phones?.check;\n if (!target.phones || !name) return failure('bad-request', { target: target.key });\n return callManager<CheckOutcome>(call, name, {});\n}\n\n/** 装上缺席的包:版本取自查新版拿到的「最新版本」(宿主用更新包的执行器真装)。 */\nexport async function installAbsent(\n call: CallFace | null,\n target: TargetInfo,\n version: string,\n): Promise<CallOutcome<{ packageName: string; version: string }>> {\n return callManager(call, MANAGER_ACTIONS.install, { packageName: target.packageName, version });\n}\n\n/** 装上更新:查新版 → 提交安装 → 有界轮询查状态到任务收尾;凭证过期重查一次再提交。 */\nexport async function updateInstalled(\n call: CallFace | null,\n target: TargetInfo,\n pollMs: number,\n): Promise<CallOutcome<CheckOutcome>> {\n if (!target.phones) return failure('bad-request', { target: target.key });\n const first = await checkTarget(call, target);\n if (!first.ok) return first;\n let checked = first.value;\n if (!checked.receipt) {\n const code = checked.snapshot.blockedReason ?? 'check-failed';\n return failure(code, { target: target.key }, checked.manual);\n }\n requestSeq += 1;\n const requestId = 'ilife-manager-' + Date.now().toString(36) + '-' + String(requestSeq);\n let submitted = await callManager<CheckOutcome>(call, target.phones.install, {\n checkId: checked.receipt.checkId,\n requestId,\n });\n if (!submitted.ok && submitted.code === 'check-expired') {\n const again = await checkTarget(call, target);\n if (!again.ok) return again;\n checked = again.value;\n if (!checked.receipt) {\n return failure(checked.snapshot.blockedReason ?? 'check-failed', { target: target.key }, checked.manual);\n }\n submitted = await callManager<CheckOutcome>(call, target.phones.install, {\n checkId: checked.receipt.checkId,\n requestId: requestId + 'r',\n });\n }\n if (!submitted.ok) return { ...submitted, manual: submitted.manual ?? checked.manual };\n // 安装是后台跑(`service.ts:434-436`:落盘后后台跑,调用方不干等),故按间隔轮询到任务收尾。\n let latest = submitted.value;\n const interval = pollMs > 0 ? pollMs : FALLBACK_POLL_MS;\n for (let tries = 0; ; tries += 1) {\n const job = latest.snapshot.job;\n if (job === null || (job.state !== 'installing' && job.state !== 'verifying')) break;\n if (tries >= MAX_POLL_TRIES) break;\n await sleep(interval);\n const status = await checkTarget(call, target, target.phones.status);\n if (!status.ok) return status;\n latest = status.value;\n }\n return { ok: true, value: latest };\n}\n","/** 面板的视图逻辑(纯函数:无 DOM、无 node、无 react),客户端与单测共用同一份。\n *\n * 判定「有没有新版」不自己算版本号大小:更新核心已经在快照里给了结论\n * (`canInstall` 为真即有新版可装,`blockedReason` 非空即为什么装不了,见更新包 README 第 8 节),\n * 本文件只把结论翻成三种状态与一句话,不另起一套版本比较。\n */\nimport type { CheckReceipt, BlockedReason, UpdateSnapshot } from 'dsh-plugin-update';\nimport { reasonText } from './update-contract.js';\n\n/** 一个更新目标的面板数据(宿主 `ilife-manager.targets` 的每行)。 */\nexport interface TargetInfo {\n readonly key: string;\n readonly title: string;\n readonly packageName: string;\n readonly phones: { readonly status: string; readonly check: string; readonly install: string } | null;\n /** 宿主启动那一刻磁盘上的版本;null = 启动时这个包还没装。 */\n readonly runningVersion: string | null;\n readonly installedVersion: string | null;\n /** 技能包随插件的那条精确 pin(插件包 manifest 里的 `skill-*` 依赖)。 */\n readonly skill: { readonly packageName: string; readonly version: string } | null;\n}\n\n/** 更新电话查到的结果(`updateCheck` / `updateStatus` 回包体)。 */\nexport interface CheckOutcome {\n readonly snapshot: UpdateSnapshot;\n readonly manual: string | null;\n readonly receipt: CheckReceipt | null;\n}\n\n/** 缺席:宿主启动时没装、现在磁盘上也没有(`runningVersion`/`installedVersion` 都是 null)。 */\nexport function isAbsent(target: TargetInfo): boolean {\n return target.runningVersion === null && target.installedVersion === null;\n}\n\nexport type VerdictKind = 'unknown' | 'up-to-date' | 'update-available' | 'blocked';\n\nexport interface Verdict {\n readonly kind: VerdictKind;\n /** 一句话结论(面板那行正文)。 */\n readonly text: string;\n /** 这一行给不给按钮:缺席给「装上」,有新版给「装上更新」,其余不给。 */\n readonly action: 'install' | 'update' | null;\n}\n\n/** 把快照翻成一行结论:还没查 / 已是最新 / 有新版 / 装不了(附人话原因)。 */\nexport function verdictOf(target: TargetInfo, snapshot: UpdateSnapshot): Verdict {\n const absent = isAbsent(target);\n const latest = snapshot.latestVersion;\n if (absent && latest === null) {\n return { kind: 'unknown', text: '未安装;点「检查更新」查最新版本后装上。', action: null };\n }\n if (absent) {\n return { kind: 'update-available', text: '未安装;最新版本 ' + String(latest) + '。', action: 'install' };\n }\n if (snapshot.blockedReason !== null) {\n const blocked = snapshot.blockedReason as BlockedReason;\n if (blocked === 'pending-restart') {\n return { kind: 'blocked', text: reasonText(blocked), action: null };\n }\n return { kind: 'blocked', text: reasonText(blocked), action: null };\n }\n if (latest === null) {\n return { kind: 'unknown', text: '还没查过最新版本;点「检查更新」。', action: null };\n }\n if (snapshot.canInstall) {\n return { kind: 'update-available', text: '有新版:' + String(latest) + ',点「装上更新」。', action: 'update' };\n }\n return { kind: 'up-to-date', text: '已是最新(' + String(latest) + ')。', action: null };\n}\n\n/** 待重启横幅文案(更新包 README 第 10 节:说清新版号与「重启后才生效」两件事)。 */\nexport function pendingRestartText(target: TargetInfo, snapshot: UpdateSnapshot): string | null {\n if (snapshot.blockedReason !== 'pending-restart') return null;\n const installed = snapshot.installedVersion ?? snapshot.job?.targetVersion ?? null;\n const running = snapshot.runningVersion;\n if (installed === null) return '⚠️ 新版已装好,重启宿主后生效。';\n if (installed === running) return '⚠️ 新版 ' + installed + ' 已装好,重启宿主后生效。';\n return '⚠️ 新版 ' + installed + ' 已装好,正在跑的还是 ' + running + ',重启宿主后生效。';\n}\n\n/** 版本行两行文字:插件包(本家) + 技能包(随插件,不单独查更新)。 */\nexport function versionLines(target: TargetInfo, snapshot: UpdateSnapshot | null): readonly string[] {\n const running = snapshot?.runningVersion ?? target.runningVersion ?? 'unknown';\n const installed = snapshot?.installedVersion ?? target.installedVersion;\n const lines = [target.packageName + ' ' + running + (installed && installed !== running ? '(磁盘已装 ' + installed + ')' : '')];\n if (target.skill) lines.push(target.skill.packageName + ' ' + target.skill.version + '(随插件,不单独查更新)');\n else lines.push('技能包随插件(不单独查更新)');\n return lines;\n}\n","/** 爱生活面板的更新界面:检查更新按钮、七家结果行、待重启横幅、缺席卡的「装上」。\n *\n * 面板侧纪律(沿 `client.ts` 既有口径):React.createElement 手写(禁 JSX)、内联 style、\n * 颜色走 DSH 主题别名(深浅主题自适应,写死值只做回退)、只用 `dsh-ctx` 镜像内的上下文成员、\n * 不 import 更新包(电话名与轮询间隔由宿主转交,见 `update-contract.ts` 的 `MANAGER_ACTIONS.targets`)。\n *\n * 视觉对照(用户给的第二张截图,参照实现 `dsh-mattpocock-skills-deck/src/client/views/SettingsPage.js:152-168`):\n * 标题右侧并排若干件——本文件出「检查更新」一件,隔壁票(爱生活面板头部与底部)在同一个\n * `actions` 容器里补星与气泡两件,故这里把容器单独留出来。\n */\nimport * as React from 'react';\nimport { checkTarget, installAbsent, loadTargets, updateInstalled } from './update-client.js';\nimport type { CallFace, CallFailure } from './update-client.js';\nimport { isAbsent, pendingRestartText, verdictOf, versionLines } from './update-view.js';\nimport type { CheckOutcome, TargetInfo } from './update-view.js';\n\n/** 面板视觉(沿用总管既有语言:内联 style,主题别名带回退)。 */\nexport const PANEL_STYLE = {\n head: { fontSize: 15, fontWeight: 700, margin: '2px 0 8px', color: 'var(--dsw-alias-label-primary, inherit)' } as React.CSSProperties,\n headRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexWrap: 'wrap' } as React.CSSProperties,\n actions: { display: 'inline-flex', alignItems: 'center', gap: 6 } as React.CSSProperties,\n meta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12, lineHeight: 1.7, marginBottom: 10 } 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, 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 color: '#fff',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border, 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 btn: {\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.35))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n borderRadius: 8,\n padding: '4px 12px',\n fontSize: 13,\n cursor: 'pointer',\n } as React.CSSProperties,\n banner: {\n padding: '10px 12px',\n marginBottom: 10,\n borderRadius: 8,\n border: '1px solid var(--dsw-alias-state-warning-primary, #d8a300)',\n background: 'var(--dsw-alias-bg-base, rgba(216,163,0,.12))',\n color: 'var(--dsw-alias-label-primary, inherit)',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n row: {\n padding: '10px 12px',\n marginBottom: 8,\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.3))',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n rowHead: { fontWeight: 700, marginBottom: 2 } as React.CSSProperties,\n rowMeta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12 } as React.CSSProperties,\n reason: { color: 'var(--dsw-alias-state-error-primary, #ff6b6b)', fontSize: 12, marginTop: 4 } as React.CSSProperties,\n skill: { color: 'var(--dsw-alias-label-tertiary, #8a8a8a)', fontSize: 12, marginTop: 4 } as React.CSSProperties,\n};\n\n/** 一行的运行时状态:还没查 / 查着 / 有结果 / 装着 / 出错。 */\nexport interface UpdateRowState {\n readonly phase: 'idle' | 'checking' | 'ready' | 'installing' | 'failed';\n readonly outcome: CheckOutcome | null;\n readonly failure: CallFailure | null;\n}\n\nexport interface UpdateRowsFace {\n readonly targets: readonly TargetInfo[];\n readonly rows: Readonly<Record<string, UpdateRowState>>;\n readonly pollMs: number;\n readonly loadError: string | null;\n readonly checking: boolean;\n checkAll(): void;\n act(target: TargetInfo): void;\n}\n\nconst IDLE: UpdateRowState = { phase: 'idle', outcome: null, failure: null };\n\n/** 面板的更新状态源:挂载时取七个目标的表,之后按需查/装(组件与按钮共用一个 face)。 */\nexport function useUpdateRows(getCall: () => CallFace | null): UpdateRowsFace {\n const [targets, setTargets] = React.useState<readonly TargetInfo[]>([]);\n const [rows, setRows] = React.useState<Record<string, UpdateRowState>>({});\n const [pollMs, setPollMs] = React.useState(1000);\n const [loadError, setLoadError] = React.useState<string | null>(null);\n const [checking, setChecking] = React.useState(false);\n React.useEffect(() => {\n let alive = true;\n void (async () => {\n const loaded = await loadTargets(getCall());\n if (!alive) return;\n if (loaded.ok) {\n setTargets(loaded.value.targets);\n setPollMs(loaded.value.pollMs);\n } else {\n setLoadError(loaded.message);\n }\n })();\n return () => {\n alive = false;\n };\n }, [getCall]);\n const patch = React.useCallback((key: string, next: UpdateRowState) => {\n setRows((previous) => ({ ...previous, [key]: next }));\n }, []);\n const checkAll = React.useCallback(() => {\n setChecking(true);\n const list = targets;\n for (const target of list) patch(target.key, { phase: 'checking', outcome: rows[target.key]?.outcome ?? null, failure: null });\n void Promise.all(\n list.map(async (target) => {\n const result = await checkTarget(getCall(), target);\n patch(target.key, result.ok ? { phase: 'ready', outcome: result.value, failure: null } : { phase: 'failed', outcome: null, failure: result });\n }),\n ).finally(() => setChecking(false));\n }, [getCall, patch, rows, targets]);\n const act = React.useCallback(\n (target: TargetInfo) => {\n const current = rows[target.key] ?? IDLE;\n patch(target.key, { ...current, phase: 'installing', failure: null });\n void (async () => {\n const absent = isAbsent(target);\n let version = current.outcome?.snapshot.latestVersion ?? null;\n if (absent && version === null) {\n const checked = await checkTarget(getCall(), target);\n if (!checked.ok) {\n patch(target.key, { phase: 'failed', outcome: null, failure: checked });\n return;\n }\n patch(target.key, { phase: 'installing', outcome: checked.value, failure: null });\n version = checked.value.snapshot.latestVersion;\n }\n const result = absent\n ? await installAbsent(getCall(), target, version ?? '')\n : await updateInstalled(getCall(), target, pollMs);\n if (result.ok) {\n // 装完不自己编快照:向宿主问一次真实状态(装到磁盘但宿主还跑着旧版 ⇒ 待重启)。\n const refreshed = await checkTarget(getCall(), target, target.phones?.status);\n patch(target.key, refreshed.ok ? { phase: 'ready', outcome: refreshed.value, failure: null } : { phase: 'failed', outcome: null, failure: refreshed });\n return;\n }\n patch(target.key, {\n phase: 'failed',\n outcome: current.outcome,\n failure: { ...result, manual: result.manual ?? current.outcome?.manual ?? null },\n });\n })();\n },\n [getCall, patch, pollMs, rows],\n );\n return { targets, rows, pollMs, loadError, checking, checkAll, act };\n}\n\n/** 标题右侧的「检查更新」:一个按钮一次查七家(吃 `useUpdateRows` 的表)。 */\nexport function CheckUpdateButton(props: { readonly face: UpdateRowsFace }): React.ReactElement {\n const { checking, checkAll, targets } = props.face;\n const disabled = checking || targets.length === 0;\n return React.createElement(\n 'button',\n {\n type: 'button',\n style: disabled ? { ...PANEL_STYLE.btn, opacity: 0.55, cursor: 'default' } : PANEL_STYLE.btn,\n disabled,\n onClick: checkAll,\n title: '一次查七家(总管自己 + 六个单品)的插件包版本',\n },\n checking ? '检查中…' : '检查更新',\n );\n}\n\n/** 待重启横幅:任何一家成 `pending-restart` 就常驻显示,重启宿主后自动消失(更新包 README 第 10 节)。 */\nfunction RestartBanners(props: { readonly face: UpdateRowsFace }): React.ReactElement | null {\n const lines: string[] = [];\n for (const target of props.face.targets) {\n const outcome = props.face.rows[target.key]?.outcome;\n if (!outcome) continue;\n const text = pendingRestartText(target, outcome.snapshot);\n if (text) lines.push(text);\n }\n if (lines.length === 0) return null;\n return React.createElement(\n 'div',\n { style: PANEL_STYLE.banner },\n lines.map((line, index) => React.createElement('div', { key: String(index) }, line)),\n );\n}\n\n/** 七家结果行:一家一行(版本行两行、结论一句、按钮一个、装不了给原因与手工命令)。 */\nexport function UpdateResults(props: { readonly face: UpdateRowsFace }): React.ReactElement | null {\n const { targets, rows, loadError, checking } = props.face;\n const shown = targets.filter((target) => rows[target.key] && rows[target.key].phase !== 'idle');\n if (loadError) return React.createElement('div', { style: PANEL_STYLE.reason }, '更新能力没接上:' + loadError);\n if (targets.length === 0) return null;\n if (shown.length === 0) return null;\n return React.createElement(\n 'div',\n null,\n React.createElement(RestartBanners, { face: props.face }),\n shown.map((target) => {\n const row = rows[target.key];\n const snapshot = row.outcome?.snapshot ?? null;\n const verdict = snapshot ? verdictOf(target, snapshot) : null;\n const manual = row.failure?.manual ?? row.outcome?.manual ?? null;\n const busy = row.phase === 'installing';\n return React.createElement(\n 'div',\n { key: target.key, style: PANEL_STYLE.row },\n React.createElement('div', { style: PANEL_STYLE.rowHead }, target.title + ' · ' + target.packageName),\n React.createElement(\n 'div',\n { style: PANEL_STYLE.rowMeta },\n '当前 ' +\n String(snapshot?.runningVersion ?? target.runningVersion ?? '未安装') +\n ' · 最新 ' +\n String(snapshot?.latestVersion ?? '未查'),\n ),\n React.createElement('div', null, busy ? '正在装,请稍候…' : (verdict?.text ?? (checking ? '检查中…' : ''))),\n verdict?.action\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: busy ? { ...PANEL_STYLE.btn, opacity: 0.55 } : { ...PANEL_STYLE.btn, marginTop: 6 },\n disabled: busy,\n onClick: () => props.face.act(target),\n },\n verdict.action === 'install' ? '装上' : '装上更新',\n )\n : null,\n row.failure ? React.createElement('div', { style: PANEL_STYLE.reason }, '装不上:' + row.failure.message) : null,\n versionLines(target, snapshot).map((line, index) =>\n React.createElement('div', { key: String(index), style: PANEL_STYLE.skill }, line),\n ),\n manual ? React.createElement('code', { style: PANEL_STYLE.cmd }, manual) : null,\n );\n }),\n );\n}\n\n/** 缺席卡:一句人话 +「装上」按钮(复制命令降为辅助展示);总管缺席时不显示此卡(面板也看不到)。 */\nexport function AbsentCard(props: {\n readonly target: TargetInfo | null;\n readonly fallbackCommand: string;\n readonly face: UpdateRowsFace;\n}): React.ReactElement {\n const target = props.target;\n const row = target ? props.face.rows[target.key] : null;\n const busy = row?.phase === 'installing';\n const manual = row?.failure?.manual ?? row?.outcome?.manual ?? props.fallbackCommand;\n let reason: string | null = row?.failure?.message ?? null;\n if (target && reason === null && row?.outcome && row.outcome.snapshot.blockedReason !== null) {\n reason = verdictOf(target, row.outcome.snapshot).text;\n }\n return React.createElement(\n 'div',\n { style: PANEL_STYLE.reco },\n React.createElement('div', null, target ? '未安装[' + target.packageName + '],装上后这个页签就能用了。' : '这个页签对应的插件还没装。'),\n target\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: busy ? { ...PANEL_STYLE.btn, marginTop: 8, opacity: 0.55 } : { ...PANEL_STYLE.btn, marginTop: 8 },\n disabled: busy,\n onClick: () => props.face.act(target),\n },\n busy ? '正在装,请稍候…' : '装上',\n )\n : null,\n reason ? React.createElement('div', { style: PANEL_STYLE.reason }, reason) : null,\n React.createElement('code', { style: PANEL_STYLE.cmd }, manual),\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_TABS, MANAGER_VERSION, MORE_PLUGINS, PANEL_LINKS, recoFor } from './nav.js';\nimport type { ManagerTab } from './nav.js';\nimport { AbsentCard, CheckUpdateButton, UpdateResults, useUpdateRows } from './update-panel.js';\nimport type { CallFace } from './update-client.js';\nimport type {\n ClientCtx,\n ConfigTabRow,\n LifePackSectionProps,\n SlotLedgerEntry,\n} from './dsh-ctx.js';\n\nexport const inject = ['slots', 'connection'];\n\n/** 爱生活页签槽(总管声明的 children,技能设置页注册进来;单段名,避开官方 settings.* 前缀)。 */\nexport const CONFIG_TAB_SLOT = 'ilife.config-tab' as const;\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 meta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12, lineHeight: 1.7, marginBottom: 10 } 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, 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 color: '#fff',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border, 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, 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};\n\n/** 缺席卡的画法与流程住 `update-panel.ts`(票 #678:一句人话 +「装上」按钮 + 可复制命令),\n * `recoFor` 的补装命令仍是卡里那条辅助展示(B11 口径不变)。 */\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/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */\nfunction LifePackSection(props: LifePackSectionProps & { getCall: () => CallFace | 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 const face = useUpdateRows(props.getCall);\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined);\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 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 function targetForTab(tab: ManagerTab) {\n return face.targets.find((t) => t.packageName === tab.plugin) ?? null;\n }\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 return React.createElement(\n 'div',\n null,\n React.createElement(\n 'div',\n { style: S.headRow },\n React.createElement('div', { style: S.head }, '爱生活'),\n // 三件并排:本票的「检查更新」在左,隔壁票(#679)的星与气泡在右,整组靠右(窄窗口折行)。\n React.createElement(\n 'div',\n { style: S.headActions },\n React.createElement(CheckUpdateButton, { face }),\n React.createElement(PanelActions, null),\n ),\n ),\n React.createElement(\n 'div',\n { style: S.meta },\n React.createElement('div', null, '总开关 · 开关(缺省启用,只读)'),\n React.createElement('div', null, '总管 dsh-life-pack · ' + MANAGER_VERSION),\n ),\n React.createElement(UpdateResults, { face }),\n React.createElement(\n 'div',\n { role: 'tablist', 'aria-label': '爱生活技能页签', style: S.tablist },\n MANAGER_TABS.map((tab, index) => {\n const selected = tab.plugin === active;\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 'aria-selected': selected,\n 'aria-controls': tabsId + '-panel-' + tab.plugin,\n tabIndex: selected ? 0 : -1,\n style: selected ? { ...S.tab, ...S.tabActive } : S.tab,\n onClick: () => {\n setActiveId(tab.plugin);\n },\n onKeyDown: (event: React.KeyboardEvent) => {\n onTabKeyDown(event, index);\n },\n },\n (present.has(tab.plugin) ? '● ' : '○ ') + labelFor(tab),\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 hidden: !selected,\n },\n present.has(tab.plugin)\n ? (props.renderSlot(CONFIG_TAB_SLOT, {}, { only: tab.plugin }) as React.ReactNode)\n : React.createElement(AbsentCard, {\n target: targetForTab(tab),\n fallbackCommand: recoFor(tab).installCmd,\n face,\n }),\n );\n }),\n React.createElement(MorePluginsCard, null),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器:每次取数时现取(connection 后到也不永久缺席),透传给面板组件。\n const getCall = (): CallFace | null => (ctx.connection?.rpc?.call as CallFace | 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 }))\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCA,SAAgB,eAAe,cAA8B;GAC3D,OAAO,kCAAwB;EACjC;;EAGA,MAAa,eAAsC;GACjD;IAAE,OAAO;IAAQ,QAAQ;IAAc,OAAO;IAAI,OAAO;IAAO,QAAQ;GAAiB;GACzF;IAAE,OAAO;IAAW,QAAQ;IAAiB,OAAO;IAAI,OAAO;IAAO,QAAQ;GAAc;GAC5F;IAAE,OAAO;IAAY,QAAQ;IAAkB,OAAO;IAAI,OAAO;IAAM,QAAQ;GAAqB;GACpG;IAAE,OAAO;IAAQ,QAAQ;IAAc,OAAO;IAAI,OAAO;IAAM,QAAQ;GAAiB;GACxF;IAAE,OAAO;IAAQ,QAAQ;IAAc,OAAO;IAAI,OAAO;IAAM,QAAQ;GAAW;GAClF;IAAE,OAAO;IAAQ,QAAQ;IAAgB,OAAO;IAAI,OAAO;IAAM,QAAQ;GAAiB;EAC5F;EAEA,SAAgB,QAAQ,KAA0B;GAChD,MAAM,aAAa,eAAe,IAAI,MAAM;GAC5C,OAAO;IACL,GAAG;IACH,MAAM;IACN;IACA,MAAM,SAAS,IAAI,SAAS,cAAc;GAC5C;EACF;;;;;;;EAsCA,MAAa,cAAuC,CAClD;GAAE,KAAK;GAAQ,OAAO;GAAK,KAAK;GAAmB,KAAK;EAAyC,GACjG;GAAE,KAAK;GAAY,OAAO;GAAM,KAAK;GAAQ,KAAK;EAAoD,CACxG;;;;;;;EAeA,MAAa,eAAyC;GACpD;IACE,KAAK;IACL,MAAM;IACN,KAAK;GACP;GACA;IACE,KAAK;IACL,MAAM;IACN,KAAK;GACP;GACA;IACE,KAAK;IACL,MAAM;IACN,KAAK;GACP;GACA;IACE,KAAK;IACL,MAAM;IACN,KAAK;GACP;EACF;;;;EC1HA,MAAa,cAAc;;GAEzB,SAAS;;GAET,UAAU;;GAEV,MAAM;EACR;;EAGA,MAAa,kBAAkB;;GAE7B,SAAS;;;;;;;GAOT,SAAS;EACX;;EAMA,MAAM,cAAsC;GAC1C,mBAAmB;GACnB,kBAAkB;GAClB,wBAAwB;GACxB,wBAAwB;GACxB,mBAAmB;GACnB,qBAAqB;GACrB,qBAAqB;GACrB,qBAAqB;GACrB,gBAAgB;GAChB,mBAAmB;GACnB,iBAAiB;GACjB,eAAe;GACf,kBAAkB;GAClB,eAAe;GACf,UAAU;EACZ;;EAGA,SAAgB,WAAW,MAAsB;GAC/C,OAAO,YAAY,SAAS,UAAU,OAAO;EAC/C;;;;;;;;;;;;;EC1BA,MAAM,iBAAiB;;EAEvB,MAAM,mBAAmB;EACzB,IAAI,aAAa;EAEjB,SAAS,QAAQ,MAAc,UAAmC,CAAC,GAAG,SAAwB,MAAmB;GAC/G,OAAO;IAAE,IAAI;IAAO;IAAM,SAAS,WAAW,IAAI;IAAG;IAAS;GAAO;EACvE;;;;;EAMA,SAAS,MAAM,IAA2B;GACxC,OAAO,IAAI,SAAS,YAAY;IAC9B,MAAM,QAAQ,iBAAiB;KAC7B,aAAa,KAAK;KAClB,QAAQ;IACV,GAAG,EAAE;GACP,CAAC;EACH;EAEA,eAAe,YAAe,MAAuB,QAAgB,SAA2D;GAC9H,IAAI,OAAO,SAAS,YAAY,OAAO,QAAQ,YAAY,EAAE,QAAQ,iCAAiC,CAAC;GACvG,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,KAAK,YAAY,SAAS,YAAY,UAAU;KAAE;KAAQ;IAAQ,CAAC;GACpF,SAAS,OAAO;IACd,OAAO,QAAQ,gBAAgB,EAAE,QAAQ,OAAQ,OAAiB,WAAW,KAAK,EAAE,CAAC;GACvF;GACA,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,OAAQ,OAA4B,OAAO,WACtF,OAAO,QAAQ,YAAY,EAAE,QAAQ,kBAAkB,CAAC;GAE1D,IAAI,OAAO,IAAI,OAAO;IAAE,IAAI;IAAM,OAAO,OAAO;GAAW;GAC3D,MAAM,QAAS,OAA6D;GAG5E,OAAO,QAFM,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO,YAC5C,SAAS,OAAO,MAAM,YAAY,YAAY,MAAM,YAAY,OAAQ,MAAM,UAAsC,CAAC,CACzG;EAC9B;;EAGA,eAAsB,YAAY,MAAwF;GACxH,MAAM,MAAM,MAAM,YAAyD,MAAM,gBAAgB,SAAS,CAAC,CAAC;GAC5G,IAAI,CAAC,IAAI,IAAI,OAAO;GAGpB,OAAO;IAAE,IAAI;IAAM,OAAO;KAAE,SAFZ,MAAM,QAAQ,IAAI,OAAO,OAAO,IAAI,IAAI,MAAM,UAAU,CAAC;KAEpC,QADtB,OAAO,IAAI,OAAO,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI,IAAI,MAAM,SAAS;IACtD;GAAE;EAChD;;EAGA,eAAsB,YACpB,MACA,QACA,WACoC;GACpC,MAAM,OAAO,aAAa,OAAO,QAAQ;GACzC,IAAI,CAAC,OAAO,UAAU,CAAC,MAAM,OAAO,QAAQ,eAAe,EAAE,QAAQ,OAAO,IAAI,CAAC;GACjF,OAAO,YAA0B,MAAM,MAAM,CAAC,CAAC;EACjD;;EAGA,eAAsB,cACpB,MACA,QACA,SACgE;GAChE,OAAO,YAAY,MAAM,gBAAgB,SAAS;IAAE,aAAa,OAAO;IAAa;GAAQ,CAAC;EAChG;;EAGA,eAAsB,gBACpB,MACA,QACA,QACoC;GACpC,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,eAAe,EAAE,QAAQ,OAAO,IAAI,CAAC;GACxE,MAAM,QAAQ,MAAM,YAAY,MAAM,MAAM;GAC5C,IAAI,CAAC,MAAM,IAAI,OAAO;GACtB,IAAI,UAAU,MAAM;GACpB,IAAI,CAAC,QAAQ,SAEX,OAAO,QADM,QAAQ,SAAS,iBAAiB,gBAC1B,EAAE,QAAQ,OAAO,IAAI,GAAG,QAAQ,MAAM;GAE7D,cAAc;GACd,MAAM,YAAY,mBAAmB,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,IAAI,MAAM,OAAO,UAAU;GACtF,IAAI,YAAY,MAAM,YAA0B,MAAM,OAAO,OAAO,SAAS;IAC3E,SAAS,QAAQ,QAAQ;IACzB;GACF,CAAC;GACD,IAAI,CAAC,UAAU,MAAM,UAAU,SAAS,iBAAiB;IACvD,MAAM,QAAQ,MAAM,YAAY,MAAM,MAAM;IAC5C,IAAI,CAAC,MAAM,IAAI,OAAO;IACtB,UAAU,MAAM;IAChB,IAAI,CAAC,QAAQ,SACX,OAAO,QAAQ,QAAQ,SAAS,iBAAiB,gBAAgB,EAAE,QAAQ,OAAO,IAAI,GAAG,QAAQ,MAAM;IAEzG,YAAY,MAAM,YAA0B,MAAM,OAAO,OAAO,SAAS;KACvE,SAAS,QAAQ,QAAQ;KACzB,WAAW,YAAY;IACzB,CAAC;GACH;GACA,IAAI,CAAC,UAAU,IAAI,OAAO;IAAE,GAAG;IAAW,QAAQ,UAAU,UAAU,QAAQ;GAAO;GAErF,IAAI,SAAS,UAAU;GACvB,MAAM,WAAW,SAAS,IAAI,SAAS;GACvC,KAAK,IAAI,QAAQ,IAAK,SAAS,GAAG;IAChC,MAAM,MAAM,OAAO,SAAS;IAC5B,IAAI,QAAQ,QAAS,IAAI,UAAU,gBAAgB,IAAI,UAAU,aAAc;IAC/E,IAAI,SAAS,gBAAgB;IAC7B,MAAM,MAAM,QAAQ;IACpB,MAAM,SAAS,MAAM,YAAY,MAAM,QAAQ,OAAO,OAAO,MAAM;IACnE,IAAI,CAAC,OAAO,IAAI,OAAO;IACvB,SAAS,OAAO;GAClB;GACA,OAAO;IAAE,IAAI;IAAM,OAAO;GAAO;EACnC;;;;ECxHA,SAAgB,SAAS,QAA6B;GACpD,OAAO,OAAO,mBAAmB,QAAQ,OAAO,qBAAqB;EACvE;;EAaA,SAAgB,UAAU,QAAoB,UAAmC;GAC/E,MAAM,SAAS,SAAS,MAAM;GAC9B,MAAM,SAAS,SAAS;GACxB,IAAI,UAAU,WAAW,MACvB,OAAO;IAAE,MAAM;IAAW,MAAM;IAAwB,QAAQ;GAAK;GAEvE,IAAI,QACF,OAAO;IAAE,MAAM;IAAoB,MAAM,cAAc,OAAO,MAAM,IAAI;IAAK,QAAQ;GAAU;GAEjG,IAAI,SAAS,kBAAkB,MAAM;IACnC,MAAM,UAAU,SAAS;IACzB,IAAI,YAAY,mBACd,OAAO;KAAE,MAAM;KAAW,MAAM,WAAW,OAAO;KAAG,QAAQ;IAAK;IAEpE,OAAO;KAAE,MAAM;KAAW,MAAM,WAAW,OAAO;KAAG,QAAQ;IAAK;GACpE;GACA,IAAI,WAAW,MACb,OAAO;IAAE,MAAM;IAAW,MAAM;IAAqB,QAAQ;GAAK;GAEpE,IAAI,SAAS,YACX,OAAO;IAAE,MAAM;IAAoB,MAAM,SAAS,OAAO,MAAM,IAAI;IAAa,QAAQ;GAAS;GAEnG,OAAO;IAAE,MAAM;IAAc,MAAM,UAAU,OAAO,MAAM,IAAI;IAAM,QAAQ;GAAK;EACnF;;EAGA,SAAgB,mBAAmB,QAAoB,UAAyC;GAC9F,IAAI,SAAS,kBAAkB,mBAAmB,OAAO;GACzD,MAAM,YAAY,SAAS,oBAAoB,SAAS,KAAK,iBAAiB;GAC9E,MAAM,UAAU,SAAS;GACzB,IAAI,cAAc,MAAM,OAAO;GAC/B,IAAI,cAAc,SAAS,OAAO,WAAW,YAAY;GACzD,OAAO,WAAW,YAAY,iBAAiB,UAAU;EAC3D;;EAGA,SAAgB,aAAa,QAAoB,UAAoD;GACnG,MAAM,UAAU,UAAU,kBAAkB,OAAO,kBAAkB;GACrE,MAAM,YAAY,UAAU,oBAAoB,OAAO;GACvD,MAAM,QAAQ,CAAC,OAAO,cAAc,MAAM,WAAW,aAAa,cAAc,UAAU,WAAW,YAAY,MAAM,GAAG;GAC1H,IAAI,OAAO,OAAO,MAAM,KAAK,OAAO,MAAM,cAAc,MAAM,OAAO,MAAM,UAAU,cAAc;QAC9F,MAAM,KAAK,gBAAgB;GAChC,OAAO;EACT;;;;;;;;;;;;;;ECvEA,MAAa,cAAc;GACzB,MAAM;IAAE,UAAU;IAAI,YAAY;IAAK,QAAQ;IAAa,OAAO;GAA0C;GAC7G,SAAS;IAAE,SAAS;IAAQ,YAAY;IAAU,gBAAgB;IAAiB,KAAK;IAAG,UAAU;GAAO;GAC5G,SAAS;IAAE,SAAS;IAAe,YAAY;IAAU,KAAK;GAAE;GAChE,MAAM;IAAE,OAAO;IAA6C,UAAU;IAAI,YAAY;IAAK,cAAc;GAAG;GAC5G,SAAS;IAAE,SAAS;IAAQ,UAAU;IAAQ,KAAK;IAAG,cAAc;GAAG;GACvE,KAAK;IACH,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,cAAc;IACd,SAAS;IACT,UAAU;IACV,QAAQ;GACV;GACA,WAAW;IACT,YAAY;IACZ,aAAa;IACb,OAAO;IACP,YAAY;GACd;GACA,MAAM;IACJ,SAAS;IACT,cAAc;IACd,QAAQ;IACR,OAAO;IACP,UAAU;IACV,YAAY;GACd;GACA,KAAK;IACH,SAAS;IACT,WAAW;IACX,SAAS;IACT,cAAc;IACd,YAAY;IACZ,OAAO;IACP,YAAY;IACZ,UAAU;IACV,YAAY;GACd;GACA,KAAK;IACH,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,cAAc;IACd,SAAS;IACT,UAAU;IACV,QAAQ;GACV;GACA,QAAQ;IACN,SAAS;IACT,cAAc;IACd,cAAc;IACd,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,UAAU;IACV,YAAY;GACd;GACA,KAAK;IACH,SAAS;IACT,cAAc;IACd,cAAc;IACd,QAAQ;IACR,UAAU;IACV,YAAY;GACd;GACA,SAAS;IAAE,YAAY;IAAK,cAAc;GAAE;GAC5C,SAAS;IAAE,OAAO;IAA6C,UAAU;GAAG;GAC5E,QAAQ;IAAE,OAAO;IAAiD,UAAU;IAAI,WAAW;GAAE;GAC7F,OAAO;IAAE,OAAO;IAA4C,UAAU;IAAI,WAAW;GAAE;EACzF;EAmBA,MAAM,OAAuB;GAAE,OAAO;GAAQ,SAAS;GAAM,SAAS;EAAK;;EAG3E,SAAgB,cAAc,SAAgD;GAC5E,MAAM,CAAC,SAAS,cAAcA,MAAM,SAAgC,CAAC,CAAC;GACtE,MAAM,CAAC,MAAM,WAAWA,MAAM,SAAyC,CAAC,CAAC;GACzE,MAAM,CAAC,QAAQ,aAAaA,MAAM,SAAS,GAAI;GAC/C,MAAM,CAAC,WAAW,gBAAgBA,MAAM,SAAwB,IAAI;GACpE,MAAM,CAAC,UAAU,eAAeA,MAAM,SAAS,KAAK;GACpD,MAAM,gBAAgB;IACpB,IAAI,QAAQ;IACZ,CAAM,YAAY;KAChB,MAAM,SAAS,MAAM,YAAY,QAAQ,CAAC;KAC1C,IAAI,CAAC,OAAO;KACZ,IAAI,OAAO,IAAI;MACb,WAAW,OAAO,MAAM,OAAO;MAC/B,UAAU,OAAO,MAAM,MAAM;KAC/B,OACE,aAAa,OAAO,OAAO;IAE/B,EAAA,CAAG;IACH,aAAa;KACX,QAAQ;IACV;GACF,GAAG,CAAC,OAAO,CAAC;GACZ,MAAM,QAAQA,MAAM,aAAa,KAAa,SAAyB;IACrE,SAAS,cAAc;KAAE,GAAG;MAAW,MAAM;IAAK,EAAE;GACtD,GAAG,CAAC,CAAC;GA8CL,OAAO;IAAE;IAAS;IAAM;IAAQ;IAAW;IAAU,UA7CpCA,MAAM,kBAAkB;KACvC,YAAY,IAAI;KAChB,MAAM,OAAO;KACb,KAAK,MAAM,UAAU,MAAM,MAAM,OAAO,KAAK;MAAE,OAAO;MAAY,SAAS,KAAK,OAAO,IAAI,EAAE,WAAW;MAAM,SAAS;KAAK,CAAC;KAC7H,QAAa,IACX,KAAK,IAAI,OAAO,WAAW;MACzB,MAAM,SAAS,MAAM,YAAY,QAAQ,GAAG,MAAM;MAClD,MAAM,OAAO,KAAK,OAAO,KAAK;OAAE,OAAO;OAAS,SAAS,OAAO;OAAO,SAAS;MAAK,IAAI;OAAE,OAAO;OAAU,SAAS;OAAM,SAAS;MAAO,CAAC;KAC9I,CAAC,CACH,CAAC,CAAC,cAAc,YAAY,KAAK,CAAC;IACpC,GAAG;KAAC;KAAS;KAAO;KAAM;IAAO,CAmC2B;IAAG,KAlCnDA,MAAM,aACf,WAAuB;KACtB,MAAM,UAAU,KAAK,OAAO,QAAQ;KACpC,MAAM,OAAO,KAAK;MAAE,GAAG;MAAS,OAAO;MAAc,SAAS;KAAK,CAAC;KACpE,CAAM,YAAY;MAChB,MAAM,SAAS,SAAS,MAAM;MAC9B,IAAI,UAAU,QAAQ,SAAS,SAAS,iBAAiB;MACzD,IAAI,UAAU,YAAY,MAAM;OAC9B,MAAM,UAAU,MAAM,YAAY,QAAQ,GAAG,MAAM;OACnD,IAAI,CAAC,QAAQ,IAAI;QACf,MAAM,OAAO,KAAK;SAAE,OAAO;SAAU,SAAS;SAAM,SAAS;QAAQ,CAAC;QACtE;OACF;OACA,MAAM,OAAO,KAAK;QAAE,OAAO;QAAc,SAAS,QAAQ;QAAO,SAAS;OAAK,CAAC;OAChF,UAAU,QAAQ,MAAM,SAAS;MACnC;MACA,MAAM,SAAS,SACX,MAAM,cAAc,QAAQ,GAAG,QAAQ,WAAW,EAAE,IACpD,MAAM,gBAAgB,QAAQ,GAAG,QAAQ,MAAM;MACnD,IAAI,OAAO,IAAI;OAEb,MAAM,YAAY,MAAM,YAAY,QAAQ,GAAG,QAAQ,OAAO,QAAQ,MAAM;OAC5E,MAAM,OAAO,KAAK,UAAU,KAAK;QAAE,OAAO;QAAS,SAAS,UAAU;QAAO,SAAS;OAAK,IAAI;QAAE,OAAO;QAAU,SAAS;QAAM,SAAS;OAAU,CAAC;OACrJ;MACF;MACA,MAAM,OAAO,KAAK;OAChB,OAAO;OACP,SAAS,QAAQ;OACjB,SAAS;QAAE,GAAG;QAAQ,QAAQ,OAAO,UAAU,QAAQ,SAAS,UAAU;OAAK;MACjF,CAAC;KACH,EAAA,CAAG;IACL,GACA;KAAC;KAAS;KAAO;KAAQ;IAAI,CAEkC;GAAE;EACrE;;EAGA,SAAgB,kBAAkB,OAA8D;GAC9F,MAAM,EAAE,UAAU,UAAU,YAAY,MAAM;GAC9C,MAAM,WAAW,YAAY,QAAQ,WAAW;GAChD,OAAOA,MAAM,cACX,UACA;IACE,MAAM;IACN,OAAO,WAAW;KAAE,GAAG,YAAY;KAAK,SAAS;KAAM,QAAQ;IAAU,IAAI,YAAY;IACzF;IACA,SAAS;IACT,OAAO;GACT,GACA,WAAW,SAAS,MACtB;EACF;;EAGA,SAAS,eAAe,OAAqE;GAC3F,MAAM,QAAkB,CAAC;GACzB,KAAK,MAAM,UAAU,MAAM,KAAK,SAAS;IACvC,MAAM,UAAU,MAAM,KAAK,KAAK,OAAO,IAAI,EAAE;IAC7C,IAAI,CAAC,SAAS;IACd,MAAM,OAAO,mBAAmB,QAAQ,QAAQ,QAAQ;IACxD,IAAI,MAAM,MAAM,KAAK,IAAI;GAC3B;GACA,IAAI,MAAM,WAAW,GAAG,OAAO;GAC/B,OAAOA,MAAM,cACX,OACA,EAAE,OAAO,YAAY,OAAO,GAC5B,MAAM,KAAK,MAAM,UAAUA,MAAM,cAAc,OAAO,EAAE,KAAK,OAAO,KAAK,EAAE,GAAG,IAAI,CAAC,CACrF;EACF;;EAGA,SAAgB,cAAc,OAAqE;GACjG,MAAM,EAAE,SAAS,MAAM,WAAW,aAAa,MAAM;GACrD,MAAM,QAAQ,QAAQ,QAAQ,WAAW,KAAK,OAAO,QAAQ,KAAK,OAAO,IAAI,CAAC,UAAU,MAAM;GAC9F,IAAI,WAAW,OAAOA,MAAM,cAAc,OAAO,EAAE,OAAO,YAAY,OAAO,GAAG,aAAa,SAAS;GACtG,IAAI,QAAQ,WAAW,GAAG,OAAO;GACjC,IAAI,MAAM,WAAW,GAAG,OAAO;GAC/B,OAAOA,MAAM,cACX,OACA,MACAA,MAAM,cAAc,gBAAgB,EAAE,MAAM,MAAM,KAAK,CAAC,GACxD,MAAM,KAAK,WAAW;IACpB,MAAM,MAAM,KAAK,OAAO;IACxB,MAAM,WAAW,IAAI,SAAS,YAAY;IAC1C,MAAM,UAAU,WAAW,UAAU,QAAQ,QAAQ,IAAI;IACzD,MAAM,SAAS,IAAI,SAAS,UAAU,IAAI,SAAS,UAAU;IAC7D,MAAM,OAAO,IAAI,UAAU;IAC3B,OAAOA,MAAM,cACX,OACA;KAAE,KAAK,OAAO;KAAK,OAAO,YAAY;IAAI,GAC1CA,MAAM,cAAc,OAAO,EAAE,OAAO,YAAY,QAAQ,GAAG,OAAO,QAAQ,QAAQ,OAAO,WAAW,GACpGA,MAAM,cACJ,OACA,EAAE,OAAO,YAAY,QAAQ,GAC7B,QACE,OAAO,UAAU,kBAAkB,OAAO,kBAAkB,KAAK,IACjE,WACA,OAAO,UAAU,iBAAiB,IAAI,CAC1C,GACAA,MAAM,cAAc,OAAO,MAAM,OAAO,aAAc,SAAS,SAAS,WAAW,SAAS,GAAI,GAChG,SAAS,SACLA,MAAM,cACJ,UACA;KACE,MAAM;KACN,OAAO,OAAO;MAAE,GAAG,YAAY;MAAK,SAAS;KAAK,IAAI;MAAE,GAAG,YAAY;MAAK,WAAW;KAAE;KACzF,UAAU;KACV,eAAe,MAAM,KAAK,IAAI,MAAM;IACtC,GACA,QAAQ,WAAW,YAAY,OAAO,MACxC,IACA,MACJ,IAAI,UAAUA,MAAM,cAAc,OAAO,EAAE,OAAO,YAAY,OAAO,GAAG,SAAS,IAAI,QAAQ,OAAO,IAAI,MACxG,aAAa,QAAQ,QAAQ,CAAC,CAAC,KAAK,MAAM,UACxCA,MAAM,cAAc,OAAO;KAAE,KAAK,OAAO,KAAK;KAAG,OAAO,YAAY;IAAM,GAAG,IAAI,CACnF,GACA,SAASA,MAAM,cAAc,QAAQ,EAAE,OAAO,YAAY,IAAI,GAAG,MAAM,IAAI,IAC7E;GACF,CAAC,CACH;EACF;;EAGA,SAAgB,WAAW,OAIJ;GACrB,MAAM,SAAS,MAAM;GACrB,MAAM,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,OAAO;GACnD,MAAM,OAAO,KAAK,UAAU;GAC5B,MAAM,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,UAAU,MAAM;GACrE,IAAI,SAAwB,KAAK,SAAS,WAAW;GACrD,IAAI,UAAU,WAAW,QAAQ,KAAK,WAAW,IAAI,QAAQ,SAAS,kBAAkB,MACtF,SAAS,UAAU,QAAQ,IAAI,QAAQ,QAAQ,CAAC,CAAC;GAEnD,OAAOA,MAAM,cACX,OACA,EAAE,OAAO,YAAY,KAAK,GAC1BA,MAAM,cAAc,OAAO,MAAM,SAAS,SAAS,OAAO,cAAc,mBAAmB,eAAe,GAC1G,SACIA,MAAM,cACJ,UACA;IACE,MAAM;IACN,OAAO,OAAO;KAAE,GAAG,YAAY;KAAK,WAAW;KAAG,SAAS;IAAK,IAAI;KAAE,GAAG,YAAY;KAAK,WAAW;IAAE;IACvG,UAAU;IACV,eAAe,MAAM,KAAK,IAAI,MAAM;GACtC,GACA,OAAO,aAAa,IACtB,IACA,MACJ,SAASA,MAAM,cAAc,OAAO,EAAE,OAAO,YAAY,OAAO,GAAG,MAAM,IAAI,MAC7EA,MAAM,cAAc,QAAQ,EAAE,OAAO,YAAY,IAAI,GAAG,MAAM,CAChE;EACF;;;;;;;;;;;;;;;;;;;;;EC/QA,MAAa,SAAS,CAAC,SAAS,YAAY;;EAG5C,MAAa,kBAAkB;;EAG/B,MAAM,IAAI;GACR,SAAS;IACP,SAAS;IACT,UAAU;IACV,YAAY;IACZ,KAAK;IACL,QAAQ;GACV;GACA,MAAM;IAAE,UAAU;IAAI,YAAY;IAAK,MAAM;IAAY,OAAO;GAA0C;GAC1G,aAAa;IACX,SAAS;IACT,YAAY;IACZ,KAAK;IACL,YAAY;IACZ,MAAM;GACR;GACA,UAAU;IACR,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,OAAO;IACP,QAAQ;IACR,cAAc;IACd,QAAQ;IACR,OAAO;IACP,gBAAgB;IAChB,UAAU;IACV,YAAY;GACd;GACA,MAAM;IAAE,OAAO;IAA6C,UAAU;IAAI,YAAY;IAAK,cAAc;GAAG;GAC5G,SAAS;IAAE,SAAS;IAAQ,UAAU;IAAQ,KAAK;IAAG,cAAc;GAAG;GACvE,KAAK;IACH,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,cAAc;IACd,SAAS;IACT,UAAU;IACV,QAAQ;GACV;GACA,WAAW;IACT,YAAY;IACZ,aAAa;IACb,OAAO;IACP,YAAY;GACd;GACA,MAAM;IACJ,SAAS;IACT,cAAc;IACd,QAAQ;IACR,OAAO;IACP,UAAU;IACV,YAAY;GACd;GACA,KAAK;IACH,SAAS;IACT,WAAW;IACX,SAAS;IACT,cAAc;IACd,YAAY;IACZ,OAAO;IACP,YAAY;IACZ,UAAU;IACV,YAAY;GACd;GACA,UAAU;IACR,WAAW;IACX,SAAS;IACT,cAAc;IACd,QAAQ;GACV;GACA,WAAW;IACT,UAAU;IACV,YAAY;IACZ,cAAc;IACd,OAAO;GACT;GACA,SAAS;IACP,SAAS;IACT,UAAU;IACV,YAAY;IACZ,KAAK;IACL,SAAS;IACT,gBAAgB;IAChB,OAAO;GACT;GACA,SAAS;IACP,MAAM;IACN,UAAU;IACV,cAAc;IACd,YAAY;IACZ,UAAU;IACV,YAAY;GACd;GACA,UAAU;IACR,MAAM;IACN,UAAU;IACV,UAAU;IACV,YAAY;IACZ,OAAO;GACT;GACA,UAAU;IACR,MAAM;IACN,YAAY;IACZ,SAAS;IACT,OAAO;GACT;EACF;;;;EAMA,SAAS,aAAa,OAAoD;GACxE,IAAI,OAAO,UAAU,YAAY,OAAO,MAAM;GAC9C,OAAO,SAAS;EAClB;;EAGA,SAAS,eAAmC;GAC1C,OAAOC,MAAM,cACX,OACA;IACE,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,aAAa;IACb,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,WAAW;GACb,GACAA,MAAM,cAAc,QAAQ,EAAE,GAAG,sBAAsB,CAAC,GACxDA,MAAM,cAAc,QAAQ,EAAE,GAAG,kBAAkB,CAAC,GACpDA,MAAM,cAAc,QAAQ,EAAE,GAAG,mEAAmE,CAAC,CACvG;EACF;;;;;;;EAQA,SAAS,eAAmC;GAC1C,OAAOA,MAAM,cACX,OACA,EAAE,OAAO,EAAE,YAAY,GACvB,YAAY,KAAK,SACfA,MAAM,cACJ,KACA;IACE,KAAK,KAAK;IACV,MAAM,KAAK;IACX,QAAQ;IACR,KAAK;IACL,OAAO,KAAK;IACZ,cAAc,KAAK;IACnB,OAAO,EAAE;GACX,GACAA,MAAM,cAAc,QAAQ,EAAE,eAAe,OAAO,GAAG,KAAK,KAAK,CACnE,CACF,CACF;EACF;;;;;EAMA,SAAS,kBAAsC;GAC7C,OAAOA,MAAM,cACX,OACA,EAAE,OAAO,EAAE,SAAS,GACpBA,MAAM,cAAc,OAAO,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAC3D,aAAa,KAAK,QAChBA,MAAM,cACJ,KACA;IACE,KAAK,IAAI;IACT,MAAM,IAAI;IACV,QAAQ;IACR,KAAK;IACL,OAAO,IAAI,MAAM;IACjB,OAAO,EAAE;GACX,GACAA,MAAM,cAAc,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,GACzDA,MAAM,cAAc,QAAQ,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,IAAI,GAC3DA,MAAM,cAAc,QAAQ,EAAE,OAAO,EAAE,SAAS,GAAGA,MAAM,cAAc,cAAc,IAAI,CAAC,CAC5F,CACF,CACF;EACF;;EAGA,SAAS,gBAAgB,OAAsF;GAC7G,MAAM,SAASA,MAAM,MAAM;GAC3B,MAAM,UAAUA,MAAM,OAAwC,CAAC,CAAC;GAChE,MAAM,OAAuB,MAAM,SAAS,UAAU,KAAK;GAC3D,MAAM,UAAU,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,EAAE,CAAC;GAC7C,MAAM,OAAO,cAAc,MAAM,OAAO;GACxC,MAAM,CAAC,UAAU,eAAeA,MAAM,SAA6B,KAAA,CAAS;GAC5E,MAAM,CAAC,YAAY,iBAAiBA,MAAM,+BAAoC,IAAI,IAAI,CAAC;GACvF,MAAM,SAAS,aAAa,MAAM,MAAM,EAAE,WAAW,QAAQ,IACxD,WACD,aAAa,EAAE,CAAC;GACpB,MAAM,gBAAgB;IACpB,eAAe,aAAa;KAC1B,IAAI,SAAS,IAAI,MAAM,GAAG,OAAO;KACjC,uBAAO,IAAI,IAAI,CAAC,GAAG,UAAU,MAAM,CAAC;IACtC,CAAC;GACH,GAAG,CAAC,MAAM,CAAC;GACX,SAAS,SAAS,KAAyB;IACzC,MAAM,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,IAAI,MAAM;IAChD,OAAO,OAAO,IAAI,MAAM,SAAS,IAAI,IAAI,QAAQ,IAAI;GACvD;GACA,SAAS,aAAa,KAAiB;IACrC,OAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,gBAAgB,IAAI,MAAM,KAAK;GACnE;GACA,SAAS,aAAa,OAA4B,OAAqB;IACrE,IAAI;IACJ,QAAQ,MAAM,KAAd;KACE,KAAK;MAAc,aAAa,QAAQ,KAAK,aAAa;MAAQ;KAClE,KAAK;MAAa,aAAa,QAAQ,IAAI,aAAa,UAAU,aAAa;MAAQ;KACvF,KAAK;MAAQ,YAAY;MAAG;KAC5B,KAAK;MAAO,YAAY,aAAa,SAAS;MAAG;KACjD,SAAS;IACX;IACA,MAAM,eAAe;IACrB,MAAM,OAAO,aAAa;IAC1B,YAAY,KAAK,MAAM;IACvB,QAAQ,QAAQ,UAAU,EAAE,MAAM;GACpC;GACA,OAAOA,MAAM,cACX,OACA,MACAA,MAAM,cACJ,OACA,EAAE,OAAO,EAAE,QAAQ,GACnBA,MAAM,cAAc,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,KAAK,GAEnDA,MAAM,cACJ,OACA,EAAE,OAAO,EAAE,YAAY,GACvBA,MAAM,cAAc,mBAAmB,EAAE,KAAK,CAAC,GAC/CA,MAAM,cAAc,cAAc,IAAI,CACxC,CACF,GACAA,MAAM,cACJ,OACA,EAAE,OAAO,EAAE,KAAK,GAChBA,MAAM,cAAc,OAAO,MAAM,mBAAmB,GACpDA,MAAM,cAAc,OAAO,MAAM,0BAAuC,CAC1E,GACAA,MAAM,cAAc,eAAe,EAAE,KAAK,CAAC,GAC3CA,MAAM,cACJ,OACA;IAAE,MAAM;IAAW,cAAc;IAAW,OAAO,EAAE;GAAQ,GAC7D,aAAa,KAAK,KAAK,UAAU;IAC/B,MAAM,WAAW,IAAI,WAAW;IAChC,OAAOA,MAAM,cACX,UACA;KACE,KAAK,IAAI;KACT,MAAM,YAAsC;MAC1C,QAAQ,QAAQ,SAAS;KAC3B;KACA,IAAI,SAAS,UAAU,IAAI;KAC3B,MAAM;KACN,MAAM;KACN,iBAAiB;KACjB,iBAAiB,SAAS,YAAY,IAAI;KAC1C,UAAU,WAAW,IAAI;KACzB,OAAO,WAAW;MAAE,GAAG,EAAE;MAAK,GAAG,EAAE;KAAU,IAAI,EAAE;KACnD,eAAe;MACb,YAAY,IAAI,MAAM;KACxB;KACA,YAAY,UAA+B;MACzC,aAAa,OAAO,KAAK;KAC3B;IACF,IACC,QAAQ,IAAI,IAAI,MAAM,IAAI,OAAO,QAAQ,SAAS,GAAG,CACxD;GACF,CAAC,CACH,GACA,aAAa,QAAQ,QAAQ,IAAI,WAAW,UAAU,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ;IAC7F,MAAM,WAAW,IAAI,WAAW;IAChC,OAAOA,MAAM,cACX,OACA;KACE,KAAK,IAAI;KACT,IAAI,SAAS,YAAY,IAAI;KAC7B,MAAM;KACN,mBAAmB,SAAS,UAAU,IAAI;KAC1C,QAAQ,CAAC;IACX,GACA,QAAQ,IAAI,IAAI,MAAM,IACjB,MAAM,WAAW,iBAAiB,CAAC,GAAG,EAAE,MAAM,IAAI,OAAO,CAAC,IAC3DA,MAAM,cAAc,YAAY;KAC9B,QAAQ,aAAa,GAAG;KACxB,iBAAiB,QAAQ,GAAG,CAAC,CAAC;KAC9B;IACF,CAAC,CACP;GACF,CAAC,GACDA,MAAM,cAAc,iBAAiB,IAAI,CAC3C;EACF;EAEA,SAAgB,MAAM,KAAsB;GAE1C,MAAM,gBAAkC,IAAI,YAAY,KAAK,QAAiC;GAE9F,IAAI,cAAc;GAClB,IAAI,OAAuB,CAAC;GAC5B,MAAM,yBAAyB,EAC7B,OAAO,EACL,MAAM;IACJ,mBAAmB;KACjB,MAAM,UAAU,IAAI,MAAM,WAAW,eAAe;KACpD,IAAI,YAAY,aAAa;MAC3B,cAAc;MACd,OAAO,IAAI,MACR,QAAQ,eAAe,CAAC,CACxB,KAAK,WAAW;OACf,IAAI,MAAM,QAAQ,MAAM;OACxB,OAAO,MAAM,QAAQ,SAAS;OAC9B,OAAO,aAAa,MAAM,QAAQ,KAAK;MACzC,EAAE,CAAC,CACF,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;KACrC;KACA,OAAO;IACT;IACA,YAAY,aAAyB,IAAI,MAAM,UAAU,iBAAiB,QAAQ;GACpF,EACF,EACF;GACA,IAAI,MAAM,OAAO,0BACf,IAAI,MAAM,SACR;IACE,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa;IACb,QAAQ;IACR,UAAU,EAAE,oBAAoB;KAAE,MAAM;KAAQ,OAAO;IAAO,EAAE;GAClE,IACC,UAAgCA,MAAM,cAAc,iBAAiB;IAAE,GAAG;IAAO;GAAQ,CAAC,CAC7F,CACF;EACF"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["React","React"],"sources":["../src/nav.ts","../src/update-contract.ts","../src/update-client.ts","../src/update-view.ts","../src/update-panel.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 readonly title: string;\n /** 提供该 tab 的单品插件包名(仅文档级引用,不 import)。 */\n readonly plugin: 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 同步,release 时一起 bump;设置页总设置区版本行唯一来源)。 */\nexport const MANAGER_VERSION = '0.2.2' as const;\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: 'memo', slotId: 'ilife:memo', order: 70, title: '备忘录', plugin: 'dsh-memo-ilife' },\n { skill: 'calorie', slotId: 'ilife:calorie', order: 75, title: '卡路里', plugin: 'dsh-calorie' },\n { skill: 'schedule', slotId: 'ilife:schedule', order: 80, title: '作息', plugin: 'dsh-schedule-ilife' },\n { skill: 'home', slotId: 'ilife:home', order: 85, title: '居家', plugin: 'dsh-home-ilife' },\n { skill: 'chef', slotId: 'ilife:chef', order: 90, title: '大厨', plugin: 'dsh-chef' },\n { skill: 'bill', slotId: 'ilife:cookie', order: 95, title: '记账', plugin: 'dsh-bill-ilife' },\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: '去 GitHub 点 Star', 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 * 分工:本文件只管「两侧都认的常量与文案」;宿主半见 `update-host.ts`,面板见 `client.ts`。\n * 电话名分两类:\n * - 七个**更新电话组**(每组三个 `updateStatus` / `updateCheck` / `updateInstall`)由更新包\n * 自己拼(`update.phoneNames`),面板侧取值见构建期派生的 `update-phones.ts`;\n * - 两个**总管自有电话**(装上缺席包、取版本行数据)是本包定义的,见 `MANAGER_ACTIONS`。\n *\n * 原因码的人话文案逐条抄更新包 README 第 8 节「用户该做什么」列与第 12 节排错表;\n * 手工命令形状抄第 9 节(`commands.ts:104-122` 的 `manualCommand`)。\n */\nimport type { BlockedReason } from 'dsh-plugin-update';\n\n/** 载体:宿主注册 `connection.fetch.register` 的路径 / 面板 `connection.rpc.call` 的两段参数。 */\nexport const MANAGER_RPC = {\n /** 单段通道名,须 match `/^\\/[A-Za-z0-9._~-]+$/`(cookbook §6)。 */\n channel: '/ilife-manager',\n /** 面板侧第二段参数(等于注册路径去掉 `/api` 前缀后那一段)。 */\n endpoint: 'ilife-manager',\n /** 宿主侧注册的完整路径(`/api` 是 DSH 公开载体)。 */\n path: '/api/ilife-manager',\n} as const;\n\n/** 总管自有电话名:更新包的三个动作名是冻结集合(`config.ts:14`),这里另起名字不撞车。 */\nexport const MANAGER_ACTIONS = {\n /** 装上缺席的目标包:入参 `{packageName, version}`,回包 `{packageName, version}`。 */\n install: 'ilife-manager.install',\n /** 七个更新目标的表:入参 `{}`,回包 `{targets: [{key, title, packageName, phones, runningVersion, installedVersion, skill}]}`。\n *\n * 电话名为什么由宿主转交而不是面板侧写死:更新包的三个电话名只有它自己知道\n * (`update.phoneNames`,`host.ts:335`);面板侧既不能 import 更新包(浏览器产物纯度门)\n * 也不该把名字再写一份(写两份必然走散)。宿主把名字表连同版本行一起交出去,\n * 面板一行字面量都不留。 */\n targets: 'ilife-manager.targets',\n} as const;\n\n/** 更新包默认官方源(`config.ts:20`,总管侧拼手工命令时用同一个值)。 */\nexport const DEFAULT_REGISTRY = 'https://registry.npmjs.org/' as const;\n\n/** 装不了 / 装不成的人话原因表:八种 `blockedReason` + 过程错误码(更新包 README 第 8、12 节)。 */\nconst REASON_TEXT: Record<string, string> = {\n 'unknown-profile': '使用范围认不出(名字非法或目录不存在):先检查使用范围名与目录,这种情形不给手工命令。',\n 'source-install': '当前是从源码装的,不是按版本号装的:想走更新,先按版本号重装一次。',\n 'invalid-installation': '已装的包不完整(名字对不上、版本非法、入口文件缺失):重装当前版本,修好已装目录再查。',\n 'installation-changed': '安装位置在使用中途变了:重新打开宿主再查一次;还出现就重装。',\n 'pending-restart': '新版已装到磁盘,正在跑的还是旧版:重启宿主后生效。',\n 'registry-conflict': '本地声明的版本与磁盘实际版本互相矛盾:打开使用范围的清单,把目标包名那行改成版本号再试。',\n 'incompatible-node': '新版要求的 Node 与当前运行的 Node 对不上:先把 Node 升到 22 或更高,再查。',\n 'recovery-required': '上次安装被打断,留下一个半截任务:重新点一次安装;一直出现就查宿主日志。',\n 'check-failed': '查新版失败(联网不通或官方源不可达):检查网络与官方源地址,再查一次。',\n 'invalid-release': '远端发行信息不合规(名字对不上、版本非法、包地址或完整性校验不过):换个版本再查。',\n 'check-expired': '上次查新版的凭证过期了:重新查一次再点安装(不要重试旧编号)。',\n 'update-busy': '同一个使用范围同时只装一个:等当前任务结束再点。',\n 'install-failed': '安装没跑成:按下面那条手工命令在终端执行一次。',\n 'bad-request': '回执异常(请求形状不对):把宿主日志里的事件名报给作者。',\n internal: '回执异常(总管内部错误):把宿主日志里的事件名报给作者。',\n};\n\n/** 原因码 → 人话(认不出的码不猜、原样回码并说明这是未知原因)。 */\nexport function reasonText(code: string): string {\n return REASON_TEXT[code] ?? '未知原因(' + code + '):把宿主日志里的事件名报给作者。';\n}\n\n/** 手工兜底命令(更新包 README 第 9 节形状):使用范围名含特殊字符时加引号。 */\nexport function manualInstallCommand(input: {\n profileName: string | null;\n packageName: string;\n version: string;\n registryUrl?: string;\n}): string | null {\n const name = typeof input.profileName === 'string' ? input.profileName.trim() : '';\n if (!name || name.length > 255 || name.startsWith('-') || ['.', '..', 'node_modules'].includes(name)) return null;\n const version = typeof input.version === 'string' && /^\\d+\\.\\d+\\.\\d+$/.test(input.version) ? input.version : '';\n if (!version) return null;\n const registry = input.registryUrl ?? DEFAULT_REGISTRY;\n const profileArg = /^[A-Za-z0-9_.-]+$/.test(name) ? name : JSON.stringify(name);\n return 'dsh plugin --profile ' + profileArg + ' add --save-exact ' + input.packageName + '@' + version + ' --registry=' + registry;\n}\n\n/** 八种装不了原因码(给门禁与测试对表用;值就是 `BlockedReason` 全集)。 */\nexport const BLOCKED_REASONS: readonly BlockedReason[] = [\n 'unknown-profile',\n 'source-install',\n 'invalid-installation',\n 'installation-changed',\n 'pending-restart',\n 'registry-conflict',\n 'incompatible-node',\n 'recovery-required',\n];\n","/** 面板到宿主的三条流程(纯异步函数:无 DOM、无 react;传输口由调用方注入,便于单测)。\n *\n * 三条流程与更新包 README 的对应关系:\n * - 检查更新:`updateCheck`(第 3 节第 3 步的 `host.call(UPD_CHECK, {})`);\n * - 装上更新:`updateCheck` 拿凭证 → `updateInstall` 提交 → **按间隔轮询 `updateStatus`**\n * 直到任务离开 `installing`/`verifying`(第 3 节第 3 步与第 9 节:安装是后台跑,\n * 调用方不干等;凭证过期 `check-expired` 是正常错误码,重查一次再提交,见第 11 节);\n * - 装上缺席的包:走总管自有的 `ilife-manager.install`(更新包的三个电话只管已装的包)。\n */\nimport { MANAGER_ACTIONS, MANAGER_RPC, reasonText } from './update-contract.js';\nimport type { CheckOutcome, TargetInfo } from './update-view.js';\n\n/** 传输口(DSH 载体 `connection.rpc.call` 的形状,cookbook §6)。 */\nexport type CallFace = (\n channel: string,\n endpoint: string,\n payload: unknown,\n signal?: AbortSignal,\n) => Promise<\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: { readonly code: string; readonly message: string; readonly details: Record<string, unknown> } }\n>;\n\nexport interface CallFailure {\n readonly ok: false;\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n /** 能给的话给一条可复制的手工命令(更新包每次回包都带,见其 README 第 9 节)。 */\n readonly manual: string | null;\n}\n\nexport type CallOutcome<T> = { readonly ok: true; readonly value: T } | CallFailure;\n\n/** 轮询上限:1 秒一次、最多 900 次(覆盖更新包 15 分钟的安装时限)。 */\nconst MAX_POLL_TRIES = 900;\n/** 轮询默认间隔(只有在宿主没给 `pollMs` 时才用;正常取值由宿主转交更新包的 panelPollMs)。 */\nconst FALLBACK_POLL_MS = 1000;\nlet requestSeq = 0;\n\nfunction failure(code: string, details: Record<string, unknown> = {}, manual: string | null = null): CallFailure {\n return { ok: false, code, message: reasonText(code), details, manual };\n}\n\n/** 取数间隔:settle 即清 timer。定时器有界重试的例外依据(p10 静态门三件套):\n * 这里有清理(clearTimeout)、有次数上限(MAX_POLL_TRIES)、间隔取自宿主转交的更新包\n * `panelPollMs`(不写死);用途是更新包 README 第 3 节要求的状态轮询(安装是后台跑,\n * 面板每秒问一次进度),不是无界重试。 */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n clearTimeout(timer);\n resolve();\n }, ms);\n });\n}\n\nasync function callManager<T>(call: CallFace | null, method: string, payload: Record<string, unknown>): Promise<CallOutcome<T>> {\n if (typeof call !== 'function') return failure('internal', { detail: '宿主连接缺席:connection.rpc.call 不可用' });\n let result: Awaited<ReturnType<CallFace>>;\n try {\n result = await call(MANAGER_RPC.channel, MANAGER_RPC.endpoint, { method, payload });\n } catch (error) {\n return failure('check-failed', { detail: String((error as Error)?.message ?? error) });\n }\n if (!result || typeof result !== 'object' || typeof (result as { ok?: unknown }).ok !== 'boolean') {\n return failure('internal', { detail: '回执信封异常(非 ok 信封)' });\n }\n if (result.ok) return { ok: true, value: result.value as T };\n const error = (result as { error?: { code?: unknown; details?: unknown } }).error;\n const code = typeof error?.code === 'string' ? error.code : 'internal';\n const details = error && typeof error.details === 'object' && error.details !== null ? (error.details as Record<string, unknown>) : {};\n return failure(code, details);\n}\n\n/** 取七个更新目标的表(宿主转交更新包的三个电话名与版本行,面板不写死任何电话名)。 */\nexport async function loadTargets(call: CallFace | null): Promise<CallOutcome<{ targets: TargetInfo[]; pollMs: number }>> {\n const out = await callManager<{ targets?: TargetInfo[]; pollMs?: number }>(call, MANAGER_ACTIONS.targets, {});\n if (!out.ok) return out;\n const targets = Array.isArray(out.value?.targets) ? out.value.targets : [];\n const pollMs = typeof out.value?.pollMs === 'number' && out.value.pollMs > 0 ? out.value.pollMs : FALLBACK_POLL_MS;\n return { ok: true, value: { targets, pollMs } };\n}\n\n/** 查一家:默认查新版(`updateCheck`,顺带回最新版本与凭证);给 `phoneName` 时查那个电话。 */\nexport async function checkTarget(\n call: CallFace | null,\n target: TargetInfo,\n phoneName?: string,\n): Promise<CallOutcome<CheckOutcome>> {\n const name = phoneName ?? target.phones?.check;\n if (!target.phones || !name) return failure('bad-request', { target: target.key });\n return callManager<CheckOutcome>(call, name, {});\n}\n\n/** 装上缺席的包:版本取自查新版拿到的「最新版本」(宿主用更新包的执行器真装)。 */\nexport async function installAbsent(\n call: CallFace | null,\n target: TargetInfo,\n version: string,\n): Promise<CallOutcome<{ packageName: string; version: string }>> {\n return callManager(call, MANAGER_ACTIONS.install, { packageName: target.packageName, version });\n}\n\n/** 装上更新:查新版 → 提交安装 → 有界轮询查状态到任务收尾;凭证过期重查一次再提交。 */\nexport async function updateInstalled(\n call: CallFace | null,\n target: TargetInfo,\n pollMs: number,\n): Promise<CallOutcome<CheckOutcome>> {\n if (!target.phones) return failure('bad-request', { target: target.key });\n const first = await checkTarget(call, target);\n if (!first.ok) return first;\n let checked = first.value;\n if (!checked.receipt) {\n const code = checked.snapshot.blockedReason ?? 'check-failed';\n return failure(code, { target: target.key }, checked.manual);\n }\n requestSeq += 1;\n const requestId = 'ilife-manager-' + Date.now().toString(36) + '-' + String(requestSeq);\n let submitted = await callManager<CheckOutcome>(call, target.phones.install, {\n checkId: checked.receipt.checkId,\n requestId,\n });\n if (!submitted.ok && submitted.code === 'check-expired') {\n const again = await checkTarget(call, target);\n if (!again.ok) return again;\n checked = again.value;\n if (!checked.receipt) {\n return failure(checked.snapshot.blockedReason ?? 'check-failed', { target: target.key }, checked.manual);\n }\n submitted = await callManager<CheckOutcome>(call, target.phones.install, {\n checkId: checked.receipt.checkId,\n requestId: requestId + 'r',\n });\n }\n if (!submitted.ok) return { ...submitted, manual: submitted.manual ?? checked.manual };\n // 安装是后台跑(`service.ts:434-436`:落盘后后台跑,调用方不干等),故按间隔轮询到任务收尾。\n let latest = submitted.value;\n const interval = pollMs > 0 ? pollMs : FALLBACK_POLL_MS;\n for (let tries = 0; ; tries += 1) {\n const job = latest.snapshot.job;\n if (job === null || (job.state !== 'installing' && job.state !== 'verifying')) break;\n if (tries >= MAX_POLL_TRIES) break;\n await sleep(interval);\n const status = await checkTarget(call, target, target.phones.status);\n if (!status.ok) return status;\n latest = status.value;\n }\n return { ok: true, value: latest };\n}\n","/** 面板的视图逻辑(纯函数:无 DOM、无 node、无 react),客户端与单测共用同一份。\n *\n * 判定「有没有新版」不自己算版本号大小:更新核心已经在快照里给了结论\n * (`canInstall` 为真即有新版可装,`blockedReason` 非空即为什么装不了,见更新包 README 第 8 节),\n * 本文件只把结论翻成三种状态与一句话,不另起一套版本比较。\n */\nimport type { CheckReceipt, BlockedReason, UpdateSnapshot } from 'dsh-plugin-update';\nimport { dualInstallCmd } from './nav.js';\nimport { reasonText } from './update-contract.js';\n\n/** 一个更新目标的面板数据(宿主 `ilife-manager.targets` 的每行)。 */\nexport interface TargetInfo {\n readonly key: string;\n readonly title: string;\n readonly packageName: string;\n readonly phones: { readonly status: string; readonly check: string; readonly install: string } | null;\n /** 宿主启动那一刻磁盘上的版本;null = 启动时这个包还没装。 */\n readonly runningVersion: string | null;\n readonly installedVersion: string | null;\n /** 技能包随插件的那条精确 pin(插件包 manifest 里的 `skill-*` 依赖)。 */\n readonly skill: { readonly packageName: string; readonly version: string } | null;\n}\n\n/** 更新电话查到的结果(`updateCheck` / `updateStatus` 回包体)。 */\nexport interface CheckOutcome {\n readonly snapshot: UpdateSnapshot;\n readonly manual: string | null;\n readonly receipt: CheckReceipt | null;\n}\n\n/** 缺席:宿主启动时没装、现在磁盘上也没有(`runningVersion`/`installedVersion` 都是 null)。 */\nexport function isAbsent(target: TargetInfo): boolean {\n return target.runningVersion === null && target.installedVersion === null;\n}\n\nexport type VerdictKind = 'unknown' | 'up-to-date' | 'update-available' | 'blocked';\n\nexport interface Verdict {\n readonly kind: VerdictKind;\n /** 一句话结论(面板那行正文)。 */\n readonly text: string;\n /** 这一行给不给按钮:缺席给「装上」,有新版给「装上更新」,其余不给。 */\n readonly action: 'install' | 'update' | null;\n}\n\n/** 把快照翻成一行结论:还没查 / 已是最新 / 有新版 / 装不了(附人话原因)。 */\nexport function verdictOf(target: TargetInfo, snapshot: UpdateSnapshot): Verdict {\n const absent = isAbsent(target);\n const latest = snapshot.latestVersion;\n if (absent && latest === null) {\n return { kind: 'unknown', text: '未安装;点「检查更新」查最新版本后装上。', action: null };\n }\n if (absent) {\n return { kind: 'update-available', text: '未安装;最新版本 ' + String(latest) + '。', action: 'install' };\n }\n if (snapshot.blockedReason !== null) {\n const blocked = snapshot.blockedReason as BlockedReason;\n if (blocked === 'pending-restart') {\n return { kind: 'blocked', text: reasonText(blocked), action: null };\n }\n return { kind: 'blocked', text: reasonText(blocked), action: null };\n }\n if (latest === null) {\n return { kind: 'unknown', text: '还没查过最新版本;点「检查更新」。', action: null };\n }\n if (snapshot.canInstall) {\n return { kind: 'update-available', text: '有新版:' + String(latest) + ',点「装上更新」。', action: 'update' };\n }\n return { kind: 'up-to-date', text: '已是最新(' + String(latest) + ')。', action: null };\n}\n\n/** 待重启横幅文案(更新包 README 第 10 节:说清新版号与「重启后才生效」两件事)。 */\nexport function pendingRestartText(target: TargetInfo, snapshot: UpdateSnapshot): string | null {\n if (snapshot.blockedReason !== 'pending-restart') return null;\n const installed = snapshot.installedVersion ?? snapshot.job?.targetVersion ?? null;\n const running = snapshot.runningVersion;\n if (installed === null) return '⚠️ 新版已装好,重启宿主后生效。';\n if (installed === running) return '⚠️ 新版 ' + installed + ' 已装好,重启宿主后生效。';\n return '⚠️ 新版 ' + installed + ' 已装好,正在跑的还是 ' + running + ',重启宿主后生效。';\n}\n\n/** 面板展示的那条可复制命令(用户会照着敲的那一条)。\n *\n * 缺席包给人人都会用的**单命令双包**口径(#674 冻结的装入口径:不带版本号、不带任何开关);\n * 已装包给更新包回包里那条(`--save-exact ... --registry=` 是它 README 第 9 节的冻结合同,\n * 本仓不动它——要改只能提上游)。规范词见 docs/plugins/plugin-manager/t678-缺席即装与检查更新.md。 */\nexport function manualForDisplay(target: TargetInfo, manual: string | null): string | null {\n if (isAbsent(target)) return dualInstallCmd(target.packageName);\n return manual;\n}\n\n/** 版本行两行文字:插件包(本家) + 技能包(随插件,不单独查更新)。 */\nexport function versionLines(target: TargetInfo, snapshot: UpdateSnapshot | null): readonly string[] {\n const running = snapshot?.runningVersion ?? target.runningVersion ?? 'unknown';\n const installed = snapshot?.installedVersion ?? target.installedVersion;\n const lines = [target.packageName + ' ' + running + (installed && installed !== running ? '(磁盘已装 ' + installed + ')' : '')];\n if (target.skill) lines.push(target.skill.packageName + ' ' + target.skill.version + '(随插件,不单独查更新)');\n else lines.push('技能包随插件(不单独查更新)');\n return lines;\n}\n","/** 爱生活面板的更新界面:检查更新按钮、七家结果行、待重启横幅、缺席卡的「装上」。\n *\n * 面板侧纪律(沿 `client.ts` 既有口径):React.createElement 手写(禁 JSX)、内联 style、\n * 颜色走 DSH 主题别名(深浅主题自适应,写死值只做回退)、只用 `dsh-ctx` 镜像内的上下文成员、\n * 不 import 更新包(电话名与轮询间隔由宿主转交,见 `update-contract.ts` 的 `MANAGER_ACTIONS.targets`)。\n *\n * 视觉对照(用户给的第二张截图,参照实现 `dsh-mattpocock-skills-deck/src/client/views/SettingsPage.js:152-168`):\n * 标题右侧并排若干件——本文件出「检查更新」一件,隔壁票(爱生活面板头部与底部)在同一个\n * `actions` 容器里补星与气泡两件,故这里把容器单独留出来。\n */\nimport * as React from 'react';\nimport { checkTarget, installAbsent, loadTargets, updateInstalled } from './update-client.js';\nimport type { CallFace, CallFailure } from './update-client.js';\nimport { isAbsent, manualForDisplay, pendingRestartText, verdictOf, versionLines } from './update-view.js';\nimport type { CheckOutcome, TargetInfo } from './update-view.js';\n\n/** 面板视觉(沿用总管既有语言:内联 style,主题别名带回退)。 */\nexport const PANEL_STYLE = {\n head: { fontSize: 15, fontWeight: 700, margin: '2px 0 8px', color: 'var(--dsw-alias-label-primary, inherit)' } as React.CSSProperties,\n headRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexWrap: 'wrap' } as React.CSSProperties,\n actions: { display: 'inline-flex', alignItems: 'center', gap: 6 } as React.CSSProperties,\n meta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12, lineHeight: 1.7, marginBottom: 10 } 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, 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 color: '#fff',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border, 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 btn: {\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.35))',\n background: 'transparent',\n color: 'var(--dsw-alias-label-primary, inherit)',\n borderRadius: 8,\n padding: '4px 12px',\n fontSize: 13,\n cursor: 'pointer',\n } as React.CSSProperties,\n banner: {\n padding: '10px 12px',\n marginBottom: 10,\n borderRadius: 8,\n border: '1px solid var(--dsw-alias-state-warning-primary, #d8a300)',\n background: 'var(--dsw-alias-bg-base, rgba(216,163,0,.12))',\n color: 'var(--dsw-alias-label-primary, inherit)',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n row: {\n padding: '10px 12px',\n marginBottom: 8,\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.3))',\n fontSize: 13,\n lineHeight: 1.7,\n } as React.CSSProperties,\n rowHead: { fontWeight: 700, marginBottom: 2 } as React.CSSProperties,\n rowMeta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12 } as React.CSSProperties,\n reason: { color: 'var(--dsw-alias-state-error-primary, #ff6b6b)', fontSize: 12, marginTop: 4 } as React.CSSProperties,\n skill: { color: 'var(--dsw-alias-label-tertiary, #8a8a8a)', fontSize: 12, marginTop: 4 } as React.CSSProperties,\n};\n\n/** 一行的运行时状态:还没查 / 查着 / 有结果 / 装着 / 出错。 */\nexport interface UpdateRowState {\n readonly phase: 'idle' | 'checking' | 'ready' | 'installing' | 'failed';\n readonly outcome: CheckOutcome | null;\n readonly failure: CallFailure | null;\n}\n\nexport interface UpdateRowsFace {\n readonly targets: readonly TargetInfo[];\n readonly rows: Readonly<Record<string, UpdateRowState>>;\n readonly pollMs: number;\n readonly loadError: string | null;\n readonly checking: boolean;\n checkAll(): void;\n act(target: TargetInfo): void;\n}\n\nconst IDLE: UpdateRowState = { phase: 'idle', outcome: null, failure: null };\n\n/** 面板的更新状态源:挂载时取七个目标的表,之后按需查/装(组件与按钮共用一个 face)。 */\nexport function useUpdateRows(getCall: () => CallFace | null): UpdateRowsFace {\n const [targets, setTargets] = React.useState<readonly TargetInfo[]>([]);\n const [rows, setRows] = React.useState<Record<string, UpdateRowState>>({});\n const [pollMs, setPollMs] = React.useState(1000);\n const [loadError, setLoadError] = React.useState<string | null>(null);\n const [checking, setChecking] = React.useState(false);\n React.useEffect(() => {\n let alive = true;\n void (async () => {\n const loaded = await loadTargets(getCall());\n if (!alive) return;\n if (loaded.ok) {\n setTargets(loaded.value.targets);\n setPollMs(loaded.value.pollMs);\n } else {\n setLoadError(loaded.message);\n }\n })();\n return () => {\n alive = false;\n };\n }, [getCall]);\n const patch = React.useCallback((key: string, next: UpdateRowState) => {\n setRows((previous) => ({ ...previous, [key]: next }));\n }, []);\n const checkAll = React.useCallback(() => {\n setChecking(true);\n const list = targets;\n for (const target of list) patch(target.key, { phase: 'checking', outcome: rows[target.key]?.outcome ?? null, failure: null });\n void Promise.all(\n list.map(async (target) => {\n const result = await checkTarget(getCall(), target);\n patch(target.key, result.ok ? { phase: 'ready', outcome: result.value, failure: null } : { phase: 'failed', outcome: null, failure: result });\n }),\n ).finally(() => setChecking(false));\n }, [getCall, patch, rows, targets]);\n const act = React.useCallback(\n (target: TargetInfo) => {\n const current = rows[target.key] ?? IDLE;\n patch(target.key, { ...current, phase: 'installing', failure: null });\n void (async () => {\n const absent = isAbsent(target);\n let version = current.outcome?.snapshot.latestVersion ?? null;\n if (absent && version === null) {\n const checked = await checkTarget(getCall(), target);\n if (!checked.ok) {\n patch(target.key, { phase: 'failed', outcome: null, failure: checked });\n return;\n }\n patch(target.key, { phase: 'installing', outcome: checked.value, failure: null });\n version = checked.value.snapshot.latestVersion;\n }\n const result = absent\n ? await installAbsent(getCall(), target, version ?? '')\n : await updateInstalled(getCall(), target, pollMs);\n if (result.ok) {\n // 装完不自己编快照:向宿主问一次真实状态(装到磁盘但宿主还跑着旧版 ⇒ 待重启)。\n const refreshed = await checkTarget(getCall(), target, target.phones?.status);\n patch(target.key, refreshed.ok ? { phase: 'ready', outcome: refreshed.value, failure: null } : { phase: 'failed', outcome: null, failure: refreshed });\n return;\n }\n patch(target.key, {\n phase: 'failed',\n outcome: current.outcome,\n failure: { ...result, manual: result.manual ?? current.outcome?.manual ?? null },\n });\n })();\n },\n [getCall, patch, pollMs, rows],\n );\n return { targets, rows, pollMs, loadError, checking, checkAll, act };\n}\n\n/** 标题右侧的「检查更新」:一个按钮一次查七家(吃 `useUpdateRows` 的表)。 */\nexport function CheckUpdateButton(props: { readonly face: UpdateRowsFace }): React.ReactElement {\n const { checking, checkAll, targets } = props.face;\n const disabled = checking || targets.length === 0;\n return React.createElement(\n 'button',\n {\n type: 'button',\n style: disabled ? { ...PANEL_STYLE.btn, opacity: 0.55, cursor: 'default' } : PANEL_STYLE.btn,\n disabled,\n onClick: checkAll,\n title: '一次查七家(总管自己 + 六个单品)的插件包版本',\n },\n checking ? '检查中…' : '检查更新',\n );\n}\n\n/** 待重启横幅:任何一家成 `pending-restart` 就常驻显示,重启宿主后自动消失(更新包 README 第 10 节)。 */\nfunction RestartBanners(props: { readonly face: UpdateRowsFace }): React.ReactElement | null {\n const lines: string[] = [];\n for (const target of props.face.targets) {\n const outcome = props.face.rows[target.key]?.outcome;\n if (!outcome) continue;\n const text = pendingRestartText(target, outcome.snapshot);\n if (text) lines.push(text);\n }\n if (lines.length === 0) return null;\n return React.createElement(\n 'div',\n { style: PANEL_STYLE.banner },\n lines.map((line, index) => React.createElement('div', { key: String(index) }, line)),\n );\n}\n\n/** 七家结果行:一家一行(版本行两行、结论一句、按钮一个、装不了给原因与手工命令)。 */\nexport function UpdateResults(props: { readonly face: UpdateRowsFace }): React.ReactElement | null {\n const { targets, rows, loadError, checking } = props.face;\n const shown = targets.filter((target) => rows[target.key] && rows[target.key].phase !== 'idle');\n if (loadError) return React.createElement('div', { style: PANEL_STYLE.reason }, '更新能力没接上:' + loadError);\n if (targets.length === 0) return null;\n if (shown.length === 0) return null;\n return React.createElement(\n 'div',\n null,\n React.createElement(RestartBanners, { face: props.face }),\n shown.map((target) => {\n const row = rows[target.key];\n const snapshot = row.outcome?.snapshot ?? null;\n const verdict = snapshot ? verdictOf(target, snapshot) : null;\n const manual = manualForDisplay(target, row.failure?.manual ?? row.outcome?.manual ?? null);\n const busy = row.phase === 'installing';\n return React.createElement(\n 'div',\n { key: target.key, style: PANEL_STYLE.row },\n React.createElement('div', { style: PANEL_STYLE.rowHead }, target.title + ' · ' + target.packageName),\n React.createElement(\n 'div',\n { style: PANEL_STYLE.rowMeta },\n '当前 ' +\n String(snapshot?.runningVersion ?? target.runningVersion ?? '未安装') +\n ' · 最新 ' +\n String(snapshot?.latestVersion ?? '未查'),\n ),\n React.createElement('div', null, busy ? '正在装,请稍候…' : (verdict?.text ?? (checking ? '检查中…' : ''))),\n verdict?.action\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: busy ? { ...PANEL_STYLE.btn, opacity: 0.55 } : { ...PANEL_STYLE.btn, marginTop: 6 },\n disabled: busy,\n onClick: () => props.face.act(target),\n },\n verdict.action === 'install' ? '装上' : '装上更新',\n )\n : null,\n row.failure ? React.createElement('div', { style: PANEL_STYLE.reason }, '装不上:' + row.failure.message) : null,\n versionLines(target, snapshot).map((line, index) =>\n React.createElement('div', { key: String(index), style: PANEL_STYLE.skill }, line),\n ),\n manual ? React.createElement('code', { style: PANEL_STYLE.cmd }, manual) : null,\n );\n }),\n );\n}\n\n/** 缺席卡:一句人话 +「装上」按钮(复制命令降为辅助展示);总管缺席时不显示此卡(面板也看不到)。 */\nexport function AbsentCard(props: {\n readonly target: TargetInfo | null;\n readonly fallbackCommand: string;\n readonly face: UpdateRowsFace;\n}): React.ReactElement {\n const target = props.target;\n const row = target ? props.face.rows[target.key] : null;\n const busy = row?.phase === 'installing';\n const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;\n let reason: string | null = row?.failure?.message ?? null;\n if (target && reason === null && row?.outcome && row.outcome.snapshot.blockedReason !== null) {\n reason = verdictOf(target, row.outcome.snapshot).text;\n }\n return React.createElement(\n 'div',\n { style: PANEL_STYLE.reco },\n React.createElement('div', null, target ? '未安装[' + target.packageName + '],装上后这个页签就能用了。' : '这个页签对应的插件还没装。'),\n target\n ? React.createElement(\n 'button',\n {\n type: 'button',\n style: busy ? { ...PANEL_STYLE.btn, marginTop: 8, opacity: 0.55 } : { ...PANEL_STYLE.btn, marginTop: 8 },\n disabled: busy,\n onClick: () => props.face.act(target),\n },\n busy ? '正在装,请稍候…' : '装上',\n )\n : null,\n reason ? React.createElement('div', { style: PANEL_STYLE.reason }, reason) : null,\n React.createElement('code', { style: PANEL_STYLE.cmd }, manual),\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_TABS, MANAGER_VERSION, MORE_PLUGINS, PANEL_LINKS, recoFor } from './nav.js';\nimport type { ManagerTab } from './nav.js';\nimport { AbsentCard, CheckUpdateButton, UpdateResults, useUpdateRows } from './update-panel.js';\nimport type { CallFace } from './update-client.js';\nimport type {\n ClientCtx,\n ConfigTabRow,\n LifePackSectionProps,\n SlotLedgerEntry,\n} from './dsh-ctx.js';\n\nexport const inject = ['slots', 'connection'];\n\n/** 爱生活页签槽(总管声明的 children,技能设置页注册进来;单段名,避开官方 settings.* 前缀)。 */\nexport const CONFIG_TAB_SLOT = 'ilife.config-tab' as const;\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 meta: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: 12, lineHeight: 1.7, marginBottom: 10 } 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, 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 color: '#fff',\n fontWeight: 700,\n } as React.CSSProperties,\n reco: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px dashed var(--dsw-alias-border, 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, 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};\n\n/** 缺席卡的画法与流程住 `update-panel.ts`(票 #678:一句人话 +「装上」按钮 + 可复制命令),\n * `recoFor` 的补装命令仍是卡里那条辅助展示(B11 口径不变)。 */\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/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */\nfunction LifePackSection(props: LifePackSectionProps & { getCall: () => CallFace | 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 const face = useUpdateRows(props.getCall);\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined);\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 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 function targetForTab(tab: ManagerTab) {\n return face.targets.find((t) => t.packageName === tab.plugin) ?? null;\n }\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 return React.createElement(\n 'div',\n null,\n React.createElement(\n 'div',\n { style: S.headRow },\n React.createElement('div', { style: S.head }, '爱生活'),\n // 三件并排:本票的「检查更新」在左,隔壁票(#679)的星与气泡在右,整组靠右(窄窗口折行)。\n React.createElement(\n 'div',\n { style: S.headActions },\n React.createElement(CheckUpdateButton, { face }),\n React.createElement(PanelActions, null),\n ),\n ),\n React.createElement(\n 'div',\n { style: S.meta },\n React.createElement('div', null, '总开关 · 开关(缺省启用,只读)'),\n React.createElement('div', null, '总管 dsh-life-pack · ' + MANAGER_VERSION),\n ),\n React.createElement(UpdateResults, { face }),\n React.createElement(\n 'div',\n { role: 'tablist', 'aria-label': '爱生活技能页签', style: S.tablist },\n MANAGER_TABS.map((tab, index) => {\n const selected = tab.plugin === active;\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 'aria-selected': selected,\n 'aria-controls': tabsId + '-panel-' + tab.plugin,\n tabIndex: selected ? 0 : -1,\n style: selected ? { ...S.tab, ...S.tabActive } : S.tab,\n onClick: () => {\n setActiveId(tab.plugin);\n },\n onKeyDown: (event: React.KeyboardEvent) => {\n onTabKeyDown(event, index);\n },\n },\n (present.has(tab.plugin) ? '● ' : '○ ') + labelFor(tab),\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 hidden: !selected,\n },\n present.has(tab.plugin)\n ? (props.renderSlot(CONFIG_TAB_SLOT, {}, { only: tab.plugin }) as React.ReactNode)\n : React.createElement(AbsentCard, {\n target: targetForTab(tab),\n fallbackCommand: recoFor(tab).installCmd,\n face,\n }),\n );\n }),\n React.createElement(MorePluginsCard, null),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器:每次取数时现取(connection 后到也不永久缺席),透传给面板组件。\n const getCall = (): CallFace | null => (ctx.connection?.rpc?.call as CallFace | 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 }))\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"],"mappings":"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"}
|
package/dist/nav.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface RecoTab extends ManagerTab {
|
|
|
27
27
|
}
|
|
28
28
|
export declare const MANAGER_PLUGIN: "dsh-life-pack";
|
|
29
29
|
/** 总管版本号(与 package.json 同步,release 时一起 bump;设置页总设置区版本行唯一来源)。 */
|
|
30
|
-
export declare const MANAGER_VERSION: "0.2.
|
|
30
|
+
export declare const MANAGER_VERSION: "0.2.2";
|
|
31
31
|
export declare const DUAL_ADD_PREFIX: "dsh plugin add dsh-life-pack";
|
|
32
32
|
export declare function dualInstallCmd(singlePlugin: string): string;
|
|
33
33
|
/** P3 定案 6 tab(order 升序即 + 号菜单顺序;已打开条不受 order 控制)。 */
|
package/dist/nav.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export const MANAGER_PLUGIN = 'dsh-life-pack';
|
|
15
15
|
/** 总管版本号(与 package.json 同步,release 时一起 bump;设置页总设置区版本行唯一来源)。 */
|
|
16
|
-
export const MANAGER_VERSION = '0.2.
|
|
16
|
+
export const MANAGER_VERSION = '0.2.2';
|
|
17
17
|
export const DUAL_ADD_PREFIX = 'dsh plugin add dsh-life-pack';
|
|
18
18
|
export function dualInstallCmd(singlePlugin) {
|
|
19
19
|
return DUAL_ADD_PREFIX + ' ' + singlePlugin;
|
package/dist/update-view.d.ts
CHANGED
|
@@ -44,6 +44,12 @@ export interface Verdict {
|
|
|
44
44
|
export declare function verdictOf(target: TargetInfo, snapshot: UpdateSnapshot): Verdict;
|
|
45
45
|
/** 待重启横幅文案(更新包 README 第 10 节:说清新版号与「重启后才生效」两件事)。 */
|
|
46
46
|
export declare function pendingRestartText(target: TargetInfo, snapshot: UpdateSnapshot): string | null;
|
|
47
|
+
/** 面板展示的那条可复制命令(用户会照着敲的那一条)。
|
|
48
|
+
*
|
|
49
|
+
* 缺席包给人人都会用的**单命令双包**口径(#674 冻结的装入口径:不带版本号、不带任何开关);
|
|
50
|
+
* 已装包给更新包回包里那条(`--save-exact ... --registry=` 是它 README 第 9 节的冻结合同,
|
|
51
|
+
* 本仓不动它——要改只能提上游)。规范词见 docs/plugins/plugin-manager/t678-缺席即装与检查更新.md。 */
|
|
52
|
+
export declare function manualForDisplay(target: TargetInfo, manual: string | null): string | null;
|
|
47
53
|
/** 版本行两行文字:插件包(本家) + 技能包(随插件,不单独查更新)。 */
|
|
48
54
|
export declare function versionLines(target: TargetInfo, snapshot: UpdateSnapshot | null): readonly string[];
|
|
49
55
|
//# sourceMappingURL=update-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-view.d.ts","sourceRoot":"","sources":["../src/update-view.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"update-view.d.ts","sourceRoot":"","sources":["../src/update-view.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIrF,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtG,sCAAsC;IACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACnF;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CACvC;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAEpD;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,kBAAkB,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,qBAAqB;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;CAC9C;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAuB/E;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CAO9F;AAED;;;;yEAIyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAGzF;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,MAAM,EAAE,CAOnG"}
|
package/dist/update-view.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dualInstallCmd } from './nav.js';
|
|
1
2
|
import { reasonText } from './update-contract.js';
|
|
2
3
|
/** 缺席:宿主启动时没装、现在磁盘上也没有(`runningVersion`/`installedVersion` 都是 null)。 */
|
|
3
4
|
export function isAbsent(target) {
|
|
@@ -40,6 +41,16 @@ export function pendingRestartText(target, snapshot) {
|
|
|
40
41
|
return '⚠️ 新版 ' + installed + ' 已装好,重启宿主后生效。';
|
|
41
42
|
return '⚠️ 新版 ' + installed + ' 已装好,正在跑的还是 ' + running + ',重启宿主后生效。';
|
|
42
43
|
}
|
|
44
|
+
/** 面板展示的那条可复制命令(用户会照着敲的那一条)。
|
|
45
|
+
*
|
|
46
|
+
* 缺席包给人人都会用的**单命令双包**口径(#674 冻结的装入口径:不带版本号、不带任何开关);
|
|
47
|
+
* 已装包给更新包回包里那条(`--save-exact ... --registry=` 是它 README 第 9 节的冻结合同,
|
|
48
|
+
* 本仓不动它——要改只能提上游)。规范词见 docs/plugins/plugin-manager/t678-缺席即装与检查更新.md。 */
|
|
49
|
+
export function manualForDisplay(target, manual) {
|
|
50
|
+
if (isAbsent(target))
|
|
51
|
+
return dualInstallCmd(target.packageName);
|
|
52
|
+
return manual;
|
|
53
|
+
}
|
|
43
54
|
/** 版本行两行文字:插件包(本家) + 技能包(随插件,不单独查更新)。 */
|
|
44
55
|
export function versionLines(target, snapshot) {
|
|
45
56
|
const running = snapshot?.runningVersion ?? target.runningVersion ?? 'unknown';
|
package/dist/update-view.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-view.js","sourceRoot":"","sources":["../src/update-view.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-view.js","sourceRoot":"","sources":["../src/update-view.ts"],"names":[],"mappings":"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"}
|