@konnga/dsh-hud 0.9.2 → 0.10.0

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/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["React","React","React","React","React","React","React"],"sources":["../src/client/styles.ts","../src/client/shared.ts","../src/client/format.ts","../src/client/hooks/use-store.ts","../src/client/components/dock-pill.ts","../src/client/components/session-changes.ts","../src/client/dom.ts","../src/client/components/change-list.ts","../src/client/components/commit-list.ts","../src/client/components/commit-pane.ts","../src/client/components/count-chips.ts","../src/client/components/image-diff.ts","../src/client/components/diff-view.ts","../src/client/components/tag-list.ts","../src/client/hooks/panel.ts","../src/client/components/floating-panel.ts","../src/client/components/pill-tip.ts","../src/client/components/sidebar.ts","../src/client/components/turn-images.ts","../src/client/composer.ts","../src/client/i18n.ts","../src/client/store/attribution.ts","../src/client/store/commits.ts","../src/client/store/diff.ts","../src/client/store/image.ts","../src/client/store/session-info.ts","../src/client/store/tip.ts","../src/client/store/tools.ts","../src/client/store/index.ts","../src/client/styles-apply.ts","../src/client/plugin.ts"],"sourcesContent":["/**\n * 面板与胶囊的样式表。\n *\n * 放在数组里逐段拼(不是模板字符串),是为了每段都能单独读懂;\n * `ensureStyles()` 把它插进 `<head>`,用 `CSS_TAG_ID` 防重复注入。\n */\n\n/**\n * 只覆盖本插件自己的类名,配色一律走主题 CSS 变量,亮/暗主题自动跟随。\n * 不碰 document.body、window 或产品的 DOM 选择器。\n */\nexport const CSS = [\n // dock 座位给的是整栏宽的一行,而输入卡片有自己的最大宽度并居中\n // (--dsh-composer-card-max-width)。外层容器复刻框架自带 dock 占位元素\n // (队列公告)的几何,胶囊才会真正贴在输入卡片上方左端,而不是飘到栏的最左。\n '.dshh-dock{box-sizing:border-box;display:flex;align-items:center;gap:6px;justify-content:flex-start;',\n 'width:calc(100% - var(--dsh-composer-side-clearance,16px) - var(--dsh-composer-side-clearance,16px)',\n ' - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'max-width:calc(var(--dsh-composer-card-max-width,952px) - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'margin:0 auto;}',\n '.dshh-pill{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.6;',\n 'width:fit-content;max-width:100%;',\n 'display:inline-flex;align-items:center;gap:4px;',\n 'padding:2px 8px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:999px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-pill:hover{color:var(--dsw-alias-label-primary,#e5e7eb);border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n // 会话计时胶囊:只读读数,所以指针不该是\"可点\";数字用等宽数字,跳动时宽度不抖。\n '.dshh-timer{cursor:default;gap:5px;}',\n // 悬停气泡:`pointer-events:none` 保证它永远不挡指针;`pre-wrap` 保留调用方给的多行结构。\n '.dshh-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:420px;padding:6px 10px;border-radius:8px;' +\n 'background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,#2c2c2e));' +\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));' +\n 'box-shadow:0 6px 20px rgba(0,0,0,0.28);color:var(--dsw-alias-label-primary,#f9fafb);' +\n 'font-size:11.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;pointer-events:none;}',\n '.dshh-timerGlyph{flex:none;opacity:.85;}',\n '.dshh-timerValue{font-variant-numeric:tabular-nums;}',\n '.dshh-panel{color:var(--dsw-alias-label-primary,#e5e7eb);font-size:12.5px;line-height:1.6;}',\n '.dshh-header{display:flex;align-items:center;gap:8px;padding:6px 8px;flex:none;cursor:move;line-height:1.4;',\n 'user-select:none;touch-action:none;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-title{font-size:13px;font-weight:600;flex:none;}',\n '.dshh-branch{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;padding:1px 6px;border-radius:6px;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-spacer{flex:1;}',\n // 固定盒模型:⟳/⌖/✕ 这类符号可能落到行高夸张的 fallback 字体上,\n // 不锁死宽高就会把标题栏整个撑高(面板上方出现一片空白)。\n '.dshh-icon{font:inherit;font-size:12px;line-height:1;width:22px;height:22px;padding:0;border-radius:6px;',\n 'display:inline-flex;align-items:center;justify-content:center;flex:none;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-icon:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-body{padding:10px;display:flex;flex-direction:column;gap:8px;overflow:auto;}',\n '.dshh-meta{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;}',\n // 不用 direction:rtl 做左省略号:它会把绝对路径开头的 “/” 重排到末尾。\n '.dshh-repo{font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-kv{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sectionTitle{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 计数芯片之间要留出横向间隔:11.5px 字号的描边胶囊挨在一起时看着是贴住的。\n '.dshh-chips{display:flex;flex-wrap:wrap;gap:6px 12px;}',\n '.dshh-chip{display:inline-flex;align-items:center;font-size:11.5px;padding:1px 8px;border-radius:999px;border:1px solid currentColor;}',\n '.dshh-clean{font-size:11.5px;color:var(--dsw-alias-state-success-primary,#3fb950);}',\n // 会话归因:概览下面那行 + 「只看本会话改的」开关 + 清单行首的标记。\n '.dshh-session{display:flex;align-items:baseline;gap:6px 10px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sessionText{min-width:0;}',\n '.dshh-sessionToggle{font:inherit;font-size:11px;padding:0 6px;border-radius:999px;white-space:nowrap;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.35));background:transparent;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-sessionToggle:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));',\n 'color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-sessionToggle:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:1px;}',\n '.dshh-sessionToggleOn{border-color:var(--dsw-alias-brand-primary,#4b8bf5);',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-marker{flex:none;width:8px;text-align:center;font-size:9px;line-height:inherit;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-markerPossible{color:var(--dsw-alias-state-warn-primary,#d9a63c);opacity:.75;}',\n // 行尾的审阅标记:`○` 未审 / `✓` 这一版已审 / `✓`(黄) 改动已变。\n // `⋯` 菜单里那条只读摘要:像一条说明,不像一个可点的菜单项。\n '.dshh-menureadout{display:flex;flex-direction:column;gap:2px;padding:4px 10px 6px;',\n 'font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary,#8a8f98));',\n 'cursor:default;user-select:text;max-width:260px;}',\n '.dshh-menureadoutLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 悬停卡片里那块详情:跟着卡片自己的字号,只做行距与弱化。\n '.dshh-rowhover{display:flex;flex-direction:column;gap:2px;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);min-width:0;}',\n '.dshh-rowhoverLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 「已审 x/y」是个可点控件:同样不能用 label-dimmed(反相表面色,1.26:1 / 1.64:1),\n // 平时用次级文字色,悬停才升到正文色。\n '.dshh-review{flex:none;margin-left:auto;padding:0 2px;font-size:10.5px;cursor:pointer;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-review:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-reviewOn{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-reviewStale{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-ctag{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n // 行首的 `○`(与本会话无关):要\"最不显眼\",但同样得看得见 —— 与上面同一个理由。\n '.dshh-markerOther{color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 工具活动胶囊:与 Git 胶囊同形,但它是只读读数,指针不该是“可点”。\n // 与 codex-hud 对齐的几支色(它的默认配色表:git=magenta、gitBranch=cyan、project=yellow):\n // 产品没有青/品红的语义变量(只有 button-info-*,那是按钮用的),所以这两支写死值,\n // 但都走变量、可被覆盖。\n '.dshh-workspace{color:var(--dsw-alias-state-warn-primary,#d9a63c);flex:none;}',\n '.dshh-gitWrap{color:var(--dsh-hud-magenta,#d946ef);flex:none;}',\n '.dshh-branch{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-ahead{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-workspaceSep{color:var(--dsw-alias-label-tertiary,rgba(138,143,152,0.6));flex:none;}',\n '.dshh-toolsPill{cursor:default;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 配色对齐 codex-hud 的 activity-lines:只有状态符上色,名字/次数/分隔符都用正文色。\n '.dshh-toolsGlyph{flex:none;}',\n '.dshh-toolsGlyphOk{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-toolsGlyphRun{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-toolsGlyphBad{color:var(--dsw-alias-state-error-primary,#f06a6a);}',\n // 唯一一处写死的颜色:codex-hud 把\"正在跑的工具名\"画成青色,产品没有对应的语义变量\n // (只有 button-info-*,那是按钮用的)。留 `--dsh-hud-tool-name` 作为覆盖钩子。\n '.dshh-toolsName{color:var(--dsh-hud-cyan,#22d3ee);}',\n // 技能名与工具名同色系但不同支:工具那支是青,技能走品牌色(一眼能分开)。\n '.dshh-skillName{color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-section{display:flex;flex-direction:column;gap:4px;}',\n '.dshh-list{display:flex;flex-direction:column;gap:1px;max-height:30vh;overflow:auto;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 改动行是按钮(点击看差异),所以要把浏览器的按钮默认样式清掉。\n '.dshh-row{display:flex;align-items:baseline;gap:8px;padding:2px 3px;border-radius:4px;',\n 'font:inherit;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-row:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-row:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n '.dshh-status{flex:none;width:10px;text-align:center;font-family:ui-monospace,Consolas,monospace;font-weight:600;}',\n '.dshh-path{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 和改动清单一样:标题下面一条分隔线(最近提交是最后一段,用它把两段分开)。\n '.dshh-commits{display:flex;flex-direction:column;gap:1px;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 提交行是按钮(点击看这次提交改了什么),因此要清掉浏览器默认的按钮样式。\n '.dshh-commit{display:flex;align-items:baseline;gap:8px;padding:1px 2px;border-radius:4px;font-size:11.5px;',\n 'font:inherit;font-size:11.5px;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-commit:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-commit:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // 打开着的那条提交(明细在右栏,左栏只高亮它)。\n '.dshh-commitActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n // 中间那一栏(提交明细,默认 240px):元信息头 + 这次提交的改动清单。\n // 与左栏同形:各自 border-right 分隔,内部自己滚。\n // 注意 flex 是 `0 1 auto` 而不是 `none`:窗口太窄时由它和差异栏一起被压缩,\n // 而不是把左栏挤出去(左栏是主面板,保持固定 380px)。\n '.dshh-commitpane{flex:0 1 auto;box-sizing:border-box;display:flex;flex-direction:column;',\n 'min-width:0;min-height:0;overflow:hidden;border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitHead{flex:none;display:flex;flex-direction:column;gap:2px;padding:6px 8px;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitTitle{display:flex;align-items:baseline;gap:6px;min-width:0;}',\n '.dshh-commitSubject{font-size:12.5px;line-height:1.5;color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-commitMeta{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 清单吃掉这一栏剩下的高度(标题跟着一起滚,栏本身不滚动)。\n '.dshh-commitFiles{flex:1 1 auto;min-height:0;overflow:auto;padding:6px 8px 8px;}',\n '.dshh-commitFiles .dshh-list{max-height:none;}',\n '.dshh-csha{flex:none;font-family:ui-monospace,Consolas,monospace;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-csubject{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-chead{flex:none;font-size:10px;line-height:1.4;padding:0 4px;border-radius:4px;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);border:1px solid currentColor;}',\n '.dshh-cwhen{flex:none;font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-note{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-empty{padding:14px 12px;font-size:12px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 图片预览(二进制改动) ----\n '.dshh-imgWrap{flex:1 1 auto;min-height:0;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:8px;}',\n '.dshh-imgMeta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgDims{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-imgBytes{font-family:ui-monospace,Consolas,monospace;}',\n '.dshh-imgPair{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap;}',\n '.dshh-imgPairSingle{flex-wrap:nowrap;}',\n // 固定档位(非\"适应宽度\")时:图片按固有像素算宽度,容器随内容撑开,外层横向滚动。\n '.dshh-imgPairZoomed{flex-wrap:nowrap;width:max-content;align-items:flex-start;}',\n '.dshh-imgPairZoomed .dshh-imgCell{flex:0 0 auto;}',\n '.dshh-imgPairZoomed .dshh-img{max-height:none;}',\n '.dshh-imgZoom{display:inline-flex;align-items:center;gap:2px;margin-left:auto;}',\n '.dshh-imgZoomBtn{border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;cursor:pointer;font:inherit;',\n 'min-width:22px;height:20px;padding:0 6px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;}',\n '.dshh-imgZoomBtn:hover{color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n '.dshh-imgZoomValue{min-width:74px;font-variant-numeric:tabular-nums;}',\n '.dshh-imgCell{flex:1 1 150px;min-width:0;display:flex;flex-direction:column;gap:4px;}',\n '.dshh-imgLabel{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgNote{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);padding:6px 0;}',\n // 透明像素要有底色才看得出边界:棋盘格由四段半透明渐变拼出来,两种主题都不用另配颜色。\n '.dshh-img{max-width:100%;max-height:420px;object-fit:contain;border-radius:6px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.28));',\n 'background-image:linear-gradient(45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(-45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(45deg,transparent 75%,rgba(127,127,127,0.12) 75%),',\n 'linear-gradient(-45deg,transparent 75%,rgba(127,127,127,0.12) 75%);',\n 'background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0;}',\n // ---- 轮尾「本轮图片改动」条 ----\n '.dshh-imgstrip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripTitle{flex:none;}',\n '.dshh-imgstripItems{display:flex;gap:6px;flex-wrap:wrap;}',\n '.dshh-imgstripItem{display:inline-flex;align-items:center;gap:2px;padding:2px 4px 2px 2px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:8px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);}',\n '.dshh-imgstripOpen{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;',\n 'color:inherit;cursor:pointer;padding:0;font:inherit;}',\n '.dshh-imgstripOpen:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-imgstripThumb{width:28px;height:28px;object-fit:cover;border-radius:5px;display:block;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));}',\n '.dshh-imgstripThumbEmpty{align-items:center;justify-content:center;font-size:8px;letter-spacing:0.02em;',\n 'display:flex;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripName{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-imgstripTier{flex:none;opacity:.7;}',\n '.dshh-imgstripCompare{border:0;background:transparent;color:inherit;cursor:pointer;padding:0 2px;',\n 'font:inherit;opacity:.7;}',\n '.dshh-imgstripCompare:hover{opacity:1;color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-imgstripMore{flex:none;}',\n '.dshh-footer{display:flex;justify-content:space-between;gap:8px;padding:6px 10px;flex:none;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));',\n 'font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 差异视图 ----\n '.dshh-workbench{display:flex;flex:1 1 auto;min-height:0;align-items:stretch;}',\n '.dshh-leftpane{flex:none;box-sizing:border-box;display:flex;flex-direction:column;min-height:0;overflow:hidden;',\n 'border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-leftpane .dshh-list{max-height:none;}',\n '.dshh-rowActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));}',\n // 键盘选中的那行:选过还没打开时只它一个亮着,所以用描边而不是底色,\n // 与 .dshh-rowActive(正在看的那份差异)区分得开。\n '.dshh-rowCursor{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // hunk 头可点(折叠 / 展开),并显示\"当前 hunk\"——键盘 n/p 与 Space 作用在它身上。\n '.dshh-difrow-hunk{cursor:pointer;user-select:none;}',\n '.dshh-difrow-hunk:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));}',\n '.dshh-difrow-hunkActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-hunkMark{text-align:right;}',\n '.dshh-grip{position:absolute;top:0;right:0;bottom:0;width:6px;cursor:col-resize;z-index:5;}',\n // 栏间那条抓握**必须占宽**:它是提交栏与差异栏之间的真实一格。绝对定位会让它贴到面板右边缘,\n // 与外面那条叠在一起(面板右边缘那条不占宽,靠宽度公式里预留的 6px 让位)。\n '.dshh-gripBetween{position:static;flex:none;width:6px;}',\n '.dshh-grip:hover{background:var(--dsw-alias-border-l2,rgba(127,127,127,0.35));}',\n '.dshh-diffview{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;}',\n '.dshh-toolbar{display:flex;align-items:center;gap:4px;padding:5px 8px;flex:none;overflow:hidden;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-difpath{font-family:ui-monospace,Consolas,monospace;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:38%;}',\n '.dshh-difcounts{display:inline-flex;gap:6px;flex:none;font-family:ui-monospace,Consolas,monospace;font-size:11px;}',\n '.dshh-tab{font:inherit;font-size:11.5px;padding:2px 7px;border-radius:6px;flex:none;cursor:pointer;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tab:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-tabActive{color:var(--dsw-alias-label-primary,#e5e7eb);background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.18));}',\n '.dshh-diff{flex:1 1 auto;min-height:0;overflow:auto;padding:4px 0 8px;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.5;}',\n '.dshh-difrow{display:flex;align-items:flex-start;white-space:pre;}',\n '.dshh-difrow-hunk{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-difrow-meta{color:var(--dsw-alias-label-secondary,#8a8f98);opacity:.85;}',\n '.dshh-difno{flex:none;width:46px;text-align:right;padding-right:8px;user-select:none;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);font-variant-numeric:tabular-nums;}',\n '.dshh-diftext{flex:1 1 auto;min-width:0;white-space:pre;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-diftext.dshh-wrap{white-space:pre-wrap;word-break:break-word;overflow:visible;text-overflow:clip;}',\n].join('')\n","/**\n * 浏览器半的**内核**:常量、存储助手、诊断探针,以及几个到处都用得着的纯函数。\n *\n * 它是最底层 —— store 各片、hook、组件都从这里 import,它自己不 import 任何本包模块,\n * 于是依赖方向永远是\"由外向内\",不会绕成环。\n */\n\nimport React from 'react'\n\nexport var h = React.createElement\n\n/* --------------------------------------------------------------- 领域形状 */\n\n/**\n * 下面这些 interface 是**浏览器半读到的线路形状**(宿主半 `src/host/types.ts` 的镜像)。\n *\n * 为什么集中放在这里:store 各片、hook、组件都从 shared import;类型只写一份,\n * 字段漂移才能一眼看见。宿主半与浏览器半各自打包,不能互相 import 类型,所以\n * 这是刻意的\"同形两份\",改线路形状时两边一起改。\n */\n\n/** 改动类型的八个键(与宿主半 `counts` 的键一致)。 */\nexport type HudCountKey =\n | 'modified'\n | 'added'\n | 'deleted'\n | 'renamed'\n | 'copied'\n | 'typeChanged'\n | 'untracked'\n | 'conflicted'\n\n/** git status 的分组计数(porcelain-v2 的八类)。 */\nexport interface HudStatusCounts {\n modified: number\n added: number\n deleted: number\n renamed: number\n copied: number\n typeChanged: number\n untracked: number\n conflicted: number\n}\n\n/** 改动清单里的一条:porcelain-v2 的两个字母 + 路径。 */\nexport interface HudChange {\n /** 展示用字母:优先 index 侧,index 为 `.` 时取 worktree 侧。 */\n status: string\n /** index 侧字母(未跟踪/未合并的记录是 `.` / `!`)。 */\n index?: string\n /** worktree 侧字母。 */\n worktree?: string\n /** 展示用路径;重命名/复制是 `旧路径 → 新路径`。 */\n path: string\n /** 新路径那一侧(git 命令当 pathspec 用)。 */\n file: string\n /** 重命名/复制的原路径;普通改动没有这个字段。 */\n origFile?: string | null\n}\n\n/** 文件 → “这一版”签名(`size.mtimeMs`,或 `missing`)。 */\nexport type ReviewSignatures = Record<string, string>\n\n/** `status` 端点的结果(工作区概览 + 改动清单)。 */\nexport interface HudStatus {\n /** 仓库根;不在仓库里时为 null。 */\n repo: string | null\n /** 当前分支(detached 时为短 oid);空仓库里也为 null。 */\n branch: string | null\n detached: boolean\n /** 短 oid(7 位);还没有提交时为 null。 */\n oid: string | null\n upstream: string | null\n ahead: number\n behind: number\n dirty: boolean\n counts: HudStatusCounts\n changes: HudChange[]\n truncated: boolean\n /** 「审阅进度」要的“这一版”签名;只在客户端明确要的时候才附上。 */\n review?: ReviewSignatures\n}\n\n/** diff 行的种类:增 / 删 / 上下文 / hunk 头 / git 自己的文件头。 */\nexport type DiffRowKind = 'add' | 'del' | 'ctx' | 'hunk' | 'meta'\n\n/** 差异里的一行。 */\nexport interface DiffRow {\n kind: DiffRowKind\n text: string\n /** 旧侧行号;新增行与 hunk/meta 为 null。 */\n oldLine: number | null\n /** 新侧行号;删除行与 hunk/meta 为 null。 */\n newLine: number | null\n}\n\n/** 一侧 diff 的解析结果。 */\nexport interface DiffSide {\n rows: DiffRow[]\n additions: number\n deletions: number\n hunks: number\n binary: boolean\n truncated: boolean\n}\n\n/** 工作区差异端点载荷:一个改动文件的两侧(工作区 / 已暂存)。 */\nexport interface WorktreeDiffPayload {\n repo: string | null\n path: string\n origPath: string | null\n /** 工作区里的未跟踪文件(`--no-index` 那条分支)。 */\n untracked: boolean\n worktree: DiffSide\n index: DiffSide\n}\n\n/** 右栏差异的 `data`:工作区差异是两侧,提交差异是单侧。 */\nexport type DiffPayload = WorktreeDiffPayload | DiffSide\n\n/** 差异的两种来源。 */\nexport type DiffSource = 'worktree' | 'commit'\n\n/** 差异的三种“侧”。 */\nexport type DiffSideName = 'worktree' | 'index' | 'commit'\n\n/** 右侧差异视图的状态(`state.diff`)。 */\nexport interface HudDiffState {\n source: DiffSource\n phase: string\n sha?: string\n shortSha?: string\n subject?: string\n path: string\n file: string\n origPath: string | null\n data: DiffPayload | null\n side: DiffSideName\n wrap?: boolean\n zoom?: number | null\n hunk?: number\n collapsed?: number[]\n errorCode?: string | null\n}\n\n/** 一侧图片可以预览:内容以 base64 附带,尺寸认出来才给。 */\nexport interface ImagePreview {\n kind: 'image'\n mime: string\n bytes: number\n width: number | null\n height: number | null\n base64: string\n}\n\n/** 一侧图片超过单侧上限:只报字节数,不塞内容。 */\nexport interface ImageTooLarge {\n kind: 'too-large'\n bytes: number\n limit: number\n}\n\n/** 一侧内容认不出是图片。 */\nexport interface ImageNotImage {\n kind: 'not-image'\n bytes: number\n}\n\n/** 一侧不存在(新文件还没有索引副本 / 文件被删掉)。 */\nexport interface ImageMissing {\n kind: 'missing'\n}\n\n/** 一侧内容的可预览状态。 */\nexport type ImageSide = ImagePreview | ImageTooLarge | ImageNotImage | ImageMissing\n\n/** `image` 端点载荷:一个二进制(图片)改动的两侧字节与像素尺寸。 */\nexport interface ImagePayload {\n repo: string | null\n /** 提交明细时才有(工作区/索引视图没有)。 */\n shortSha?: string\n path: string\n origPath: string | null\n side: string\n sha: string | null\n before: ImageSide\n after: ImageSide\n}\n\n/** `image` 请求:缓存键按这几个字段算。 */\nexport interface ImageRequest {\n cwd: string\n path: string\n origPath?: string | null\n side?: string | null\n sha?: string | null\n}\n\n/** 「本会话确定改过」的一个文件。 */\nexport interface SessionChange {\n path: string\n file: string\n writes: number\n edits: number\n firstAt: number\n lastAt: number\n lastTurn: number | null\n turns: number[]\n}\n\n/** 「可能相关」的一个路径(命令旁听来的启发式)。 */\nexport interface SessionChangeCandidate {\n path: string\n file: string\n hits: number\n firstAt: number\n lastAt: number\n reason: string\n lastTurn: number | null\n}\n\n/** 本会话跑过的一条 shell 命令。 */\nexport interface SessionCommand {\n tool: string\n command: string\n at: number\n turn: number | null\n failed: boolean\n readOnly: boolean\n}\n\n/** 「会话归因」这一片 store 的状态(`state.sessionChanges`)。 */\nexport interface SessionChangesState {\n phase: string\n /** `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)。 */\n files: Record<string, SessionChange> | null\n possible: Record<string, SessionChangeCandidate> | null\n commands: SessionCommand[]\n since: number | null\n dropped: number\n possibleDropped: number\n errorCode: string | null\n}\n\n/** 「第几轮 → 那一轮的请求预览」(`state.turns`,宿主 `turnOutline` 的归一结果)。 */\nexport interface TurnsState {\n available: boolean\n prompts: Record<number, string>\n latest: number | null\n}\n\n/** 归因与 git 现状核对后的视图(`sessionChangeView()` 的结果)。 */\nexport interface SessionChangeView {\n /** 本会话改过、且工作区里还有差异。 */\n mine: Record<string, SessionChange>\n /** 命令旁听出来的「可能相关」(只标没被确定认领的)。 */\n possible: Record<string, SessionChangeCandidate>\n turnPrompts: Record<number, string>\n latestTurn: number | null\n latestCount: number\n /** 第几轮 → 那一轮改了几个文件。 */\n byTurn: Record<number, number>\n mineCount: number\n possibleCount: number\n commandCount: number\n writingCommands: number\n commands: SessionCommand[]\n /** 本会话改过、但工作区里已经没有差异的文件。 */\n gone: string[]\n /** 本会话记录过的文件总数。 */\n tracked: number\n since: number | null\n dropped: number\n possibleDropped: number\n /** 工作区里有差异、但不是本会话动的条数。 */\n others: number\n}\n\n/** 审阅标记:这一版审过了(fresh)/ 之后又变了(stale)。 */\nexport type ReviewMark = 'fresh' | 'stale'\n\n/** 「审阅进度」视图(`reviewView()` 的结果)。 */\nexport interface ReviewView {\n marks: Record<string, ReviewMark>\n known: Record<string, true>\n done: number\n total: number\n staleCount: number\n}\n\n/** 存档里一个\"已审\"标记。 */\nexport interface ReviewedEntry {\n signature: string\n at: number\n}\n\n/** 存档:`工作区 + NUL + 文件 → { signature, at }`。 */\nexport type ReviewedMap = Record<string, ReviewedEntry>\n\n/** 面板位置。 */\nexport interface PanelPos {\n left: number\n top: number\n}\n\n/** 右栏里打开着哪几栏(`panelLayout()` 的结果)。 */\nexport interface PanelLayout {\n diff: boolean\n commit: boolean\n}\n\n/** 本地 UI 记忆(`dsh-hud.ui.v1`):只记界面开关与布局,不混业务数据。 */\nexport interface UiPrefs {\n onlySession?: boolean\n onlyLatest?: boolean\n open?: boolean\n pos?: PanelPos | null\n wrap?: boolean\n zoom?: number | null\n diffWidth?: number\n commitWidth?: number\n}\n\n/** 悬停气泡快照(`state.tip`)。 */\nexport interface TipState {\n key: string\n text: string\n rect: { left: number; top: number; right: number }\n}\n\n/** 最近提交里的一行(`log` 端点载荷里的一条)。 */\nexport interface HudLogEntry {\n sha: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n subject: string\n}\n\n/** 提交元信息(`commit` 端点载荷里的 `commit`)。 */\nexport interface HudCommitSummary {\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n}\n\n/** 提交明细里的一个改动文件(`git diff --name-status -z` 的一条)。 */\nexport interface HudCommitFile {\n status: string\n path: string\n file: string\n origFile?: string | null\n}\n\n/** 提交明细(右栏视图)的状态(`state.commit`)。 */\nexport interface HudCommitState {\n phase: string\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n files: HudCommitFile[]\n truncated: boolean\n errorCode: string | null\n}\n\n/** 标签列表里的一条(`tags` 端点里的一条)。 */\nexport interface HudTag {\n name: string\n sha: string\n annotated: boolean\n date: string\n committedAt: number | null\n subject: string\n}\n\n/** 正在跑的工具(胶囊显示实时秒数用:秒数由客户端本地计时)。 */\nexport interface HudToolActive {\n callId: string | null\n name: string\n summary: string | null\n startedAt: number\n}\n\n/** 最近一次工具调用。 */\nexport interface HudToolRecent {\n name: string\n summary: string | null\n ok: boolean\n at: number\n ms: number | null\n}\n\n/** 某个工具跑了多少次、最近一次成功没有。 */\nexport interface HudToolCount {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 某个技能用了几次、最近一次成功没有。 */\nexport interface HudToolSkill {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 「工具活动」这一片 store 的状态(`state.tools`)。 */\nexport interface ToolsState {\n phase: string\n active: HudToolActive | null\n recent: HudToolRecent[]\n counts: HudToolCount[]\n skills: HudToolSkill[]\n skillsDropped: number\n total: number\n updatedAt: number | null\n}\n\n/** `session-time` 端点载荷:创建时刻 + 累计墙钟与轮次/步数。 */\nexport interface HudSessionTime {\n available: boolean\n sessionId?: string | null\n createdAt?: number | null\n turns?: number | null\n steps?: number | null\n llmMs?: number | null\n toolMs?: number | null\n now?: number\n}\n\n/** 会话时长快照(`state.sessionTime`):`sessionId` 用来判断\"这份数还是不是这个会话的\"。 */\nexport interface SessionTimeState {\n sessionId: string | null\n value: HudSessionTime | null\n}\n\n/** `prompt-history` 端点载荷。 */\nexport interface HudPromptHistory {\n available: boolean\n sessionId?: string | null\n prompts: string[]\n}\n\n/** prompt 历史快照(`state.promptHistory`)。 */\nexport interface PromptHistoryState {\n sessionId: string | null\n value: HudPromptHistory | null\n}\n\n/** 侧栏每条会话行要的摘要(`session-statuses` 里的一行)。 */\nexport interface HudSessionOverviewRow {\n sessionId: string\n cwd: string | null\n /** 确定改过的文件数(归因记录)。 */\n mine: number\n /** “可能相关”的历史候选路径数。 */\n possible: number\n /** “可能相关”里此刻仍然有差异的个数。 */\n possibleNow: number\n repo: boolean\n error: string | null\n /** 该工作区的 git 摘要还没算出来。 */\n pending?: boolean\n branch?: string | null\n detached?: boolean\n dirty?: boolean\n ahead?: number\n behind?: number\n counts?: HudStatusCounts\n}\n\n/** 「侧栏摘要」这一片 store 的状态(`state.rows`)。 */\nexport interface RowsState {\n phase: string\n byId: Record<string, HudSessionOverviewRow>\n unavailable: boolean\n truncated: boolean\n updatedAt: number | null\n}\n\n/** 紧凑计数简写(胶囊与侧栏 chip 共用)。 */\nexport interface CountShorthand {\n key: HudCountKey\n mark: string\n value: number\n}\n\n/** 侧栏一枚状态 chip 的\"事实摘要\"(`rowSummaryOf()` 的结果)。 */\nexport interface RowSummary {\n row: HudSessionOverviewRow\n mine: number\n possibleNow: number\n possible: number\n shorthands: CountShorthand[]\n changed: number\n dirty: boolean\n hasContent: boolean\n}\n\n/** 浏览器半共享 store 的快照。 */\nexport interface HudState {\n phase: string\n sessionId: string | null\n cwd: string | null\n status: HudStatus | null\n errorCode: string | null\n updatedAt: number | null\n panelOpen: boolean\n pos: PanelPos | null\n /** 胶囊所在 dock 行的视口顶部坐标。 */\n anchorTop: number | null\n diff: HudDiffState | null\n commit: HudCommitState | null\n /** 键盘高亮的那个文件(清单里的 `file`)。 */\n cursor: string | null\n commits: HudLogEntry[] | null\n commitsOid: string | null\n tags: HudTag[] | null\n tagsAt: number\n tagsTruncated: boolean\n diffWidth: number\n commitWidth: number\n sessionChanges: SessionChangesState\n onlySession: boolean\n onlyLatest: boolean\n /** 这一版每个改动文件的签名(`file → signature`)。 */\n review: ReviewSignatures\n tools: ToolsState\n sessionTime: SessionTimeState\n promptHistory: PromptHistoryState\n tip: TipState | null\n rows: RowsState\n reviewed: ReviewedMap\n turns: TurnsState\n}\n\n/**\n * 共享 store 的对外接口(hook / 组件只依赖它,不依赖 `createStore` 的实现)。\n *\n * 只列浏览器半真正用到的那些;方法参数用线路形状(`HudChange` / `ImageRequest` …),\n * 这样 hook 里传参也一起被检查到。\n */\nexport interface HudStore {\n getSnapshot(): HudState\n subscribe(listener: () => void): () => void\n bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void\n refresh(): Promise<unknown>\n openPanel(open: boolean): void\n togglePanel(): void\n movePanel(pos: PanelPos): void\n commitPanel(pos: PanelPos): void\n resetPanel(): void\n setAnchorTop(top: number | null): void\n setDiffWidth(width: number): void\n setCommitWidth(width: number): void\n persistLayout(): void\n openDiff(change: HudChange): Promise<unknown>\n loadImagePair(request: ImageRequest): Promise<unknown>\n cachedImagePair(request: ImageRequest): unknown\n loadSessionChanges(): Promise<unknown>\n loadSessionTime(sessionId: string): Promise<unknown>\n loadPromptHistory(sessionId: string): Promise<unknown>\n showTip(\n key: string,\n text: string,\n rect: { left: number; top: number; right: number } | null,\n ): void\n hideTip(key?: string): void\n tipHandlers(key: string, textOf: () => string): Record<string, unknown>\n closeDiff(): void\n closeDetail(): void\n setDiffSide(side: string): void\n toggleDiffWrap(): void\n setZoom(value: number | null): void\n openCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown>\n openCommitDiff(file: HudCommitFile): Promise<unknown>\n toggleCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown> | void\n closeCommit(): void\n stepFile(delta: number, open: boolean): HudChange | null\n openCursor(): HudChange | null\n stepHunk(delta: number): void\n toggleHunk(index: number | undefined): void\n toggleOnlySession(): void\n toggleOnlyLatest(): void\n refreshRows(): Promise<unknown>\n loadTools(): Promise<unknown>\n toggleReviewed(file: string): void\n clearReviewed(): void\n}\n\n/** 取词函数(框架把 `t` 交给座位;取不到会回落成键本身)。 */\nexport type Translate = (key: string, params?: Record<string, string>) => string\n\n/** 诊断探针(`createProbe()` 的返回)。 */\nexport interface Probe {\n log(reason: string, detail?: Record<string, unknown>): void\n}\n\n/* --------------------------------------------------------------- 常量 */\n\nexport var CHANNEL = '/dsh-hud-rpc'\nexport var NS = 'dshHud'\n\nexport var UI_KEY = 'dsh-hud.ui.v1'\n/**\n * 插件自诊断的留存键(最近 24 行):会话归因每一步的事实都往这里留一行。\n *\n * 为什么落在这里:客户端插件的异常与\"服务找不到\"都不会进宿主日志,界面出问题时往往只有\n * 有/没有两种观察;把每一步的事实留一行在 localStorage,用户不用开 DevTools 也能把它捞出来,\n * 排查完可以随手删掉。\n */\nexport var DEBUG_KEY = 'dsh-hud.debug'\n\n/** 轮询间隔:面板与胶囊共用,页面不可见时跳过。 */\nexport var POLL_MS = 8000\n/**\n * 侧栏行内 chip 的轮询节奏:比面板慢一档。\n *\n * 那些 chip 是\"扫一眼\"的信息(哪个会话脏了),不值得跟面板一样频繁;宿主侧还按目录\n * 缓存 + 去重,多会话共用一个目录只跑一次 git。\n */\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\nexport var ROWS_POLL_MS = 30000\n/**\n * 工具活动的节拍。比面板那 8s 密得多:宿主这一端是**内存读**(不碰 git、不跑子进程),\n * 密一点才能让\"正在跑什么\"真的看到;代价用\"内容没变就不 set\"抵掉。\n */\nexport var TOOLS_POLL_MS = 3000\n/** 胶囊上最多铺几个技能名(其余收成 `+k`,完整清单在悬停里)。 */\nexport var SKILLS_SHOWN = 2\n/** 面板默认宽度与“停靠在输入框上方”的锚点高度。 */\nexport var PANEL_WIDTH = 380\nexport var COMPOSER_OFFSET = 168\n\nexport var VIEWPORT_MARGIN = 8\n\n/** 超过这个位移才算拖动,避免把头部上的普通点击当成拖动。 */\nexport var DRAG_THRESHOLD_PX = 3\n/** 面板与胶囊所在 dock 行之间的间距。 */\nexport var PANEL_DOCK_GAP = 8\n/**\n * 中间那一栏(打开提交明细时:这次提交的元信息 + 改动清单)的宽度。\n * 它夹在左栏与差异栏之间,是固定的——右边缘那个抓握仍只调整差异栏。\n */\nexport var COMMIT_LIST_WIDTH = 240\n/** 提交明细栏的最小宽度(它装的是文件路径,不能太窄;默认 240 只是起点,可拖)。 */\nexport var COMMIT_MIN_WIDTH = 180\n\n/** diff 模式的面板:更宽,且用固定高度(避免 bottom 锚定下\"内容变高就跳位\")。 */\n/** 右栏(差异)默认宽度、下限,以及面板右边缘抓握的宽度。 */\nexport var DIFF_DEFAULT_WIDTH = 470\nexport var DIFF_MIN_WIDTH = 240\n\nexport var RESIZE_GRIP_WIDTH = 6\n\nexport var DIFF_PANEL_HEIGHT_VH = 0.72\n\n/** 改动类型的固定展示顺序,与宿主半的 counts 键一致。 */\nexport var COUNT_ORDER: HudCountKey[] = [\n 'modified',\n 'added',\n 'deleted',\n 'renamed',\n 'copied',\n 'typeChanged',\n 'untracked',\n 'conflicted',\n]\n\n/** 每类改动对应的主题色令牌(都带回退色,暗亮色主题都能看)。 */\nexport var COUNT_COLOR: Record<HudCountKey, string> = {\n modified: 'var(--dsw-alias-state-warn-primary, #d9a63c)',\n added: 'var(--dsw-alias-state-success-primary, #3fb950)',\n deleted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n // 与 codex-hud 的 `statEntries` 一一对应:R→cyan、C→brightBlue(我们的品牌蓝就是这个位)、\n // T→magenta、?→灰。M/A/D/! 本来就一致(yellow/green/red/critical)。\n // `?` 用 `label-secondary` 而不是 `label-dimmed`:后者是**反相的表面色**(浅 #e1e5ee、深 #43454a),\n // 当文字压在面板底色上只有 1.26:1 / 1.64:1 —— 看不见(issue:未跟踪计数读不出来)。\n // secondary 仍是中性灰、比彩色状态弱一档,但浅 5.8:1 / 深 10.4:1 都读得清。\n renamed: 'var(--dsh-hud-cyan, #22d3ee)',\n copied: 'var(--dsw-alias-brand-primary, #4b8bf5)',\n typeChanged: 'var(--dsh-hud-magenta, #d946ef)',\n untracked: 'var(--dsw-alias-label-secondary, #8a8f98)',\n conflicted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n}\n\n/**\n * 每个改动类型在胶囊里的简写符,沿用 codex-hud / 上游 statusline 的 git 标记约定:\n * M 已修改、A 已暂存新增、D 已删除、R 重命名、C 复制、T 类型改变、? 未跟踪、! 冲突。\n */\nexport var COUNT_MARK: Record<HudCountKey, string> = {\n modified: 'M',\n added: 'A',\n deleted: 'D',\n renamed: 'R',\n copied: 'C',\n typeChanged: 'T',\n untracked: '?',\n conflicted: '!',\n}\n\n/** 胶囊里分支名的显示上限;超出就中段省略,别把简写符挤出可见区。 */\nexport var PILL_BRANCH_MAX = 28\n/** 胶囊里工作区名最多几个字符(它在分支前面,不该抢分支的位置)。 */\nexport var PILL_WORKSPACE_MAX = 20\n\n/**\n * 差异行的底色:半透明,压在面板表面色之上,因此亮/暗主题都可读\n * (文本与行号仍走主题令牌)。\n */\nexport var DIFF_ADD_BG = 'rgba(63,185,80,0.12)'\nexport var DIFF_DEL_BG = 'rgba(240,106,106,0.13)'\n\nexport var DIFF_GUTTER_COLOR = 'var(--dsw-alias-label-secondary, #8a8f98)'\n\n/** prompt 历史的条数上限(与宿主半的 `PROMPT_HISTORY_MAX` 同一个数)。 */\nexport var PROMPT_HISTORY_MAX = 100\n\n/**\n * 时长格式化:**与 codex-hud 同一套口径**。\n *\n * 不足一分钟给 `<1m`;然后按 `Xd Yh Zm` 只列非零项(`45m` / `2h 14m` / `3d 2h`)。\n * 取的是 floor —— 计时器宁可比真实少一点,也不要\"明明刚过 1 分钟就写 2m\"。\n */\nexport function formatDuration(ms: unknown) {\n var safe = typeof ms === 'number' && isFinite(ms) ? Math.max(0, ms) : 0\n if (safe < 60000) return '<1m'\n var minutes = Math.floor(safe / 60000)\n var days = Math.floor(minutes / 1440)\n var hours = Math.floor((minutes % 1440) / 60)\n var rest = minutes % 60\n var parts = []\n if (days > 0) parts.push(String(days) + 'd')\n if (hours > 0) parts.push(String(hours) + 'h')\n if (rest > 0) parts.push(String(rest) + 'm')\n return parts.join(' ')\n}\n\n/** 固定缩放档位:与内置文档预览的菜单一致(25% / 50% / 100% / 150% / 200%)。 */\nexport var ZOOM_STEPS = [0.25, 0.5, 1, 1.5, 2]\n\n/** 落盘/读回的缩放值:只认档位里的数,其他一律当\"适应宽度\"。 */\nexport function readZoom(ui: UiPrefs | null | undefined): number | null {\n return ui && typeof ui.zoom === 'number' && ZOOM_STEPS.indexOf(ui.zoom) >= 0 ? ui.zoom : null\n}\n\n/**\n * 走一档缩放。\n *\n * `null`(适应宽度)出发:`+` 从 100% 开始、`−` 从 50% 开始 —— 和\"适应宽度\"的直觉一致,\n * 不用先经过 25% 才知道按了有反应。\n */\nexport function stepZoom(current: number | null | undefined, direction: number): number {\n if (current === null || current === undefined) return direction > 0 ? 1 : 0.5\n var index = ZOOM_STEPS.indexOf(current)\n if (index < 0) return direction > 0 ? 1 : 0.5\n var next = index + (direction > 0 ? 1 : -1)\n if (next < 0 || next >= ZOOM_STEPS.length) return current\n return ZOOM_STEPS[next]\n}\n\nexport function readUi(): UiPrefs {\n try {\n var raw = window.localStorage.getItem(UI_KEY)\n if (!raw) return {}\n // localStorage 是不可信输入(旧版本写的、手改过的都有可能):先当 unknown,收窄后再当 UiPrefs。\n var parsed: unknown = JSON.parse(raw)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as UiPrefs) : {}\n } catch (error) {\n return {}\n }\n}\n\nexport function writeUi(next: UiPrefs): void {\n try {\n window.localStorage.setItem(UI_KEY, JSON.stringify(next))\n } catch (error) {\n // 隐私模式等写入失败只影响“记住位置/展开状态”,不影响功能。\n }\n}\n\n/** 「审阅进度」的存档键:按\"工作区 + 文件\"记下审过的那一版签名。 */\nexport var REVIEWED_KEY = 'dsh-hud.reviewed.v1'\n/**\n * 存档键的分隔符:NUL。\n *\n * 用 `String.fromCharCode(0)` 而不是 `\"\\u0000\"` 字面量:路径里不可能有 NUL,而转义写法\n * 在源码与测试里极易被多转一层(写成字面量反斜杠),排查起来极其费劲。\n */\nexport var REVIEW_KEY_SEP = String.fromCharCode(0)\n\n/** 存档键:`工作区 NUL 文件`(同一个文件名在别的仓库里是另一码事)。 */\nexport function reviewKeyOf(cwd: string | null | undefined, file: string): string {\n return String(cwd || '') + REVIEW_KEY_SEP + file\n}\n\n/** 存档上限:超出丢最久没动的那些(不然用久了这个键会一直长)。 */\nexport var REVIEWED_MAX = 200\n\n/** 读取\"我审过哪些版本\":坏数据一律当空表(存档不该把插件带崩)。 */\nexport function readReviewed(): ReviewedMap {\n try {\n var raw = window.localStorage.getItem(REVIEWED_KEY)\n if (!raw) return {}\n // 与 readUi 同理:localStorage 里的 JSON 是不可信输入,逐步收窄而不是直接当目标类型用。\n var parsed: unknown = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n var source = parsed as Record<string, unknown>\n var out: ReviewedMap = {}\n for (var key in source) {\n var item = source[key]\n if (!Object.prototype.hasOwnProperty.call(source, key) || !item || typeof item !== 'object')\n continue\n // 同一系统边界:条目也是不可信 JSON,先收成表再逐个字段判断。\n var entry = item as Record<string, unknown>\n if (typeof entry.signature !== 'string') continue\n out[key] = { signature: entry.signature, at: typeof entry.at === 'number' ? entry.at : 0 }\n }\n return out\n } catch (error) {\n return {}\n }\n}\n\n/** 写回\"我审过哪些版本\":超上限就按最后触达时间丢最旧的。 */\nexport function writeReviewed(next: ReviewedMap): ReviewedMap {\n try {\n var keys = Object.keys(next)\n if (keys.length > REVIEWED_MAX) {\n keys.sort(function (left: string, right: string) {\n return (next[left].at || 0) - (next[right].at || 0)\n })\n var trimmed: ReviewedMap = {}\n var keep = keys.slice(keys.length - REVIEWED_MAX)\n for (var index = 0; index < keep.length; index += 1) trimmed[keep[index]] = next[keep[index]]\n next = trimmed\n }\n window.localStorage.setItem(REVIEWED_KEY, JSON.stringify(next))\n return next\n } catch (error) {\n // 写不进去只意味着\"这次不记住\",不影响功能。\n return next\n }\n}\n\n/* --------------------------------------------------------------- store */\n\n/**\n * 插件自诊断的现场记录:只写控制台,不碰界面、不留全局。\n *\n * 客户端插件的异常和\"服务找不到\"都不会进宿主日志,所以控制台是排查时唯一的通道。\n * 同一个 reason + 同样的事实只报一次(轮询每 8s 会重试),一行 JSON 便于复制。\n */\nexport function createProbe(): Probe {\n var seen: Record<string, true> = {}\n return {\n /** 记一行现场:一行进控制台,同时留一份在 localStorage(都按 reason+事实去重)。 */\n log: function (reason: string, detail?: Record<string, unknown>) {\n var facts = detail === undefined ? {} : detail\n var line = reason + ' ' + JSON.stringify(facts)\n if (seen[line] === true) return\n seen[line] = true\n console.warn('[dsh-hud] ' + line)\n try {\n var history = window.localStorage.getItem(DEBUG_KEY)\n var parsed: unknown = history === null ? [] : JSON.parse(history)\n var list: Array<{ at: number; line: string }> = Array.isArray(parsed) ? parsed : []\n list.push({ at: Date.now(), line: line })\n window.localStorage.setItem(DEBUG_KEY, JSON.stringify(list.slice(-24)))\n } catch (error) {\n // 隐私模式等写不进去只影响这行诊断,不影响功能。\n }\n },\n }\n}\n\n/**\n * 面板左栏那份工作区清单 —— **渲染与键盘导航共用这一份**。\n *\n * 过滤必须发生在一个地方:键盘选中用的是行下标,如果渲染时过滤了而导航没过滤,\n * ↑/↓ 就会落在看不见的行上。\n */\nexport function visibleChangeRows(state: HudState): HudChange[] {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var onlySession = state.onlySession === true\n var onlyLatest = state.onlyLatest === true\n if (!onlySession && !onlyLatest) return changes\n var view = sessionChangeView(state)\n // 归因还没到手时这些开关不该悄悄把清单清空:那时候它们还没有意义。\n if (view === null) return changes\n // 别名:`view` 被闭包捕获后 TS 不再保留\"非 null\"的收窄,这里先取出两份表。\n var mineOf = view.mine\n var possibleOf = view.possible\n var rows = changes\n if (onlySession) {\n // \"可能相关\"(命令旁听)也算本会话碰过的:过滤器的承诺是\"只看本会话动过的\"。\n rows = rows.filter(function (change) {\n return mineOf[change.file] !== undefined || possibleOf[change.file] !== undefined\n })\n }\n var latestTurn = view.latestTurn\n if (onlyLatest && latestTurn !== null) {\n // \"这一轮改了什么\":只留最近一轮确定动过的(\"可能相关\"没有可靠的回合归属,不收)。\n rows = rows.filter(function (change) {\n var record = mineOf[change.file]\n return record !== undefined && record.lastTurn === latestTurn\n })\n }\n return rows\n}\n\n/** 进程内唯一一份探针:两个座位共用,去重也一起算。 */\nexport var probe: Probe = createProbe()\n\n/**\n * 两个 sha 说的是不是同一次提交。\n *\n * 最近提交那份清单里是 **短 sha**,而提交明细里是宿主半解析回来的**完整 sha**,\n * 直接 `===` 永远配不上;按前缀对一下(git 自己认 rev 也是这个语义)。\n */\nexport function sameCommit(left: unknown, right: unknown): boolean {\n if (typeof left !== 'string' || typeof right !== 'string' || left === '' || right === '')\n return false\n return left === right || right.indexOf(left) === 0 || left.indexOf(right) === 0\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/** 图片请求的缓存键:把请求的每个字段都带上,免得两侧共用一个键。 */\nexport function imagePairKey(request: ImageRequest): string {\n return [\n request.cwd || '',\n request.path || '',\n request.origPath || '',\n request.side || '',\n request.sha || '',\n ].join('\\u0000')\n}\n\n/** 字节数:十进制 KB/MB,和文件管理器里看到的一致。 */\nexport function formatBytes(bytes: unknown): string {\n if (typeof bytes !== 'number' || !isFinite(bytes) || bytes < 0) return '?'\n if (bytes < 1000) return String(bytes) + ' B'\n if (bytes < 1000 * 1000) return (bytes / 1000).toFixed(1) + ' KB'\n return (bytes / 1000 / 1000).toFixed(2) + ' MB'\n}\n\n/** 图片类文件的扩展名。这里只做粗筛 —— 真正\"是不是图\"由宿主按文件头判断。 */\nexport var IMAGE_FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'avif']\n\n/* ------------------------------------------------------- 悬浮面板 */\n\n/**\n * 当前右栏里有哪几栏 —— 样式、夹位置、渲染三处必须用同一份判断,抽出来免得漂移。\n *\n * @returns `{ diff, commit }`:`diff` = 有工作区/提交差异,`commit` = 打开着提交明细。\n */\nexport function panelLayout(state: HudState): PanelLayout {\n return {\n diff: state.diff !== null && typeof state.diff === 'object',\n commit: state.commit !== null && typeof state.commit === 'object',\n }\n}\n\n/**\n * 面板宽度:单栏紧凑;打开右栏就按\"左栏 + [提交明细栏] + 抓握 + 差异栏\"算。\n *\n * 右栏(差异)宽度由面板右边缘的抓握拖动调整并记忆;中间那栏是固定宽度。\n * 视口不够宽时只把弹性的一栏(差异)压小——左栏与提交明细栏是有下限的固定栏。\n *\n * @param layout - `panelLayout()` 的结果。\n * @param diffWidth - 差异栏的目标宽度。\n */\nexport function panelWidthFor(\n layout: PanelLayout,\n diffWidth?: number | null,\n commitWidth?: number | null,\n): number {\n var hasCommit = layout.commit === true\n var hasDiff = layout.diff === true\n if (!hasCommit && !hasDiff) return PANEL_WIDTH\n // **逐栏钳到自己的下限**:只对总宽加一个 floor 是不够的 —— 存档里写着 40px 时,\n // 总宽仍然会按 40 算(floor 只保证\"不小于最小总和\",但差额会被算到别的栏头上)。\n var middle = hasCommit\n ? Math.max(COMMIT_MIN_WIDTH, typeof commitWidth === 'number' ? commitWidth : COMMIT_LIST_WIDTH)\n : 0\n var right = hasDiff\n ? Math.max(DIFF_MIN_WIDTH, typeof diffWidth === 'number' ? diffWidth : DIFF_DEFAULT_WIDTH)\n : 0\n // 两条抓握:面板右边缘那条(调差异栏;只有提交栏时调提交栏)—— 它是绝对定位、**不占宽**,\n // 但宽度里要预留 6px 让位,否则它压在内容上;栏间那条是真实的一格,占宽。\n var outerGrip = RESIZE_GRIP_WIDTH\n var innerGrip = hasCommit && hasDiff ? RESIZE_GRIP_WIDTH : 0\n var available = window.innerWidth - 2 * VIEWPORT_MARGIN\n return Math.min(PANEL_WIDTH + middle + innerGrip + right + outerGrip, available)\n}\n\n/** 把位置夹回视口内,避免拖到屏幕外再也点不到。 */\nexport function clampPos(left: number, top: number, width?: number | null): PanelPos {\n var panelWidth = typeof width === 'number' ? width : PANEL_WIDTH\n var maxLeft = Math.max(VIEWPORT_MARGIN, window.innerWidth - panelWidth - VIEWPORT_MARGIN)\n var maxTop = Math.max(VIEWPORT_MARGIN, window.innerHeight - 48)\n return {\n left: Math.min(Math.max(VIEWPORT_MARGIN, left), maxLeft),\n top: Math.min(Math.max(VIEWPORT_MARGIN, top), maxTop),\n }\n}\n\n/**\n * 量出胶囊所在 dock 行的视口顶部坐标。\n *\n * 这里用**自己的** `data-dsh-hud` 标记,不碰产品的 DOM 选择器。dsh-git 把\n * \"输入框堆叠高 168px\"写成了常量并注明那只是一次测量;直接量真实位置可以同时\n * 适配 hero(空白会话,输入框居中)与 active 两种布局。\n *\n * @returns 视口坐标的顶部像素,量不到时返回 null。\n */\n/**\n * 面板当前该有多宽:从 store **现读**(模式切换会改宽度,别用可能过期的渲染闭包)。\n *\n * 原来它是组件里的 `panelWidthOf(store)`,被\"视口夹取\"和\"拖动\"两处用;提成顶层\n * 纯函数后两边共用同一份判断。\n */\nexport function panelWidthOf(store: HudStore): number {\n var snapshot = store.getSnapshot()\n return panelWidthFor(panelLayout(snapshot), snapshot.diffWidth, snapshot.commitWidth)\n}\n\n/** 自有样式表的稳定 tag id:`data-plugin-css` 用它做幂等插入。 */\nexport var CSS_TAG_ID = 'dsh-hud/styles.css'\n\n/* --------------------------------------------------- 会话归因(对账) */\n\n/**\n * 把\"本会话改过哪些文件\"(宿主半按 fs 事件记的)与 git 现状对起来。\n *\n * - `mine`:本会话改过、且工作区里还有差异(`status.changes` 里能找到)\n * - `gone`:本会话改过、但工作区里已经没有差异(被改回去了 / 被别的会话或你自己覆盖了)\n * - `others`:工作区里有差异、但不是本会话动的\n *\n * 名字能直接对上:宿主报的是 cwd 之内的 posix 相对路径,`status.changes[].file` 也是。\n * 还拿不到归因(老宿主 / 请求还没回来)时返回 `null` —— 界面这时**不打任何标记**,\n * 而不是把所有改动都标成\"别人的\"。\n */\nexport function sessionChangeView(state: HudState): SessionChangeView | null {\n // 快照可能来自老宿主/旧存档:这里保留\"缺字段就当没有\"的老行为,不让界面崩。\n var slice: SessionChangesState | null | undefined = state.sessionChanges\n if (slice === null || slice === undefined) return null\n var files = slice.files\n if (files === null || files === undefined) return null\n var possibleAll: Record<string, SessionChangeCandidate> | null =\n slice.possible === undefined ? null : slice.possible\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var mine: Record<string, SessionChange> = {}\n var possible: Record<string, SessionChangeCandidate> = {}\n var gone: string[] = []\n var index\n var change\n // 先认\"确定改过\"的:一档优先于一档,同一个文件不会被标两次。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (Object.prototype.hasOwnProperty.call(files, change.file))\n mine[change.file] = files[change.file]\n }\n // 再看\"可能相关\":只标没有被确定认领的那些(命令旁听来的,置信度低一档)。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (mine[change.file] !== undefined) continue\n if (possibleAll !== null && Object.prototype.hasOwnProperty.call(possibleAll, change.file)) {\n possible[change.file] = possibleAll[change.file]\n }\n }\n for (var file in files) {\n if (Object.prototype.hasOwnProperty.call(files, file) && mine[file] === undefined)\n gone.push(file)\n }\n gone.sort()\n var commands: SessionCommand[] = Array.isArray(slice.commands) ? slice.commands : []\n var turns =\n state.turns !== undefined && state.turns !== null && state.turns.available === true\n ? state.turns\n : null\n var latestTurn = turns === null ? null : turns.latest\n var byTurn: Record<number, number> = {}\n var latestCount = 0\n for (var mineFile in mine) {\n if (!Object.prototype.hasOwnProperty.call(mine, mineFile)) continue\n var turn = typeof mine[mineFile].lastTurn === 'number' ? mine[mineFile].lastTurn : null\n if (turn === null) continue\n byTurn[turn] = (byTurn[turn] === undefined ? 0 : byTurn[turn]) + 1\n if (latestTurn !== null && turn === latestTurn) latestCount += 1\n }\n return {\n mine: mine,\n possible: possible,\n /** 第几轮 → prompt 预览(来自 `turnOutline` 投影;没有就是空表)。 */\n turnPrompts: turns === null ? {} : turns.prompts,\n latestTurn: latestTurn,\n latestCount: latestCount,\n byTurn: byTurn,\n mineCount: Object.keys(mine).length,\n possibleCount: Object.keys(possible).length,\n /** 跑过的命令里\"看着会写文件\"的条数(含失败的,界面自己再分)。 */\n commandCount: commands.length,\n writingCommands: commands.filter(function (item) {\n return item && item.readOnly !== true\n }).length,\n commands: commands,\n gone: gone,\n /** 本会话记录过的文件总数(宿主那边的集合大小)。 */\n tracked: Object.keys(files).length,\n since: slice.since === undefined ? null : slice.since,\n dropped: slice.dropped === undefined ? 0 : slice.dropped,\n possibleDropped: slice.possibleDropped === undefined ? 0 : slice.possibleDropped,\n others: changes.length - Object.keys(mine).length,\n }\n}\n\nexport function reviewView(state: HudState): ReviewView {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var signatures: ReviewSignatures =\n state.review && typeof state.review === 'object' ? state.review : {}\n var reviewed = state.reviewed && typeof state.reviewed === 'object' ? state.reviewed : {}\n var prefix = reviewKeyOf(state.cwd, '')\n var marks: Record<string, ReviewMark> = {}\n var known: Record<string, true> = {}\n var done = 0\n var total = 0\n var staleCount = 0\n for (var index = 0; index < changes.length; index += 1) {\n var file = changes[index].file\n var signature = typeof signatures[file] === 'string' ? signatures[file] : null\n if (signature === null) continue\n known[file] = true\n total += 1\n var mark = reviewed[prefix + file]\n if (mark === undefined) continue\n if (mark.signature === signature) {\n marks[file] = 'fresh'\n done += 1\n } else {\n marks[file] = 'stale'\n staleCount += 1\n }\n }\n return { marks: marks, known: known, done: done, total: total, staleCount: staleCount }\n}\n","/**\n * 展示用的格式化:分支名、工作区名、相对时间、计数简写。\n *\n * 都是纯函数(输入数据、输出字符串),所以可以直接单测,也让组件里不再夹带这类判断。\n */\n\nimport type {\n CountShorthand,\n HudCountKey,\n HudSessionOverviewRow,\n HudStatusCounts,\n RowSummary,\n Translate,\n} from './shared.js'\nimport { COUNT_MARK, COUNT_ORDER, PILL_BRANCH_MAX, PILL_WORKSPACE_MAX } from './shared.js'\n/** 从中间省略:保留头尾(路径/分支名两头的区分度都比中间高)。 */\nexport function middleEllipsis(name: string, max: number): string {\n if (name.length <= max) return name\n var head = Math.ceil((max - 1) / 2)\n var tail = max - 1 - head\n return name.slice(0, head) + '…' + name.slice(name.length - tail)\n}\n\n/**\n * 工作区名 = 会话 cwd 的目录名(侧栏给工作区起的名字就是它)。\n *\n * 取的是 **cwd** 而不是仓库顶层:会话开在子目录时,用户认的是那个子目录;\n * 完整路径在悬停与面板里,胶囊上只要一个能认出来的短名字。\n */\nexport function workspaceNameOf(cwd: string | null | undefined): string | null {\n if (typeof cwd !== 'string' || cwd.length === 0) return null\n var trimmed = cwd.replace(/[/\\\\]+$/, '')\n if (trimmed.length === 0) return null\n var index = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n var name = index === -1 ? trimmed : trimmed.slice(index + 1)\n if (name.length === 0) return null\n return middleEllipsis(name, PILL_WORKSPACE_MAX)\n}\n\n/** 分支名过长时中段省略,保留头尾信息量。 */\nexport function shortBranch(name: string): string {\n return middleEllipsis(name, PILL_BRANCH_MAX)\n}\n\n/**\n * 改动计数 → 简写符列表 `[{ key, mark, value }]`(只列非零项,顺序固定)。\n *\n * 胶囊与侧栏行内 chip 共用这一份:两处各写一套算法一定会漂。\n */\nexport function countShorthands(counts: HudStatusCounts | null | undefined): CountShorthand[] {\n var out: CountShorthand[] = []\n var source: Partial<HudStatusCounts> = counts || {}\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = source[key] || 0\n if (value <= 0) continue\n out.push({ key: key, mark: COUNT_MARK[key], value: value })\n }\n return out\n}\n\nexport function branchLabel(\n status: { branch?: string | null; oid?: string | null } | null | undefined,\n t: Translate,\n): string {\n if (!status) return '—'\n if (status.branch) return status.branch\n if (status.oid) return t('panel.detached') + ' ' + status.oid\n return t('panel.detached')\n}\n\nexport function timeLabel(at: number): string {\n var date = new Date(at)\n var pad = function (value: number) {\n return value < 10 ? '0' + String(value) : String(value)\n }\n return pad(date.getHours()) + ':' + pad(date.getMinutes()) + ':' + pad(date.getSeconds())\n}\n\n/* --------------------------------------------------------- 胶囊(dock) */\n\n/**\n * 会话作用域的座位:框架给出 `sessionId`,`useSessions` 快照给出该会话的 `cwd`,\n * 因此它是唯一能完成绑定的座位。\n */\n/**\n * 侧栏每条会话行上的状态 chip(座位 `sidebar.workspaces.session.row.action`)。\n *\n * 一行只有 32px 高、悬停才露出来,所以只放**最该一眼看到的两件事**:这个工作区脏不脏\n * (git 简写符)与**这个会话自己改过几个文件**(`●n`,只我们有这个信息)。干净且本会话\n * 没改过就整枚不画 —— 不占位置、也不制造噪音。完整信息在悬停里。\n */\n/**\n * 一枚侧栏状态的\"事实摘要\":三种呈现(常亮标记 / 悬停动条 / 卡片详情)共用这一份计算。\n *\n * 同一份数据要在三个地方以三种宽度呈现,各写一套一定会漂 —— 所以抽成一处。\n */\nexport function rowSummaryOf(row: HudSessionOverviewRow | null | undefined): RowSummary | null {\n if (row === undefined || row === null || row.pending === true) return null\n var mineCount = typeof row.mine === 'number' ? row.mine : 0\n var possibleCount = typeof row.possible === 'number' ? row.possible : 0\n var shorthands: CountShorthand[] = row.repo === true ? countShorthands(row.counts) : []\n var changed = 0\n for (var index = 0; index < shorthands.length; index += 1) changed += shorthands[index].value\n return {\n row: row,\n mine: mineCount,\n possibleNow: typeof row.possibleNow === 'number' ? row.possibleNow : 0,\n possible: possibleCount,\n shorthands: shorthands,\n changed: changed,\n dirty: row.repo === true && row.dirty === true,\n hasContent: changed > 0 || mineCount > 0 || possibleCount > 0 || row.error !== null,\n }\n}\n\n/** 相对时间;超过 30 天直接给提交自己的日期。 */\nexport function commitWhen(\n commit: { committedAt?: number | null; date?: string | null },\n t: Translate,\n): string {\n if (typeof commit.committedAt !== 'number') return commit.date || ''\n return fullWhen(commit.committedAt)\n}\n\n/**\n * 时刻一律给完整年月日 + 时分(本地时区):`2026-10-16 14:18`。\n *\n * 两级简化都被否掉了:先是从\"2 小时前\"改成具体时间(相对时间在对账场景不够用),\n * 再是\"今天只给 `14:18`\"也被否 —— 用户要**年月日都在**。所以不再分档,一视同仁。\n * 只到分钟:提交/标签不需要秒,列一排在面板里也塞得下。\n */\nexport function fullWhen(at: number): string {\n var then = new Date(at)\n return (\n String(then.getFullYear()) +\n '-' +\n pad2(then.getMonth() + 1) +\n '-' +\n pad2(then.getDate()) +\n ' ' +\n pad2(then.getHours()) +\n ':' +\n pad2(then.getMinutes())\n )\n}\n\nexport function pad2(value: number): string {\n return value < 10 ? '0' + String(value) : String(value)\n}\n\n/** porcelain-v2 的展示字母 → counts 键(用于配色)。 */\nexport function statusKey(letter: string): HudCountKey {\n if (letter === 'M') return 'modified'\n if (letter === 'A') return 'added'\n if (letter === 'D') return 'deleted'\n if (letter === 'R') return 'renamed'\n if (letter === 'C') return 'copied'\n if (letter === 'T') return 'typeChanged'\n if (letter === '?') return 'untracked'\n if (letter === '!') return 'conflicted'\n return 'typeChanged'\n}\n\n/** 宿主半的 `details.code` → 本座位的本地化文案(store 只存机器码)。 */\nexport function errorText(code: string | null | undefined, t: Translate): string {\n if (code === 'timeout') return t('err.timeout')\n if (code === 'no-cwd') return t('err.no-cwd')\n if (code === 'git-missing') return t('err.git-missing')\n if (code === 'invalid-cwd') return t('err.invalid-cwd')\n if (code === 'transport') return t('err.transport')\n if (code === 'invalid-path') return t('err.invalid-path')\n // 提交可能在两次请求之间被 rebase / amend 换掉:给一句能操作的提示,而不是\"git 执行失败\"。\n if (code === 'unknown-commit') return t('commit.unknown')\n // 宿主半是启动时加载的:只刷新页面时,新端点还不存在,这个提示能省下一轮排查。\n if (code === 'unknown-endpoint') return t('err.stale-host')\n return t('err.git-failed')\n}\n","/**\n * 订阅共享 store 的经典写法(不依赖种子里的 `useSyncExternalStore` 构建)。\n */\n\nimport React from 'react'\n\nimport type { HudState, HudStore } from '../shared.js'\n\n/* ---------------------------------------------------------- 订阅 hook */\n\n/** 订阅共享 store 的经典写法(不依赖种子里的 useSyncExternalStore 构建)。 */\nexport function useStore(store: HudStore): HudState {\n var pair = React.useState(function () {\n return store.getSnapshot()\n })\n React.useEffect(\n function () {\n return store.subscribe(function () {\n pair[1](store.getSnapshot())\n })\n },\n [store],\n )\n return pair[0]\n}\n","/**\n * 输入框上方那一行里的胶囊:分支摘要 / 非 git 仓库,点击开关面板;\n * 含两枚工具胶囊、技能清单与悬停气泡的锚点。\n */\n\nimport React from 'react'\n\nimport { branchLabel, countShorthands, fullWhen, shortBranch, workspaceNameOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudState,\n HudStatusCounts,\n HudStore,\n HudToolActive,\n ToolsState,\n Translate,\n} from '../shared.js'\nimport {\n COUNT_COLOR,\n COUNT_ORDER,\n SKILLS_SHOWN,\n formatDuration,\n h,\n probe,\n sessionChangeView,\n} from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\nexport interface SessionSummaryLike {\n cwd?: string | null\n /** 这个会话被哪些视图保留着;`mainView` 为 0 时说明它在内嵌的子会话里。 */\n retainedBy?: { mainView?: number | null } | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\nexport interface SessionsSnapshotLike {\n byId?: Record<string, SessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\nexport type UseSessions = <T>(selector: (snapshot: SessionsSnapshotLike) => T) => T\n\n/** `HudDockPill` 的 props:store + 框架注入的会话作用域信息。 */\nexport interface HudDockPillProps {\n t?: Translate\n /** 当前会话(座位按会话作用域注入)。 */\n sessionId?: string | null\n useSessions: UseSessions\n store: HudStore\n /** 测试/调试用:固定\"现在\",省得等本地计时器。 */\n now?: number\n}\n\n/** 工具胶囊的选项。 */\nexport interface ToolPillOptions {\n t: Translate\n store: HudStore\n now: number\n}\n\n/** 计时胶囊的选项。 */\nexport interface TimerPillOptions {\n t: Translate\n store: HudStore\n sessionId: string | null | undefined\n now: number\n}\n\nexport function HudDockPill(props: HudDockPillProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var store = props.store\n\n // useSessions 是选择器 hook:选择器参数是必需的,裸调用会以\n // \"selector is not a function\" 崩掉。\n var sessions = useSessions(function (snapshot) {\n return snapshot\n })\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n // 右栏里的嵌入式会话(子会话 chat)自带会话绑定;只有主视图座位可以驱动共享\n // 面板,否则一个子会话会把它下面的仓库从用户正在看的会话下抽走。\n // 快照不带这个计数(更旧的宿主)时按主视图放行,胶囊不会因此消失。\n var mainView = !summary || !summary.retainedBy ? true : (summary.retainedBy.mainView || 0) > 0\n\n var state = useStore(store)\n\n /**\n * 工具胶囊的\"实时秒数\"滴答。\n *\n * **必须在组件体里、无条件、固定顺序地调用**:早先放在普通函数 `toolPillNode` 里、还带\n * `if (now === undefined)` 的条件,就是条件钩子 —— React 会抛错,导致整行 dock 渲染失败,\n * 两枚胶囊一起消失(用户实测\"胶囊都不显示了\")。回调里再判断要不要开定时器就没这问题。\n */\n var tick = React.useState(Date.now())\n var tickSet = tick[1]\n var toolsTickKey =\n state.tools && state.tools.active !== null && state.tools.active !== undefined\n ? String(state.tools.active.callId)\n : null\n // 会话计时 + 输入框 ↑ 召回的数据(两个 loader 各自带 TTL,这里按最密的节奏问就行)。\n React.useEffect(\n function () {\n if (typeof sessionId !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`var` 变量在回调里 TS 不再保留)。\n const id = sessionId\n var load = function () {\n if (store.loadSessionTime !== undefined) store.loadSessionTime(id)\n if (store.loadPromptHistory !== undefined) store.loadPromptHistory(id)\n }\n load()\n var poll = window.setInterval(load, 3000)\n return function () {\n window.clearInterval(poll)\n }\n },\n [sessionId],\n )\n\n // 挂钟每 4 秒跳一次:计时胶囊的显示粒度是分钟,但跨分钟不用等下一次取数。\n React.useEffect(function () {\n var clock = window.setInterval(function () {\n tickSet(Date.now())\n }, 4000)\n return function () {\n window.clearInterval(clock)\n }\n }, [])\n\n React.useEffect(\n function () {\n if (toolsTickKey === null) return undefined\n var timer = window.setInterval(function () {\n tickSet(Date.now())\n }, 500)\n return function () {\n window.clearInterval(timer)\n }\n },\n [toolsTickKey],\n )\n\n React.useEffect(\n function () {\n if (!mainView) return\n store.bindSession(sessionId, cwd)\n },\n [sessionId, cwd, mainView, store],\n )\n\n if (!cwd || !mainView || state.sessionId !== sessionId) return null\n\n // 会话归因的胶囊段:只有\"确实改过东西\"时才露头(没数据 / 0 个都不显示)。\n var sessionView = sessionChangeView(state)\n var sessionSegment =\n sessionView !== null && sessionView.mineCount > 0\n ? ' │ ●' + String(sessionView.mineCount)\n : null\n\n var children: React.ReactNode[] = []\n // 工作区名放在最前、且与分支用 `·` 隔开:写成 `git:(dsh-hud/main*)` 会被读成\"分支叫 dsh-hud/main\"。\n // 三种\"非仓库\"状态也带上它 —— 工作区跟 git 没关系,没仓库时它反而更有用。\n var workspace = workspaceNameOf(cwd)\n if (workspace !== null) {\n children.push(h('span', { key: 'workspace', className: 'dshh-workspace' }, workspace))\n children.push(h('span', { key: 'workspaceSep', className: 'dshh-workspaceSep' }, ' · '))\n }\n if (state.phase === 'error') {\n children.push(\n h('span', { key: 'gitWraperr', className: 'dshh-gitWrap' }, 'git:(' + t('pill.error') + ')'),\n )\n } else if (state.phase === 'loading' || state.phase === 'idle') {\n children.push(\n h(\n 'span',\n { key: 'gitWrapload', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.loading') + ')',\n ),\n )\n } else if (!state.status || !state.status.repo) {\n children.push(\n h(\n 'span',\n { key: 'gitWrapnorepo', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.notRepo') + ')',\n ),\n )\n } else {\n var status = state.status\n // `git:(branch*)` —— codex-hud 的 gitSegment 就是这么拼的,配色也照它:\n // `git:(` 与 `)` 品红(colors.git)、分支名与 ↑↓ 青(colors.gitBranch),\n // 而 `*` 在括号**里面**、与分支名同色。\n children.push(h('span', { key: 'gitOpen', className: 'dshh-gitWrap' }, 'git:('))\n children.push(\n h(\n 'span',\n { key: 'branch', className: 'dshh-branch' },\n shortBranch(branchLabel(status, t)) + (status.dirty ? '*' : ''),\n ),\n )\n children.push(h('span', { key: 'gitClose', className: 'dshh-gitWrap' }, ')'))\n if (status.ahead > 0)\n children.push(\n h('span', { key: 'ahead', className: 'dshh-ahead' }, ' ↑' + String(status.ahead)),\n )\n if (status.behind > 0)\n children.push(\n h('span', { key: 'behind', className: 'dshh-ahead' }, ' ↓' + String(status.behind)),\n )\n\n // 简写符:只列非零项,按固定顺序,各自上色(M 黄 / A 绿 / D 红 / ? 灰 …)。\n // 与侧栏行内 chip 共用 `countShorthands` —— 两处各写一套算法一定会漂。\n var shorthands = countShorthands(status.counts)\n for (var index = 0; index < shorthands.length; index += 1) {\n children.push(' ')\n children.push(\n h(\n 'span',\n {\n key: shorthands[index].key,\n style: { color: COUNT_COLOR[shorthands[index].key] },\n },\n shorthands[index].mark + String(shorthands[index].value),\n ),\n )\n }\n }\n\n if (sessionSegment !== null) children.push(sessionSegment)\n\n // 悬停给出完整信息:总数型的托盘里放不下细节,这里补齐。\n var titleLines: string[] = [t('pill.title')]\n if (sessionView !== null) titleLines.push(t('session.pill', { n: String(sessionView.mineCount) }))\n if (cwd) titleLines.push(cwd)\n if (state.status && state.status.repo) {\n var tip = branchLabel(state.status, t)\n if (state.status.upstream) tip += ' · ' + t('panel.upstream') + ' ' + state.status.upstream\n titleLines.push(tip)\n var labels: string[] = []\n var counts2: Partial<HudStatusCounts> = state.status.counts || {}\n for (var i2 = 0; i2 < COUNT_ORDER.length; i2 += 1) {\n var k2 = COUNT_ORDER[i2]\n var v2 = counts2[k2] || 0\n if (v2 > 0) labels.push(t('count.' + k2) + ' ' + String(v2))\n }\n if (labels.length > 0) titleLines.push(labels.join(' · '))\n }\n\n // 外层容器复刻框架自己的 dock 行几何(见 queue 公告):仅靠胶囊自身对齐,\n // 胶囊会飘到整栏的最左边,而输入卡片是居中的。\n return h(\n 'div',\n { className: 'dshh-dock', 'data-dsh-hud': 'dock' },\n h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill',\n 'data-dsh-hud': 'pill',\n 'aria-expanded': state.panelOpen ? 'true' : 'false',\n style: { color: state.panelOpen ? 'var(--dsw-alias-brand-primary, #4b8bf5)' : undefined },\n onClick: function () {\n store.togglePanel()\n },\n },\n store.tipHandlers('pill', function () {\n return titleLines.join('\\n')\n }),\n ),\n children,\n ),\n // 工具活动那一枚并排在后面 —— dock 是竖列,独立注册会另起一整行(用户实测\"换行了\")。\n toolPillNode(state, { t: t, store: store, now: props.now === undefined ? tick[0] : props.now }),\n // 计时胶囊放在**最后**(三枚同在一条注册里 → 同一行;独立注册会另起一整行)。\n timerPillNode(state, {\n t: t,\n store: store,\n sessionId: sessionId,\n now: props.now === undefined ? tick[0] : props.now,\n }),\n )\n}\n\nexport function toolPillNode(state: HudState, options: ToolPillOptions): React.ReactElement | null {\n var t = options.t\n var tools = state.tools\n if (!tools || (tools.active === null && tools.total === 0)) return null\n var now = options.now\n\n var children: React.ReactNode[] = []\n if (tools.active !== null) {\n // 配色对齐 codex-hud 的 activity-lines:`◐` 黄 + 工具名青 + 目标/耗时用默认正文色。\n var elapsed = toolElapsedLabel(tools.active, now)\n children.push(\n h('span', { key: 'glyph', className: 'dshh-toolsGlyph dshh-toolsGlyphRun' }, '\\u25d0'),\n )\n children.push(\n h(\n 'span',\n { key: 'name', className: 'dshh-toolsName' },\n ' ' + String(tools.active.name === undefined ? '?' : tools.active.name),\n ),\n )\n // 带上参数摘要(codex-hud 就是 `◐ exec_command: pnpm test`)—— 技能调用因此能看出是哪个技能。\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0) {\n children.push(': ' + tools.active.summary)\n }\n if (elapsed !== null) children.push(' ' + elapsed)\n } else {\n // 空闲时把**每个工具各自几次**铺出来(codex-hud 那条 `Tools:` 就是这么给的):\n // `✓ bash ×16 │ ✓ apply_patch ×6 │ ✗ write_stdin ×3`。名字与数字贴在一起,\n // 就不会再有\"×54 是总数还是这个工具的次数\"那种误读。\n //\n // 最多铺 3 个(按次数排序,宿主已经排好),其余收成 `+k` —— 悬停里有完整清单与最近几条。\n // `skill` 工具本身对用户没信息量(`skill ×2` 说明不了是哪个技能)—— codex-hud 也是直接列技能名。\n // 所以把它从计数里剔掉,改由下面那段技能清单承担;总次数不受影响。\n var toolCounts = tools.counts.filter(function (item) {\n return item.name !== 'skill'\n })\n var shown = toolCounts.slice(0, 3)\n for (var i = 0; i < shown.length; i += 1) {\n var item = shown[i]\n var bad = item.ok === false\n if (i > 0) children.push(' \\u2502 ')\n children.push(\n h(\n 'span',\n {\n key: 'glyph' + String(i),\n className: 'dshh-toolsGlyph' + (bad ? ' dshh-toolsGlyphBad' : ' dshh-toolsGlyphOk'),\n },\n bad ? '\\u2717' : '\\u2713',\n ),\n )\n children.push(' ' + String(item.name) + ' \\u00d7' + String(item.count))\n }\n // 技能清单:名字直接铺出来(\"会话用了哪些 skill\"要能一眼看到,不只是次数)。\n // 标签用字面的 `skills:`(不翻译)—— 与 `git:(` 同一套:它是约定写法,不是描述。\n var skills = tools.skills || []\n if (skills.length > 0) {\n if (children.length > 0) children.push(' \\u2502 ')\n children.push(t('tools.skills') + ' ')\n for (var k = 0; k < skills.length && k < SKILLS_SHOWN; k += 1) {\n if (k > 0) children.push(' \\u00b7 ')\n children.push(\n h(\n 'span',\n {\n key: 'skill' + String(k),\n className: skills[k].ok === false ? 'dshh-toolsGlyphBad' : 'dshh-skillName',\n },\n String(skills[k].name) + ' \\u00d7' + String(skills[k].count),\n ),\n )\n }\n if (skills.length > SKILLS_SHOWN) children.push(' +' + String(skills.length - SKILLS_SHOWN))\n if (tools.skillsDropped > 0) children.push(' +' + String(tools.skillsDropped))\n }\n if (toolCounts.length > shown.length)\n children.push(' +' + String(toolCounts.length - shown.length))\n }\n\n // 渲染现场:画没画、画的是哪一档(与 row-chip/row-token 同一套排查手段)。\n probe.log('tools-pill', {\n active: tools.active === null ? null : String(tools.active.name),\n total: tools.total,\n })\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-toolsPill',\n 'data-dsh-hud': 'tools-pill',\n key: 'tools',\n },\n options.store.tipHandlers('tools-pill', function () {\n return toolsTitle(tools, t)\n }),\n ),\n children,\n )\n}\n\nexport function timerPillNode(\n state: HudState,\n options: TimerPillOptions,\n): React.ReactElement | null {\n var t = options.t\n var sessionId = options.sessionId\n var now = options.now\n var snapshot = state.sessionTime\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId === sessionId\n ? snapshot.value\n : null\n // 会话不在(还没恢复进存储、已归档、子会话已回收)、宿主服务缺失:不画这枚胶囊,也不占位。\n if (value === null || value.available !== true) return null\n var createdAt = value.createdAt\n if (typeof createdAt !== 'number') return null\n\n var age = Math.max(0, now - createdAt)\n var hints = [t('timer.title'), t('timer.created', { time: fullWhen(createdAt) })]\n if (typeof value.llmMs === 'number' && typeof value.toolMs === 'number') {\n hints.push(\n t('timer.work', { llm: formatDuration(value.llmMs), tool: formatDuration(value.toolMs) }),\n )\n }\n if (typeof value.turns === 'number' && typeof value.steps === 'number') {\n hints.push(t('timer.turns', { turns: String(value.turns), steps: String(value.steps) }))\n }\n probe.log('timer-pill', { age: Math.round(age / 60000) })\n var tipKey = 'session-timer'\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-timer',\n 'data-dsh-hud': 'session-timer',\n key: 'timer',\n },\n options.store.tipHandlers(tipKey, function () {\n return hints.join(' · ')\n }),\n ),\n [\n h('span', { key: 'glyph', className: 'dshh-timerGlyph' }, '⏱'),\n h('span', { key: 'value', className: 'dshh-timerValue' }, formatDuration(age)),\n ],\n )\n}\n\nexport function toolsTitle(tools: ToolsState, t: Translate): string {\n var lines: string[] = []\n if (tools.active !== null) {\n lines.push(t('tools.now', { name: String(tools.active.name) }))\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0)\n lines.push(tools.active.summary)\n }\n lines.push(t('tools.total', { n: String(tools.total) }))\n if (tools.counts.length > 0) {\n lines.push(\n t('tools.byTool') +\n ' ' +\n tools.counts\n .map(function (item) {\n return String(item.name) + ' \\u00d7' + String(item.count)\n })\n .join(' \\u00b7 '),\n )\n }\n if (tools.skills && tools.skills.length > 0) {\n lines.push(\n t('tools.skills') +\n ' ' +\n tools.skills\n .map(function (item) {\n return (\n String(item.name) +\n ' \\u00d7' +\n String(item.count) +\n (item.ok === false ? ' (\\u2717)' : '')\n )\n })\n .join(' \\u00b7 '),\n )\n }\n var recent = tools.recent.slice(0, 3)\n if (recent.length > 0) {\n lines.push(t('tools.recent'))\n lines.push(\n recent\n .map(function (item) {\n var mark = item.ok === false ? '\\u2717' : '\\u2713'\n var ms = typeof item.ms === 'number' ? ' ' + (item.ms / 1000).toFixed(1) + 's' : ''\n var summary =\n typeof item.summary === 'string' && item.summary.length > 0\n ? ' \\u00b7 ' + item.summary\n : ''\n return mark + ' ' + String(item.name) + ms + summary\n })\n .join('\\n'),\n )\n }\n return lines.join('\\n')\n}\n\nexport function toolElapsedLabel(active: HudToolActive | null, now: number): string | null {\n if (active === null || typeof active !== 'object') return null\n if (typeof active.startedAt !== 'number') return null\n var seconds = Math.max(0, (now - active.startedAt) / 1000)\n return seconds < 10 ? seconds.toFixed(1) + 's' : String(Math.round(seconds)) + 's'\n}\n","/**\n * 「本会话改过哪些文件」那一行的展示:标题、审阅标记、回合标签。\n *\n * 数据来自 store 的会话归因,这里只负责把事实排成一行字。\n */\n\nimport type React from 'react'\n\nimport { timeLabel } from '../format.js'\nimport type {\n ReviewMark,\n ReviewView,\n SessionChangeView,\n SessionCommand,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `SessionChangesLine` 的 props:归因视图 + 审阅进度 + 三枚开关。 */\nexport interface SessionChangesLineProps {\n t: Translate\n /** 会话归因视图;`null` = 这份数据还没到手(这一行只剩开关)。 */\n view: SessionChangeView | null\n /** 「只看本会话改的」开关状态。 */\n onlySession?: boolean\n /** 「只看最近一轮」开关状态。 */\n onlyLatest?: boolean\n /** 审阅进度;`null` / 缺省 = 这次没要签名。 */\n review?: ReviewView | null\n onToggle: () => void\n onToggleLatest: () => void\n onClearReviewed: () => void\n}\n\n/**\n * 改动概览下面那一行:本会话改了几个文件 + 「只看本会话改的」开关。\n *\n * 归因数据没到手(`view === null`)时整行不渲染 —— 不显示一个\"全部都是别人的\"的假象。\n */\nexport function SessionChangesLine(props: SessionChangesLineProps): React.ReactElement {\n var t = props.t\n var view = props.view\n var review = props.review || null\n // 这一行**总是渲染**:它带着三枚开关(只看本会话改的 / 侧栏档位 / 只看最近一轮),\n // 归因或审阅数据缺失时只是没有计数可报 —— 控件不能因为\"数据还没到\"就凭空消失。\n var notes: string[] = []\n if (view !== null) {\n notes.push(t('session.line', { n: String(view.mineCount) }))\n // 回合信息拿得到时,\"最近一轮改了几个\"是最常问的那个问题,直接写在行上。\n if (view.latestTurn !== null && view.latestCount > 0) {\n notes.push(\n t('session.latestTurn', { n: String(view.latestCount), turn: String(view.latestTurn + 1) }),\n )\n }\n if (view.possibleCount > 0) notes.push(t('session.possible', { n: String(view.possibleCount) }))\n }\n if (view !== null) {\n if (view.gone.length > 0) notes.push(t('session.gone', { n: String(view.gone.length) }))\n if (view.dropped > 0) notes.push(t('session.dropped', { n: String(view.dropped) }))\n if (view.possibleDropped > 0)\n notes.push(t('session.possibleDropped', { n: String(view.possibleDropped) }))\n }\n if (props.review && props.review.total > 0) {\n notes.push(\n t('review.count', { n: String(props.review.done), total: String(props.review.total) }),\n )\n }\n if (props.review && props.review.staleCount > 0)\n notes.push(t('review.staleCount', { n: String(props.review.staleCount) }))\n return h(\n 'div',\n { className: 'dshh-session', 'data-dsh-hud': 'session' },\n // 没有任何可报的计数时,文字位置留空(开关照旧在右边)。\n notes.length === 0\n ? null\n : h(\n 'span',\n { className: 'dshh-sessionText', title: sessionLineTitle(view, t) },\n notes.join(' · '),\n ),\n h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlySession === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'only',\n 'aria-pressed': props.onlySession === true ? 'true' : 'false',\n title: t('session.onlyTitle'),\n onClick: props.onToggle,\n },\n props.onlySession === true ? '◉ ' : '○ ',\n t('session.only'),\n ),\n // 已经把某些文件标成\"已审\"时,给一个\"清空\"的出口(只清当前工作区)。\n props.review && props.review.done > 0\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-sessionToggle',\n 'data-session-review': 'clear',\n title: t('review.clear'),\n onClick: props.onClearReviewed,\n },\n t('review.clearShort'),\n )\n : null,\n // 第二枚开关只在\"这一轮的归属拿得到\"时出现:拿不到就整枚不画,别给一个按不动的按钮。\n view === null || view.latestTurn === null\n ? null\n : h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlyLatest === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'latest',\n 'aria-pressed': props.onlyLatest === true ? 'true' : 'false',\n title: turnLabel(view, view.latestTurn, t) + '\\n' + t('session.onlyLatestTitle'),\n onClick: props.onToggleLatest,\n },\n props.onlyLatest === true ? '◉ ' : '○ ',\n t('session.onlyLatest'),\n ),\n )\n}\n\n/** 一个文件属于哪一档:`mine` 确定改过 / `possible` 命令旁听 / `other` 与本会话无关。 */\nexport function sessionMarkOf(\n view: SessionChangeView,\n file: string,\n): 'mine' | 'possible' | 'other' {\n if (view.mine !== undefined && view.mine[file] !== undefined) return 'mine'\n if (view.possible !== undefined && view.possible[file] !== undefined) return 'possible'\n return 'other'\n}\n\nexport function sessionTitle(view: SessionChangeView, file: string, t: Translate): string {\n var record = view.mine !== undefined ? view.mine[file] : undefined\n if (record === undefined) {\n var guess = view.possible !== undefined ? view.possible[file] : undefined\n if (guess === undefined) return t('session.other')\n // 低一档的置信度:说清是\"从命令里看出来的\",并把那条命令贴出来。\n return t('session.possibleTitle', { reason: String(guess.reason || '').slice(0, 80) })\n }\n var parts: string[] = []\n if (record.writes > 0) parts.push(t('session.writes', { n: String(record.writes) }))\n if (record.edits > 0) parts.push(t('session.edits', { n: String(record.edits) }))\n var when = typeof record.lastAt === 'number' ? timeLabel(record.lastAt) : ''\n var line =\n t('session.mine') +\n (parts.length === 0 ? '' : '(' + parts.join('、') + ')') +\n (when ? ' ' + when : '')\n // 回合是\"这一轮改了什么\"的锚点:有就一并说清是哪一轮、那一轮的请求是什么。\n var turn = typeof record.lastTurn === 'number' ? record.lastTurn : null\n if (turn !== null) line += '\\n' + turnLabel(view, turn, t)\n return line\n}\n\n/** 行尾那个标记的悬停说明:三种状态各说清下一步。 */\nexport function reviewTitle(state: ReviewMark | undefined, t: Translate): string {\n if (state === 'fresh') return t('review.unmark')\n if (state === 'stale') return t('review.stale')\n return t('review.mark')\n}\n\n/**\n * 那一行的悬停说明:自哪一刻起记录 + 跑过哪些命令。\n *\n * \"可能相关\"是从命令里看出来的,用户要能自己判断 —— 所以把命令原文贴出来,而不是只报一个数。\n */\nexport function sessionLineTitle(view: SessionChangeView | null, t: Translate): string | undefined {\n // 没有归因数据(那一行只剩审阅进度)时没有\"自 … 起记录\"也没有命令可列。\n if (view === null) return undefined\n var lines: string[] = []\n if (view.since !== null) lines.push(t('session.since', { time: timeLabel(view.since) }))\n var commands = (view.commands || []).slice(-5).reverse()\n if (commands.length > 0) {\n lines.push(t('session.commands', { n: String(commands.length) }))\n for (var index = 0; index < commands.length; index += 1) {\n var item: Partial<SessionCommand> = commands[index] || {}\n lines.push(\n '· ' +\n String(item.command || '') +\n (item.failed === true ? ' ' + t('session.commandFailed') : ''),\n )\n }\n if (view.writingCommands > commands.length) {\n lines.push(t('session.commandsMore', { n: String(view.writingCommands - commands.length) }))\n }\n }\n return lines.length === 0 ? undefined : lines.join('\\n')\n}\n\n/**\n * 清单里那一行的悬停说明:这行是不是本会话改的。\n *\n * `mine` 里有这条就把写入/编辑次数与时间区间说清楚;没有就直说\"不是这个会话改的\"\n * (可能是你自己、编辑器、或另一个会话)。\n */\n/**\n * 一轮的说明:`第 3 轮:<那一轮的请求预览>`。\n *\n * 回合号从宿主的 `turnBoundary` 投影来,请求预览从 `turnOutline` 投影来 —— 拿不到 prompt\n * 时只说\"第 N 轮\",不编内容。\n */\nexport function turnLabel(view: SessionChangeView, turn: number, t: Translate): string {\n var prompt =\n view.turnPrompts !== undefined && view.turnPrompts !== null ? view.turnPrompts[turn] : undefined\n if (typeof prompt !== 'string' || prompt.length === 0)\n return t('session.turn', { n: String(turn + 1) })\n return t('session.turnWithPrompt', { n: String(turn + 1), prompt: prompt })\n}\n","/**\n * 小段 DOM 判定与定位:焦点在哪、点在不在面板里、差异行怎么滚进视野。\n *\n * 数量少但被 hooks 与组件共用,所以单独一层,避免它们互相 import。\n */\n\n/**\n * 本层用到的 DOM 子集(结构类型)。\n *\n * 为什么不用 `Element` / `instanceof Element`:测试里的假 DOM 是普通对象(没有 Element 类),\n * 用 instanceof 会把那些用例全判成\"没有 DOM\"。这里只关心下面几个字段,按结构收窄即可;\n * 外部传进来的值(事件目标、查询结果)都从 `unknown` 收一次 —— 这就是系统边界。\n */\ninterface DomNodeLike {\n getAttribute?: (name: string) => string | null\n parentNode?: DomNodeLike | null\n tagName?: string\n isContentEditable?: boolean\n className?: string\n scrollTop?: number\n offsetTop?: number\n offsetHeight?: number\n clientHeight?: number\n getBoundingClientRect?: () => { top: number }\n}\n\n/** 把未知值收窄成\"结构上像个 DOM 节点\";不是对象就给 null。 */\nfunction asDomNode(value: unknown): DomNodeLike | null {\n return value !== null && typeof value === 'object' ? (value as DomNodeLike) : null\n}\n\n/** 取当前焦点节点;没有 document / activeElement 时给 null。 */\nexport function activeElement(): Element | null {\n if (typeof document === 'undefined') return null\n return document.activeElement || null\n}\n\n/** 事件目标是否落在面板里(走自己的 `data-dsh-hud=\"panel\"` 标记,不碰产品 DOM)。 */\nexport function isInsidePanel(node: unknown): boolean {\n return isInsideMarked(node, 'panel')\n}\n\n/** 事件目标是否落在某个 `data-dsh-hud` 标记的子树里(自己的标记,不碰产品的选择器)。 */\nexport function isInsideMarked(node: unknown, mark: string): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 64) {\n var attribute =\n typeof current.getAttribute === 'function' ? current.getAttribute('data-dsh-hud') : null\n if (attribute === mark) return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/**\n * 事件目标是不是\"正在输入\"的地方。\n *\n * 产品的输入框是 contenteditable,所以不能只看 tagName;两种都认。\n */\nexport function isTypingTarget(node: unknown): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 8) {\n var tag = typeof current.tagName === 'string' ? current.tagName.toLowerCase() : ''\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true\n if (current.isContentEditable === true) return true\n var editable =\n typeof current.getAttribute === 'function' ? current.getAttribute('contenteditable') : null\n if (editable === '' || editable === 'true' || editable === 'plaintext-only') return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/** 用自己写在节点上的标记找节点;找不到或不支持就是 null。 */\nexport function findMarked(selector: string): Element | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n return document.querySelector(selector)\n}\n\n/**\n * 把一个带自己标记的节点滚进它所在滚动容器的视野(最小滚动量)。\n *\n * 全走能力判断:假 DOM(测试)里没有 offsetTop / clientHeight,这个函数直接\n * no-op,测试断言的是状态与结构,不是像素。不用 `scrollIntoView`:那会连带\n * 滚动祖先容器(面板是浮层,滚了产品的会话列表就不好了)。\n */\nexport function revealRow(node: unknown, containerClassName: string): void {\n var start = asDomNode(node)\n if (!start || typeof start.parentNode === 'undefined') return\n var container = asDomNode(start.parentNode)\n var depth = 0\n while (container && depth < 32) {\n if (String(container.className || '').indexOf(containerClassName) >= 0) break\n container = asDomNode(container.parentNode)\n depth += 1\n }\n if (!container || typeof container.scrollTop !== 'number') return\n var top = start.offsetTop\n var height = start.offsetHeight\n var view = container.clientHeight\n if (typeof top !== 'number' || typeof height !== 'number' || typeof view !== 'number') return\n if (top < container.scrollTop) container.scrollTop = top\n else if (top + height > container.scrollTop + view) container.scrollTop = top + height - view\n}\n\n/* ------------------------------------------------------- 悬浮面板 */\n\nexport function measureDockTop(): number | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n var node = document.querySelector('[data-dsh-hud=\"dock\"]')\n if (!node || typeof node.getBoundingClientRect !== 'function') return null\n var rect = node.getBoundingClientRect()\n return typeof rect.top === 'number' ? rect.top : null\n}\n","/**\n * 改动清单(面板左栏):分组、计数、每行的状态标记,以及只显示本会话/最近一轮的过滤。\n */\n\nimport { statusKey } from '../format.js'\nimport React from 'react'\n\nimport { reviewTitle, sessionMarkOf, sessionTitle } from './session-changes.js'\nimport { revealRow } from '../dom.js'\nimport type { HudChange, ReviewView, SessionChangeView, Translate } from '../shared.js'\nimport { COUNT_COLOR, h } from '../shared.js'\n\n/** `ChangeList` 的 props:清单本体 + 会话归因/审阅两列标记 + 交互回调。 */\nexport interface ChangeListProps {\n /** 要渲染的清单:工作区那份是 `HudChange`,提交明细那份是 `HudCommitFile`(结构兼容)。 */\n changes?: HudChange[] | null\n /** 正在看的那份差异对应的文件(`null` = 没打开差异)。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 两份清单同时在屏幕上时要区分\"哪一份\"(滚进视野时按它找行)。 */\n listId?: string\n /** 会话归因视图;`null` = 这份数据还没到手(整列不画)。 */\n sessionView?: SessionChangeView | null\n /** 审阅进度;`null` = 这次没要签名(胶囊那一路的轮询就是这样)。 */\n review?: ReviewView | null\n /** 清单被截断时的那句脚注。 */\n truncated?: boolean\n /** 自定义标题(提交明细那栏用);不给自己拼一个带计数的。 */\n title?: string\n t: Translate\n /** 点一行:打开该文件的差异视图。 */\n onOpen: (change: HudChange) => void\n /** 点行尾审阅标记:切换\"这一版审过了\"(不画审阅列的那份清单可以不给)。 */\n onToggleReviewed?: (file: string) => void\n}\n\nexport function ChangeList(props: ChangeListProps): React.ReactElement | null {\n var changes = props.changes || []\n var activeFile = props.activeFile\n var cursorFile = props.cursor || null\n // 提交明细打开时,屏幕上会同时有两份清单(左栏工作区 + 右栏这次提交),\n // 所以行标记必须带上是哪一份,否则\"把选中行滚进视野\"会滚错栏。\n var listId = props.listId || 'changes'\n\n // 键盘选中(或正在看)的那行可能被滚出视野:把它带回来。\n React.useEffect(\n function () {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return\n var wanted = cursorFile || activeFile\n if (!wanted) return\n for (var index = 0; index < changes.length; index += 1) {\n if (changes[index].file !== wanted) continue\n var node = document.querySelector(\n '[data-dsh-hud=\"row\"][data-row-list=\"' +\n listId +\n '\"][data-row-index=\"' +\n String(index) +\n '\"]',\n )\n revealRow(node, 'dshh-list')\n return\n }\n },\n [cursorFile, activeFile, changes.length, listId],\n )\n\n if (changes.length === 0) return null\n\n // 会话归因标记:只在这份数据到手时才有(`sessionView` 为 null 就整列不画)。\n var sessionView = props.sessionView === undefined ? null : props.sessionView\n // 审阅进度:`null` 表示这次没要签名(胶囊那一路的轮询就是这样),整列不画。\n var review = props.review === undefined ? null : props.review\n\n var rows = changes.map(function (change, index) {\n // 整行可点:打开该文件的差异视图(只读,不改动任何东西)。\n // \"正在看的那行\"和\"键盘选中的那行\"可能是两行(选了还没打开),因此两种标记分开。\n return h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-row' +\n (activeFile && change.file === activeFile ? ' dshh-rowActive' : '') +\n (cursorFile && change.file === cursorFile ? ' dshh-rowCursor' : ''),\n key: String(index) + ':' + change.path,\n 'data-dsh-hud': 'row',\n 'data-row-list': listId,\n 'data-row-index': String(index),\n title:\n change.path +\n (sessionView === null ? '' : '\\n' + sessionTitle(sessionView, change.file, props.t)) +\n '\\n' +\n props.t('panel.clickForDiff') +\n '\\n' +\n props.t('panel.rowKeys'),\n onClick: function () {\n props.onOpen(change)\n },\n },\n sessionView === null\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-marker' +\n (sessionMarkOf(sessionView, change.file) === 'mine'\n ? ''\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? ' dshh-markerPossible'\n : ' dshh-markerOther'),\n 'data-session-mark': sessionMarkOf(sessionView, change.file),\n 'aria-hidden': 'true',\n },\n sessionMarkOf(sessionView, change.file) === 'mine'\n ? '●'\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? '◐'\n : '○',\n ),\n h(\n 'span',\n {\n className: 'dshh-status',\n style: {\n color:\n COUNT_COLOR[statusKey(change.status)] || 'var(--dsw-alias-label-secondary, #8a8f98)',\n },\n },\n change.status,\n ),\n h('span', { className: 'dshh-path' }, change.path),\n review === null || review.known === undefined || review.known[change.file] !== true\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-review' +\n (review.marks[change.file] === 'fresh' ? ' dshh-reviewOn' : '') +\n (review.marks[change.file] === 'stale' ? ' dshh-reviewStale' : ''),\n 'data-review':\n review.marks[change.file] === undefined ? 'none' : review.marks[change.file],\n title: reviewTitle(review.marks[change.file], props.t),\n onClick: function (event: React.MouseEvent<HTMLSpanElement>) {\n // 行本身是按钮(点开差异):这里必须拦住,否则标记一下就顺手打开了差异。\n event.stopPropagation()\n event.preventDefault()\n // 只有画了审阅列的清单才会渲染这枚标记,所以回调不给时这里也不会被走到。\n if (props.onToggleReviewed) props.onToggleReviewed(change.file)\n },\n },\n review.marks[change.file] === undefined ? '○' : '✓',\n ),\n )\n })\n\n var footer = props.truncated\n ? h('div', { className: 'dshh-note' }, props.t('panel.truncated'))\n : null\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h(\n 'div',\n { className: 'dshh-sectionTitle' },\n props.title || props.t('panel.changes') + ' · ' + String(changes.length),\n ),\n h('div', { className: 'dshh-list' }, rows),\n footer,\n )\n}\n","/**\n * 最近提交清单(面板中间那一栏):一条一行,短 sha + 标题 + HEAD 徽标 + 时刻。\n */\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudLogEntry, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `CommitList` 的 props:最近提交清单 + 打开回调 + 高亮的那一条。 */\nexport interface CommitListProps {\n /** 最近提交;`null` / 空表时整段不画。 */\n commits?: HudLogEntry[] | null\n t: Translate\n /** 提交明细当前打开的那条(短 sha 与完整 sha 前缀匹配)。 */\n activeSha?: string | null\n /** 点一行:打开/收起这次提交的明细。 */\n onOpen?: ((commit: HudLogEntry) => void) | null\n}\n\n/**\n * 最近提交:一行一条,sha + 标题 + HEAD 徽标 + 相对时间;整行可点看这次提交改了什么。\n * 打开的那条会高亮(再点一次收起)——提交明细在右栏,左栏这块主面板不动。\n */\nexport function CommitList(props: CommitListProps) {\n var commits = props.commits || []\n if (commits.length === 0) return null\n var t = props.t\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.commits')),\n h(\n 'div',\n { className: 'dshh-commits' },\n commits.map(function (commit, index) {\n var refs = commit.refs || []\n var isHead = refs.some(function (ref) {\n return ref.indexOf('HEAD') === 0\n })\n var when =\n typeof commit.committedAt === 'number' ? fullWhen(commit.committedAt) : commit.date || ''\n var tip = [t('commit.click'), '', commit.subject, commit.author + ' · ' + when]\n .concat(refs)\n .join('\\n')\n // 整行是按钮:点了在左栏换成这次提交的改动清单(只读)。\n var open = sameCommit(commit.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n key: commit.sha + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(commit)\n },\n },\n h('span', { className: 'dshh-csha' }, commit.sha),\n h('span', { className: 'dshh-csubject' }, commit.subject || '—'),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(commit, t)),\n )\n }),\n ),\n )\n}\n","/**\n * 提交明细(右栏):某次提交的元信息与它改过的文件。\n */\n\nimport { errorText } from '../format.js'\nimport { ChangeList } from './change-list.js'\nimport type { HudCommitFile, HudCommitState, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `CommitPane` 的 props:这次提交的完整明细 + 打开某个文件差异的回调。 */\nexport interface CommitPaneProps {\n t: Translate\n /** 提交明细的状态(`state.commit`);只在它非空时才渲染本栏。 */\n commit: HudCommitState\n /** 打开清单里一条(= 提交里的某个文件)的差异。 */\n onOpenFile: (file: HudCommitFile) => void\n /** 正在看的那份差异对应的文件。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 本栏宽度(右边缘抓握可拖)。 */\n width?: number | null\n}\n\n/**\n * 一次提交的那一栏(**中间**栏,固定宽度):元信息 + 这次提交的改动清单。\n *\n * 差异在它右边那一栏(DiffView)。打开提交不改动左栏——左栏始终是那块主面板,\n * 只是\"最近提交\"里对应的一行高亮起来。清单复用工作区那套 ChangeList(同一个\n * 行样式、同一套键盘移动)。\n */\nexport function CommitPane(props: CommitPaneProps) {\n var t = props.t\n var commit = props.commit\n var style = { width: props.width }\n\n var head\n if (commit.phase === 'loading') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, t('commit.loading')),\n )\n } else if (commit.phase === 'error') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, errorText(commit.errorCode, t)),\n )\n } else {\n var lines = [commit.author + ' · ' + commit.date]\n if (commit.refs && commit.refs.length > 0) lines.push(commit.refs.join(', '))\n if (commit.merge) lines.push(t('commit.merge'))\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h(\n 'div',\n { className: 'dshh-commitTitle' },\n h('span', { className: 'dshh-csha' }, commit.shortSha),\n h(\n 'span',\n { className: 'dshh-commitSubject', title: commit.subject },\n commit.subject || '—',\n ),\n ),\n h('div', { className: 'dshh-commitMeta' }, lines.join(' · ')),\n )\n }\n\n var files = commit.files || []\n var list =\n commit.phase !== 'ready'\n ? null\n : files.length === 0\n ? h(\n 'div',\n { className: 'dshh-commitFiles' },\n h('div', { className: 'dshh-note' }, t('commit.empty')),\n )\n : h(\n 'div',\n { className: 'dshh-commitFiles' },\n h(ChangeList, {\n changes: files,\n truncated: commit.truncated,\n t: t,\n title: t('commit.files') + ' · ' + String(files.length),\n onOpen: props.onOpenFile,\n activeFile: props.activeFile,\n cursor: props.cursor,\n listId: 'commit',\n }),\n )\n\n return h('div', { className: 'dshh-commitpane', style: style }, head, list)\n}\n","/**\n * 概览那一行的计数小片:按「改动 > 新增 > 未跟踪」排列,颜色与胶囊上的简写一致。\n */\n\nimport type React from 'react'\n\nimport type { HudStatusCounts, Translate } from '../shared.js'\nimport { COUNT_COLOR, COUNT_ORDER, h } from '../shared.js'\n\n/** `CountChips` 的 props:八类改动计数 + 取词函数。 */\nexport interface CountChipsProps {\n /** git status 的分组计数;缺字段按 0 处理。 */\n counts?: HudStatusCounts | null\n t: Translate\n}\n\n/* ------------------------------------------------------------- 小组件 */\n\nexport function CountChips(props: CountChipsProps): React.ReactElement {\n var counts: Partial<HudStatusCounts> = props.counts || {}\n var children: React.ReactElement[] = []\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = counts[key] || 0\n if (value <= 0) continue\n children.push(\n h(\n 'span',\n {\n key: key,\n className: 'dshh-chip',\n style: { color: COUNT_COLOR[key], borderColor: 'currentColor' },\n title: props.t('count.' + key) + ' ' + String(value),\n },\n props.t('count.' + key) + ' ' + String(value),\n ),\n )\n }\n if (children.length === 0) {\n return h('span', { className: 'dshh-clean' }, props.t('panel.clean'))\n }\n return h('div', { className: 'dshh-chips' }, children)\n}\n","/**\n * 图片改动的前后对比:两侧缩略、尺寸与字节数、可缩放,认不出的二进制退回一句话说明。\n */\n\nimport React from 'react'\n\nimport { errorText } from '../format.js'\nimport type {\n HudDiffState,\n HudStore,\n ImagePayload,\n ImageRequest,\n ImageSide,\n Translate,\n} from '../shared.js'\nimport { IMAGE_FILE_EXTENSIONS, formatBytes, h, imagePairKey, stepZoom } from '../shared.js'\n\n/**\n * `image` 端点在 store 缓存里的条目(store 的 image 片写进 `Map` 的那个形状)。\n *\n * 它同时被 image-diff 与本目录的 turn-images 消费;先放在这里(等 shared 收尾后视需要上提)。\n */\nexport interface ImagePairEntry {\n phase: string\n value?: ImagePayload\n code?: string | null\n}\n\n/** 图片请求失败/成功的一侧标签对。 */\nexport interface ImageLabels {\n before: string\n after: string\n}\n\n/** 图片并排/切换时的一格。 */\ninterface ImageCell {\n key: string\n label: string\n side: ImageSide\n}\n\n/** `ImageDiff` 的 props:差异状态(二进制改动才有它)+ store + 工作区(发请求要用)。 */\nexport interface ImageDiffProps {\n t: Translate\n store: HudStore\n diff: HudDiffState\n /** 会话工作区;没有它就没法发 `image` 请求(文本 diff 也用不到)。 */\n cwd?: string | null\n}\n\n/**\n * 二进制(图片)改动的预览:并排两张图,或按需在两张之间切换。\n *\n * 只在文本 diff 报 `binary` 时挂上来。字节来自宿主的 `image` 端点(只读),\n * 这里只负责画:单侧画不出来就画那一侧的原因,两侧都不行才退回原来那句话。\n */\nexport function ImageDiff(props: ImageDiffProps) {\n var t = props.t\n var store = props.store\n var diff = props.diff\n var request = imageRequest(diff, props.cwd)\n var key = request === null ? null : imagePairKey(request)\n // 先同步看缓存:已经取过的图第一帧就画出来,不闪\"读取图片…\"。\n var viewRef = React.useState<ImagePairEntry | null>(function () {\n return request !== null && store.cachedImagePair\n ? asPairEntry(store.cachedImagePair(request))\n : null\n })\n var modeRef = React.useState<'pair' | 'flip'>('pair')\n var flipRef = React.useState<'after' | 'before'>('after')\n\n React.useEffect(\n function () {\n if (request === null || key === null) return undefined\n var cancelled = false\n var cached = store.cachedImagePair ? asPairEntry(store.cachedImagePair(request)) : null\n if (cached !== null) {\n viewRef[1](cached)\n return undefined\n }\n viewRef[1](null)\n store.loadImagePair(request).then(function (entry) {\n if (!cancelled) viewRef[1](asPairEntry(entry))\n })\n return function () {\n cancelled = true\n }\n },\n [key],\n )\n\n if (key === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('diff.binary'))\n\n var view = viewRef[0]\n if (view === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('image.loading'))\n if (view.phase === 'error')\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, errorText(view.code, t))\n\n var value: Partial<ImagePayload> = view.value || {}\n var before: ImageSide = value.before || { kind: 'missing' }\n var after: ImageSide = value.after || { kind: 'missing' }\n if (before.kind !== 'image' && after.kind !== 'image') {\n return h(\n 'div',\n { className: 'dshh-empty', 'data-dsh-hud': 'image' },\n imageFallbackText(t, before, after),\n )\n }\n\n var labels = imageLabels(diff, t)\n var mode = modeRef[0]\n var flip = flipRef[0]\n // 缩放住在 store 里(和\"自动换行\"一样记住):`null` = 适应宽度,数字 = 固定档位。\n var zoom = diff.zoom === undefined ? null : diff.zoom\n var cells: ImageCell[] =\n mode === 'pair'\n ? [\n { key: 'before', label: labels.before, side: before },\n { key: 'after', label: labels.after, side: after },\n ]\n : [\n {\n key: flip,\n label: flip === 'after' ? labels.after : labels.before,\n side: flip === 'after' ? after : before,\n },\n ]\n\n function cell(entry: ImageCell): React.ReactElement {\n var side = entry.side\n var body =\n side.kind === 'image'\n ? h('img', {\n className: 'dshh-img',\n alt: entry.label,\n // 缩放:适应宽度交给 CSS(`max-width:100%`);固定档位按**固有像素**算宽度,\n // 因为宿主已经把宽高一起给我们了 —— 不用等图片加载就能算对。\n style:\n zoom === null || typeof side.width !== 'number'\n ? undefined\n : { width: Math.round(side.width * zoom) + 'px', maxWidth: 'none' },\n src: 'data:' + side.mime + ';base64,' + String(side.base64 || ''),\n })\n : h('div', { className: 'dshh-imgNote' }, sideReasonText(side, t))\n return h(\n 'div',\n { className: 'dshh-imgCell', key: entry.key },\n h('div', { className: 'dshh-imgLabel' }, entry.label),\n body,\n )\n }\n\n function tab(id: 'pair' | 'flip', label: string): React.ReactElement {\n var active = mode === id\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n onClick: function () {\n modeRef[1](id)\n },\n },\n label,\n )\n }\n\n // 尺寸变了才是这张图最值得一眼看到的信息(改图最常见的就是换尺寸)。\n var dimsChanged =\n before.kind === 'image' &&\n after.kind === 'image' &&\n typeof before.width === 'number' &&\n typeof after.width === 'number' &&\n (before.width !== after.width || before.height !== after.height)\n ? h(\n 'span',\n { className: 'dshh-imgDims' },\n t('image.dimsChanged', {\n from: t('image.dims', { w: String(before.width), h: String(before.height) }),\n to: t('image.dims', { w: String(after.width), h: String(after.height) }),\n }),\n )\n : null\n var bytes =\n before.kind === 'image' && after.kind === 'image'\n ? h(\n 'span',\n { className: 'dshh-imgBytes' },\n formatBytes(before.bytes) + ' → ' + formatBytes(after.bytes),\n )\n : null\n\n // 缩放按钮:内置那个文档预览把缩放藏在\"预览区底部 72px 的悬停感应带\"里\n // (CSS 默认 opacity:0,还得 Ctrl+滚轮),这里给一排看得见、点的着的。\n var zoomControls = h('span', { className: 'dshh-imgZoom', 'data-dsh-hud': 'image-zoom' }, [\n h(\n 'button',\n {\n key: 'out',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomOut'),\n 'data-dsh-hud': 'image-zoom-out',\n onClick: function () {\n store.setZoom(stepZoom(zoom, -1))\n },\n },\n '−',\n ),\n h(\n 'button',\n {\n key: 'value',\n type: 'button',\n className: 'dshh-imgZoomBtn dshh-imgZoomValue',\n title: t('image.fitWidth'),\n 'data-dsh-hud': 'image-zoom-value',\n onClick: function () {\n store.setZoom(null)\n },\n },\n zoom === null\n ? t('image.fitWidth')\n : t('image.zoomValue', { percent: String(Math.round(zoom * 100)) }),\n ),\n h(\n 'button',\n {\n key: 'in',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomIn'),\n 'data-dsh-hud': 'image-zoom-in',\n onClick: function () {\n store.setZoom(stepZoom(zoom, 1))\n },\n },\n '+',\n ),\n ])\n\n return h(\n 'div',\n { className: 'dshh-imgWrap', 'data-dsh-hud': 'image' },\n h(\n 'div',\n { className: 'dshh-imgMeta' },\n tab('pair', t('image.pair')),\n mode === 'flip'\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('image.flip'),\n 'data-dsh-hud': 'image-flip',\n onClick: function () {\n flipRef[1](flip === 'after' ? 'before' : 'after')\n },\n },\n t('image.flip'),\n )\n : null,\n zoomControls,\n dimsChanged,\n bytes,\n ),\n h(\n 'div',\n {\n className:\n 'dshh-imgPair' +\n (mode === 'flip' ? ' dshh-imgPairSingle' : '') +\n (zoom === null ? '' : ' dshh-imgPairZoomed'),\n },\n cells.map(cell),\n ),\n )\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/**\n * 由 diff 状态推出 `image` 请求;推不出(没有 cwd / 文件)返回 null。\n *\n * 页签语义与文本 diff 完全一致:未暂存看「索引 → 工作区」,已暂存看「HEAD → 索引」,\n * 提交明细按 sha 取,所以两个视图里\"未暂存 / 已暂存\"指的是同一件事。\n */\nexport function imageRequest(\n diff: HudDiffState | null | undefined,\n cwd: string | null | undefined,\n): ImageRequest | null {\n if (!diff || !diff.file || !cwd) return null\n var request: ImageRequest = { cwd: cwd, path: diff.file }\n if (diff.origPath) request.origPath = diff.origPath\n if (diff.source === 'commit' && diff.sha) request.sha = diff.sha\n else request.side = diff.side === 'index' ? 'index' : 'worktree'\n return request\n}\n\n/** 两侧的标签跟着\"看的到底是哪两条记录\"走。 */\nexport function imageLabels(diff: HudDiffState, t: Translate): ImageLabels {\n if (diff.source === 'commit')\n return { before: t('image.beforeCommit'), after: t('image.afterCommit') }\n if (diff.side === 'index') return { before: t('image.beforeIndex'), after: t('image.afterIndex') }\n return { before: t('image.beforeWorktree'), after: t('image.afterWorktree') }\n}\n\n/** 一侧画不出来时的原因文案(每一侧各有各的原因)。 */\nexport function sideReasonText(side: ImageSide, t: Translate): string {\n if (side.kind === 'too-large') {\n return t('image.tooLarge', {\n limit: String(Math.round((typeof side.limit === 'number' ? side.limit : 0) / 1024 / 1024)),\n size: formatBytes(side.bytes),\n })\n }\n if (side.kind === 'not-image') return t('image.notImage', { size: formatBytes(side.bytes) })\n return t('image.missing')\n}\n\n/**\n * 两张都画不出来时说什么:能说清原因就说原因,否则退回原来那句话。\n * \"太大\"优先于\"不是图\" —— 前者才是用户会想处理的那件事。\n */\nexport function imageFallbackText(t: Translate, before: ImageSide, after: ImageSide): string {\n var sides = [before, after]\n var tooLarge = sides.filter(function (side) {\n return side.kind === 'too-large'\n })[0]\n if (tooLarge !== undefined) return sideReasonText(tooLarge, t)\n var notImage = sides.filter(function (side) {\n return side.kind === 'not-image'\n })[0]\n if (notImage !== undefined) return sideReasonText(notImage, t)\n return t('diff.binary')\n}\n\n/* ----------------------------------------- 轮尾:本轮改过的图片(一条门) */\n\n/** 按扩展名粗筛\"这看起来是张图\"。 */\nexport function looksLikeImage(file: unknown): boolean {\n if (typeof file !== 'string') return false\n var dot = file.lastIndexOf('.')\n if (dot < 0 || dot === file.length - 1) return false\n return IMAGE_FILE_EXTENSIONS.indexOf(file.slice(dot + 1).toLowerCase()) >= 0\n}\n\n/**\n * store 边界:图片缓存条目由 store 的 image 片构造(形状见 `store/image.ts`),\n * 到这里只做一次\"是不是对象\"的收窄;坏值当没有缓存(会再发一次请求,行为不变)。\n */\nfunction asPairEntry(value: unknown): ImagePairEntry | null {\n return value !== null && value !== undefined && typeof value === 'object'\n ? (value as ImagePairEntry)\n : null\n}\n","/**\n * 差异视图:两侧页签、逐行渲染、折叠 hunk、复制为文本。\n */\n\nimport React from 'react'\n\nimport { ImageDiff } from './image-diff.js'\nimport { findMarked, revealRow } from '../dom.js'\nimport { errorText } from '../format.js'\nimport type {\n DiffPayload,\n DiffRow as DiffRowShape,\n DiffSide,\n HudDiffState,\n HudStore,\n Translate,\n WorktreeDiffPayload,\n} from '../shared.js'\nimport { DIFF_ADD_BG, DIFF_DEL_BG, DIFF_GUTTER_COLOR, h } from '../shared.js'\n\n/** 差异视图的两侧页签(提交差异没有这个概念,不会画)。 */\nexport type DiffTabSide = 'worktree' | 'index'\n\n/** `DiffView` 的 props:当前差异状态 + 打开它的 store + 工作区(图片预览要按它发请求)。 */\nexport interface DiffViewProps {\n t: Translate\n diff: HudDiffState\n store: HudStore\n cwd?: string | null\n}\n\n/** `DiffRow` 的 props:一行解析好的差异 + 它的 hunk 归属与折叠状态。 */\nexport interface DiffRowProps {\n row: DiffRowShape\n /** 自动换行(store 里记住的开关)。 */\n wrap?: boolean\n /** 这一行属于第几个 hunk(头之前的行是 `null`)。 */\n hunk?: number | null\n /** hunk 头是否\"当前正在看\"的那一段。 */\n active?: boolean\n /** hunk 是否被折叠。 */\n collapsed?: boolean\n /** hunk 头的悬停说明(\"点一下折叠\")。 */\n hint?: string\n /** 点 hunk 头:折叠/展开这一段。 */\n onToggle?: (() => void) | null\n}\n\n/** `visibleDiffRows()` 的输出项:要渲染的行 + 它属于哪个 hunk。 */\nexport interface VisibleDiffEntry {\n row: DiffRowShape\n /** hunk 序号;hunk 头之前的行是 `null`。 */\n hunk: number | null\n}\n\nexport function DiffView(props: DiffViewProps) {\n var t = props.t\n var diff = props.diff\n var store = props.store\n // 图片预览要按工作区发请求;文本 diff 用不到它。\n var cwd = props.cwd\n\n var copiedRef = React.useState(false)\n var copied = copiedRef[0]\n var setCopied = copiedRef[1]\n // 复制的\"已复制\"提示要 1.5s 后收回;定时器句柄得留一份好清掉。\n var timerRef = React.useState<number | null>(null)\n\n var isCommit = diff.source === 'commit'\n // 提交差异没有\"侧\"这个概念,但只要不是提交,页签就得知道自己亮的是哪个。\n var side: DiffTabSide = diff.side === 'index' ? 'index' : 'worktree'\n var data = diffPayload(diff)\n var untracked = isCommit ? false : payloadUntracked(diff.data)\n var hunk = typeof diff.hunk === 'number' ? diff.hunk : 0\n var collapsed = diff.collapsed || []\n var rows = data && data.rows ? data.rows : []\n\n // 换了文件 / 侧 / 提交:回到顶部。否则会停在上一份 diff 的滚动位置\n // (React 复用同一个容器节点,scrollTop 会留下来)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (host && typeof host.scrollTop === 'number') host.scrollTop = 0\n },\n [diff.file, diff.sha, diff.side],\n )\n\n // 键盘移动 hunk:把当前 hunk 滚进视野(已经看得见就一点都不动)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (!host || typeof host.querySelector !== 'function') return\n revealRow(\n host.querySelector('[data-dsh-hud=\"hunk\"][data-hunk-index=\"' + String(hunk) + '\"]'),\n 'dshh-diff',\n )\n },\n [hunk, diff.file, diff.sha, diff.side, collapsed.length],\n )\n\n function onCopy(): void {\n if (!data) return\n var clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined\n if (!clipboard || typeof clipboard.writeText !== 'function') return\n Promise.resolve(clipboard.writeText(rowsToText(data.rows || [])))\n .then(function () {\n setCopied(true)\n if (timerRef[0]) window.clearTimeout(timerRef[0])\n timerRef[1](\n window.setTimeout(function () {\n setCopied(false)\n }, 1500),\n )\n })\n .catch(function () {\n // 剪贴板被拒绝(无权限/非安全上下文)时不谎报成功。\n })\n }\n\n function tab(kind: DiffTabSide, label: string, title: string): React.ReactElement {\n var active = side === kind\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n title: title,\n onClick: function () {\n store.setDiffSide(kind)\n },\n },\n label,\n )\n }\n\n var counts =\n data && data.binary !== true\n ? h(\n 'span',\n { className: 'dshh-difcounts' },\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-success-primary, #3fb950)' } },\n '+' + String(data.additions),\n ),\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-error-primary, #f06a6a)' } },\n '-' + String(data.deletions),\n ),\n )\n : null\n\n var body: React.ReactElement\n if (diff.phase === 'loading') {\n body = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else if (diff.phase === 'error') {\n body = h('div', { className: 'dshh-empty' }, errorText(diff.errorCode, t))\n } else if (!data) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else if (data.binary === true) {\n // 二进制里最常被改的就是图:能认出来就画出来,认不出才退回那句话。\n body = h(ImageDiff, { t: t, store: store, diff: diff, cwd: cwd })\n } else if (rows.length === 0) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else {\n var visible = visibleDiffRows(rows, collapsed)\n body = h(\n 'div',\n { className: 'dshh-diff', 'data-dsh-hud': 'diff' },\n visible.map(function (entry, index) {\n // hunk 序号只在 hunk 头及其后面的行上有;头之前的行是 null。\n var entryHunk = entry.hunk === null ? -1 : entry.hunk\n var isHunk = entry.row.kind === 'hunk'\n return h(DiffRow, {\n key: String(index) + ':' + entry.row.kind,\n row: entry.row,\n wrap: diff.wrap === true,\n hunk: entry.hunk,\n active: isHunk && entry.hunk === hunk && collapsed.indexOf(entryHunk) < 0,\n collapsed: isHunk && collapsed.indexOf(entryHunk) >= 0,\n hint: isHunk ? t('diff.hunkToggle') : undefined,\n onToggle: isHunk\n ? function () {\n store.toggleHunk(entry.hunk === null ? undefined : entry.hunk)\n }\n : undefined,\n })\n }),\n data.truncated === true ? h('div', { className: 'dshh-note' }, t('diff.truncated')) : null,\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-diffview' },\n h(\n 'div',\n { className: 'dshh-toolbar' },\n h('span', { className: 'dshh-difpath', title: diff.path }, diff.path),\n counts,\n h('span', { className: 'dshh-spacer' }),\n // 提交差异只有一个\"侧\"(相对父提交),因此不给 未暂存/已暂存 页签\n // (短 sha 与标题在右栏的明细头里,这里不重复)。\n untracked ? h('span', { className: 'dshh-kv' }, t('diff.untrackedHint')) : null,\n isCommit ? null : tab('worktree', t('diff.worktree'), t('diff.worktree')),\n isCommit || untracked ? null : tab('index', t('diff.index'), t('diff.index')),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (diff.wrap === true ? ' dshh-tabActive' : ''),\n 'aria-pressed': diff.wrap === true ? 'true' : 'false',\n title: t('diff.wrap'),\n onClick: function () {\n store.toggleDiffWrap()\n },\n },\n t('diff.wrap'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('diff.copy'),\n onClick: onCopy,\n },\n copied ? t('diff.copied') : t('diff.copy'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n // 提交明细里这个 ✕ 收的是整块明细(差异是明细的一段),文案跟着改。\n title: isCommit ? t('commit.close') : t('diff.close'),\n 'aria-label': isCommit ? t('commit.close') : t('diff.close'),\n onClick: function () {\n store.closeDetail()\n },\n },\n '✕',\n ),\n ),\n body,\n )\n}\n\n/**\n * 一行差异:两个行号槽 + 文本。\n *\n * hunk 头是**可点**的(折叠 / 展开这一段),因此它第一个行号槽放的是折叠标记,\n * 不是行号——那个位置本来就空着,放标记不会让文本错位。\n */\nexport function DiffRow(props: DiffRowProps): React.ReactElement {\n var row = props.row\n var style =\n row.kind === 'add'\n ? { background: DIFF_ADD_BG }\n : row.kind === 'del'\n ? { background: DIFF_DEL_BG }\n : null\n var gutterStyle = { color: DIFF_GUTTER_COLOR }\n var text = row.text === '' ? '\\u00a0' : row.text\n\n if (row.kind === 'hunk') {\n return h(\n 'div',\n {\n className: 'dshh-difrow dshh-difrow-hunk' + (props.active ? ' dshh-difrow-hunkActive' : ''),\n 'data-dsh-hud': 'hunk',\n 'data-hunk-index':\n props.hunk === null || props.hunk === undefined ? undefined : String(props.hunk),\n title: props.hint,\n onClick: props.onToggle,\n },\n h('span', { className: 'dshh-difno', style: gutterStyle }, ''),\n h(\n 'span',\n { className: 'dshh-difno dshh-hunkMark', style: gutterStyle },\n props.collapsed ? '▸' : '▾',\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-difrow dshh-difrow-' + row.kind, style: style },\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.oldLine === null ? '' : String(row.oldLine),\n ),\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.newLine === null ? '' : String(row.newLine),\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n}\n\n/**\n * 把 rows 整理成\"要渲染的行 + 它属于哪个 hunk\"。\n *\n * hunk 段从 hunk 头开始,到下一个 hunk 头(或结尾)结束;折叠某个 hunk 就是\n * 把该段里除头以外的行丢掉。hunk 头之前那些行(`diff --git`、`---`、`+++`)\n * 永远显示——它们是\"这是哪个文件\"的上下文。\n *\n * @returns `[{ row, hunk }]`,`hunk` 是 hunk 序号(头之前的行是 null,收起时是负数)。\n */\nexport function visibleDiffRows(rows: DiffRowShape[], collapsed: number[]): VisibleDiffEntry[] {\n var folded = collapsed || []\n var out: VisibleDiffEntry[] = []\n var ordinal = -1\n for (var index = 0; index < rows.length; index += 1) {\n var row = rows[index]\n if (row.kind === 'hunk') {\n ordinal += 1\n out.push({ row: row, hunk: ordinal })\n continue\n }\n if (ordinal >= 0 && folded.indexOf(ordinal) >= 0) continue\n out.push({ row: row, hunk: ordinal >= 0 ? ordinal : null })\n }\n return out\n}\n\n/** 把 rows 还原成 unified diff 文本(\"复制差异\"用)。 */\nexport function rowsToText(rows: DiffRowShape[]): string {\n return rows\n .map(function (row) {\n if (row.kind === 'add') return '+' + row.text\n if (row.kind === 'del') return '-' + row.text\n if (row.kind === 'ctx') return ' ' + row.text\n return row.text\n })\n .join('\\n')\n}\n\n/** 工作区差异的载荷形状:两侧都在。提交差异是单侧,没有这个形状。 */\nfunction isWorktreePayload(payload: DiffPayload): payload is WorktreeDiffPayload {\n return 'worktree' in payload && 'index' in payload\n}\n\n/** 工作区差异载荷里的 `untracked`;单侧载荷(提交差异)没有这个字段。 */\nfunction payloadUntracked(payload: DiffPayload | null): boolean {\n return payload !== null && isWorktreePayload(payload) ? payload.untracked === true : false\n}\n\n/** 当前这一侧的 rows 载体:提交差异只有一侧,工作区差异要按 side 取。 */\nexport function diffPayload(diff: HudDiffState | null | undefined): DiffSide | null {\n if (!diff || !diff.data) return null\n // 域边界:来源是 commit 时宿主只回单侧载荷(`DiffSide`),类型上 `data` 是联合,这里收窄一次。\n if (diff.source === 'commit') return diff.data as DiffSide\n // 工作区差异的 `data` 一定是两侧载荷;收窄成单侧只是给类型看的,取的是同一份 rows。\n if (!isWorktreePayload(diff.data)) return null\n return diff.data[diff.side === 'index' ? 'index' : 'worktree'] || null\n}\n","/**\n * 标签列表(面板底部):只在有标签时渲染,点了复制名字。\n */\n\nimport type React from 'react'\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudTag, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `TagList` 的 props:标签清单 + HEAD 比对 + 打开回调。 */\nexport interface TagListProps {\n /** `null` = 还没取到(整段不画);`[]` = 取回了、确实一个都没有(画一行说明)。 */\n tags?: HudTag[] | null\n t: Translate\n /** 当前 HEAD 的 oid(用来给指向 HEAD 的标签标 HEAD 徽标)。 */\n headSha?: string | null\n /** 提交明细当前打开的那条。 */\n activeSha?: string | null\n truncated?: boolean\n onOpen?: ((tag: HudTag) => void) | null\n}\n\n/**\n * 标签列表:一行一个,tag 名 + 它指向的提交 sha + 相对时间;指向当前 HEAD 的标 HEAD。\n * 点一行 = 用右栏的提交明细看这个 tag 指向的那次提交(复用同一条管道,不新增界面)。\n */\nexport function TagList(props: TagListProps): React.ReactElement | null {\n // 原样保留 `null`(\"还没取到\")—— 写成 `props.tags || []` 会把\"没取到\"和\"确实没有\"合并,\n // 于是面板一打开就先闪一行\"还没有标签\"。\n var tags = props.tags\n var t = props.t\n // `null` = 还没取到(先不画,避免闪一下\"还没有标签\");`[]` = 取回了、确实一个都没有 →\n // 这时要**画一行写明**:整段不画的话,用户没法区分\"这个仓库没有 tag\"与\"这个功能没做\"(实测踩过)。\n if (!Array.isArray(tags)) return null\n if (tags.length === 0) {\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h('div', { className: 'dshh-note', 'data-tags-empty': '1' }, t('panel.tagsEmpty')),\n )\n }\n var headSha = props.headSha\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h(\n 'div',\n { className: 'dshh-commits' },\n tags.map(function (tag, index) {\n var isHead =\n typeof headSha === 'string' &&\n headSha.length > 0 &&\n (tag.sha === headSha || headSha.indexOf(tag.sha) === 0)\n var tip = [\n t('tag.click'),\n '',\n tag.subject || '',\n t(tag.annotated ? 'tag.annotated' : 'tag.lightweight') +\n ' · ' +\n tag.sha +\n ' · ' +\n (typeof tag.committedAt === 'number' ? fullWhen(tag.committedAt) : tag.date || ''),\n ].join('\\n')\n var open = sameCommit(tag.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n 'data-tag-row': tag.name,\n key: tag.name + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(tag)\n },\n },\n h('span', { className: 'dshh-ctag' }, tag.name),\n h('span', { className: 'dshh-csha' }, tag.sha),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(tag, t)),\n )\n }),\n ),\n props.truncated === true\n ? h('div', { className: 'dshh-note' }, t('panel.tagsCapped', { n: String(tags.length) }))\n : null,\n )\n}\n","/**\n * 面板的三段行为,各自一个 hook:键盘与\"点进来\"手势、视口变化后夹回位置、量 dock 行位置。\n *\n * 提成 hook 的理由:这些是 `FloatingPanel` 里最费劲的部分,而它们只依赖 `store` 与几个快照字段 ——\n * 组件从此只剩\"hooks 调用 + JSX\"。\n */\n\nimport { activeElement, isInsidePanel, isTypingTarget, measureDockTop } from '../dom.js'\nimport { clampPos, panelWidthOf, visibleChangeRows } from '../shared.js'\nimport type { HudStatus, HudStore, PanelPos } from '../shared.js'\nimport React from 'react'\n\n/**\n * 根作用域的座位:读不到当前会话,只读共享 store。\n * 收起时渲染 `null` —— 这样不会在输入框上留一个挡点击的气泡。\n */\n/**\n * 面板的键盘与\"点进来\"手势:Esc 逐层退(右栏 → 收起面板),其余键只在面板被激活时接管。\n *\n * 提成顶层 hook 的理由:这段是面板里最费劲的一部分,而它只依赖 `store` 与 `panelOpen`\n * (`armed` 是就地可变的对象,按引用传进来,语义与原来完全一致)。\n */\nexport function usePanelKeys(\n store: HudStore,\n panelOpen: boolean,\n armed: { inside: boolean },\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var onKeyDown = function (event: KeyboardEvent) {\n // Esc 任何时候都收(和以前一样:焦点在输入框里也能收到)。\n if (event.key === 'Escape') {\n // 和 dsh-git 一样\"退一层\":右栏(差异 / 提交明细)→ 收起面板。\n var current = store.getSnapshot()\n if (current.diff || current.commit) store.closeDetail()\n else store.openPanel(false)\n return\n }\n\n // 其余按键只在面板被激活时接管:先在面板里点一下(或 Tab 进去)。\n // 本插件是挂在输入框上方的浮层,抢键的代价(打字、翻菜单、滚会话\n // 全被吃掉)远大于多几个快捷键,所以宁可要一次\"点进来\"。\n if (!armed.inside && !isInsidePanel(activeElement())) return\n // 正在输入时一律不管。\n if (isTypingTarget(event.target)) return\n // 带修饰键的组合交给产品与浏览器。\n if (event.metaKey || event.ctrlKey || event.altKey) return\n\n var handled = true\n if (event.key === 'ArrowDown') store.stepFile(1, false)\n else if (event.key === 'ArrowUp') store.stepFile(-1, false)\n else if (event.key === ']') store.stepFile(1, true)\n else if (event.key === '[') store.stepFile(-1, true)\n else if (event.key === 'Enter') store.openCursor()\n else if (event.key === 'n') store.stepHunk(1)\n else if (event.key === 'p') store.stepHunk(-1)\n else if (event.key === ' ' || event.code === 'Space') {\n var openDiff = store.getSnapshot().diff\n store.toggleHunk(openDiff ? openDiff.hunk : 0)\n } else if (event.key === 'r') store.refresh()\n // `v`:把当前聚焦的那一行标成\"这一版我审过了\"(再按一次取消)。\n // 用键盘而不是给行尾那个标记加 tabindex:行本身是按钮,里面再塞一个按钮是无效结构。\n else if (event.key === 'v') {\n var snapshot = store.getSnapshot()\n var rows = visibleChangeRows(snapshot)\n var target =\n snapshot.cursor !== null && snapshot.cursor !== undefined ? snapshot.cursor : null\n if (target === null) target = rows[0] ? rows[0].file : null\n if (target !== null) store.toggleReviewed(target)\n } else handled = false\n\n if (handled) {\n // 认领了的键就别再让浏览器滚动页面,也别让产品再处理一遍。\n event.preventDefault()\n event.stopPropagation()\n }\n }\n\n var onPointerDown = function (event: PointerEvent) {\n armed.inside = isInsidePanel(event.target)\n }\n\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('pointerdown', onPointerDown, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('pointerdown', onPointerDown, true)\n }\n },\n [panelOpen, store],\n )\n}\n\n/** 视口变化后把已记忆的面板位置夹回可见区域。 */\nexport function usePanelClamp(store: HudStore, pos: PanelPos | null): void {\n React.useEffect(\n function () {\n if (!pos) return undefined\n var onResize = function () {\n var next = clampPos(pos.left, pos.top, panelWidthOf(store))\n if (next.left !== pos.left || next.top !== pos.top) store.movePanel(next)\n }\n window.addEventListener('resize', onResize)\n return function () {\n window.removeEventListener('resize', onResize)\n }\n },\n [pos, store],\n )\n}\n\n/** 量 dock 行位置:面板据此贴到胶囊上方(会话/状态变化都会重量一次,所以输入框移位时面板跟着走)。 */\nexport function useDockAnchor(\n store: HudStore,\n panelOpen: boolean,\n sessionId: string | null | undefined,\n status: HudStatus | null,\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var measure = function () {\n store.setAnchorTop(measureDockTop())\n }\n measure()\n window.addEventListener('resize', measure)\n return function () {\n window.removeEventListener('resize', measure)\n }\n },\n [panelOpen, sessionId, status, store],\n )\n}\n","/**\n * 悬浮面板本体:头部、概览、左栏改动清单、中间提交栏、右栏差异/提交明细,以及拖拽与抓握。\n *\n * 三段行为(键盘、视口夹回、dock 锚点)在 `hooks/panel.ts` 里;这里只剩 hooks 调用与 JSX。\n */\n\nimport { reviewView } from '../shared.js'\nimport React from 'react'\n\nimport { ChangeList } from './change-list.js'\nimport { CommitList } from './commit-list.js'\nimport { CommitPane } from './commit-pane.js'\nimport { CountChips } from './count-chips.js'\nimport { DiffView } from './diff-view.js'\nimport { SessionChangesLine } from './session-changes.js'\nimport { TagList } from './tag-list.js'\nimport { branchLabel, errorText, timeLabel } from '../format.js'\nimport { useDockAnchor, usePanelClamp, usePanelKeys } from '../hooks/panel.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudChange, HudLogEntry, HudStore, HudTag, Translate } from '../shared.js'\nimport {\n COMMIT_LIST_WIDTH,\n COMPOSER_OFFSET,\n DIFF_DEFAULT_WIDTH,\n DIFF_PANEL_HEIGHT_VH,\n DRAG_THRESHOLD_PX,\n PANEL_DOCK_GAP,\n PANEL_WIDTH,\n VIEWPORT_MARGIN,\n clampPos,\n h,\n panelLayout,\n panelWidthFor,\n panelWidthOf,\n sessionChangeView,\n visibleChangeRows,\n} from '../shared.js'\n\n/** 头部拖拽的现场(指针 id + 起点 + 按下时指针在面板内的偏移)。 */\ninterface DragState {\n pointerId: number\n startX: number\n startY: number\n offsetX: number\n offsetY: number\n}\n\n/** 抓握拖拽的现场(调哪一栏 + 指针 id + 起点与起始宽度)。 */\ninterface GripState {\n kind: 'diff' | 'commit'\n pointerId: number\n startX: number\n startWidth: number\n}\n\n/** `HudFloatingPanel` 的 props:共享 store(面板状态都在它的快照里)。 */\nexport interface HudFloatingPanelProps {\n t?: Translate\n store: HudStore\n}\n\nexport function HudFloatingPanel(props: HudFloatingPanelProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var store = props.store\n var state = useStore(store)\n\n var dragRef = React.useState<DragState | null>(null)\n var drag = dragRef[0]\n var setDrag = dragRef[1]\n\n var gripRef = React.useState<GripState | null>(null)\n /**\n * \"面板已激活\"标记:在面板里按过指针(或 Tab 进去)才允许接管裸按键。\n *\n * 用可变对象而不是 React state:事件处理里读的必须是最新值,而重建监听\n * 只依赖 `panelOpen`,改这个标记不该触发重渲染。\n * 必须和其它 hook 一起放在下面那个 `!panelOpen → null` 早退之前。\n */\n var armedState = React.useState(function () {\n return { inside: false }\n })\n var armed = armedState[0]\n usePanelKeys(store, state.panelOpen, armed)\n usePanelClamp(store, state.pos)\n useDockAnchor(store, state.panelOpen, state.sessionId, state.status)\n var grip = gripRef[0]\n var setGrip = gripRef[1]\n\n /**\n * 拖动:只在头部按下、并且**不是**按在按钮上时开始;移动走 window 监听,\n * 刻意**不用** `setPointerCapture` —— 一旦在头部捕获指针,`pointerup` 会被\n * 重定向到头部,`click` 便落在按下与松开的公共祖先(头部)上,按钮的\n * `onClick` 永远不触发,面板就「关不掉」了。\n */\n React.useEffect(\n function () {\n if (!drag) return undefined\n // 闭包里用局部常量保住上面的非空收窄(`drag` 是 `var`,TS 在回调里会重新放宽成可空)。\n const activeDrag = drag\n var moved = false\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n if (\n !moved &&\n Math.abs(event.clientX - activeDrag.startX) < DRAG_THRESHOLD_PX &&\n Math.abs(event.clientY - activeDrag.startY) < DRAG_THRESHOLD_PX\n )\n return\n moved = true\n store.movePanel(\n clampPos(\n event.clientX - activeDrag.offsetX,\n event.clientY - activeDrag.offsetY,\n panelWidthOf(store),\n ),\n )\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n setDrag(null)\n // 只有真的拖动过才落盘:头部上的普通点击不该改变位置。\n if (moved) {\n // 拖动过程中 movePanel 已经把位置写进快照,这里收窄只是 strictNullChecks 需要。\n var next = store.getSnapshot().pos\n if (next !== null) store.commitPanel(next)\n }\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [drag, store],\n )\n\n /**\n * 拖动面板**右边缘**调整差异栏宽度(与 dsh-git 的抓握位置一致):\n * 往左拖变宽、往右拖变窄;松手才落盘。\n */\n React.useEffect(\n function () {\n if (!grip) return undefined\n // 同拖动:回调里用局部常量保住非空收窄。\n const activeGrip = grip\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n if (activeGrip.kind === 'commit')\n store.setCommitWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n else store.setDiffWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n setGrip(null)\n store.persistLayout()\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [grip, store],\n )\n\n if (!state.panelOpen) return null\n\n /**\n * 抓握按下。\n *\n * `kind` 决定它调哪一栏:`diff` = 面板右边缘那条(调差异栏);`commit` = 提交栏右边那条\n * (调提交明细栏)。只有提交栏、没有差异栏时,右边缘那条也调提交栏 —— 它是右边界上唯一\n * 还能变宽的东西,不该按下去没反应。\n */\n function onGripPointerDown(\n kind: GripState['kind'],\n event: React.PointerEvent<HTMLDivElement>,\n ): void {\n if (event.button !== 0) return\n var snapshot = store.getSnapshot()\n event.preventDefault()\n var start =\n kind === 'commit'\n ? typeof snapshot.commitWidth === 'number'\n ? snapshot.commitWidth\n : COMMIT_LIST_WIDTH\n : typeof snapshot.diffWidth === 'number'\n ? snapshot.diffWidth\n : DIFF_DEFAULT_WIDTH\n setGrip({ kind: kind, pointerId: event.pointerId, startX: event.clientX, startWidth: start })\n }\n\n function onHeaderPointerDown(event: React.PointerEvent<HTMLDivElement>): void {\n if (event.button !== 0) return\n // DOM 边界:事件的 target 是 EventTarget,`closest` 只存在于 Element 上,按能力判断前先收窄。\n var target = event.target as Element | null\n // 头部里的按钮要保持普通点击,否则拖动会吞掉它们。\n if (target && typeof target.closest === 'function' && target.closest('button')) return\n // DOM 边界:头部的父节点就是面板容器(渲染结构固定),这里按元素取几何。\n var rect = (event.currentTarget.parentNode as HTMLElement).getBoundingClientRect()\n setDrag({\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n offsetX: event.clientX - rect.left,\n offsetY: event.clientY - rect.top,\n })\n }\n\n function onHeaderDoubleClick(): void {\n store.resetPanel()\n }\n\n var layout = panelLayout(state)\n var diffMode = layout.diff\n // 提交明细在场时右栏也在场(明细是中间那一栏,差异是右边那一栏),\n // 而当时 `state.diff` 可能还在路上 —— 因此两种情形都算\"宽面板\"。\n var commitMode = layout.commit\n var wide = diffMode || commitMode\n var dockTop = typeof state.anchorTop === 'number' ? state.anchorTop : null\n // 贴到胶囊所在行的上方;量不到就退回 dsh-git 记录的那次测量(168px)。\n var anchorBottom =\n dockTop === null\n ? COMPOSER_OFFSET\n : Math.max(VIEWPORT_MARGIN, window.innerHeight - dockTop + PANEL_DOCK_GAP)\n\n var panelStyle: React.CSSProperties = {\n position: 'fixed',\n zIndex: 30,\n boxSizing: 'border-box',\n width: panelWidthFor(layout, state.diffWidth, state.commitWidth),\n maxWidth: 'calc(100vw - 16px)',\n display: 'flex',\n flexDirection: 'column',\n border: '1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))',\n borderRadius: 12,\n // 表面用\"抬升面\"令牌:DSH 自己的浮层几乎都用 bg-layer-1,而 bg-overlay\n // 在暗色下是中灰 bluish-700,拿它当面板底色会发灰、与产品观感不一致。\n background: 'var(--dsw-alias-bg-layer-1, #1f2126)',\n boxShadow: 'var(--dsw-shadow-lv2, 0 4px 12px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.12))',\n overflow: 'hidden',\n pointerEvents: 'auto',\n }\n\n if (wide) {\n // 固定高度:面板以 bottom 锚定,若只给 max-height,差异到达时面板会长高并整体跳位。\n var spaceAbove =\n (dockTop === null ? window.innerHeight - anchorBottom : dockTop - PANEL_DOCK_GAP) -\n VIEWPORT_MARGIN\n panelStyle.height = Math.max(\n 240,\n Math.min(Math.round(window.innerHeight * DIFF_PANEL_HEIGHT_VH), spaceAbove),\n )\n } else {\n panelStyle.maxHeight = '62vh'\n }\n\n // 未拖动过:锚在胶囊上方居中;拖动过:显式坐标。\n if (state.pos) {\n panelStyle.left = state.pos.left\n panelStyle.top = state.pos.top\n } else {\n panelStyle.left = '50%'\n panelStyle.bottom = anchorBottom\n panelStyle.transform = 'translateX(-50%)'\n }\n\n // 单栏内容(\"面板本来就长这样\"):两种模式共用同一份,\n // 打开差异时它原封不动地留在左栏,只有右栏是新出现的。\n // \"正在看的那份差异\"与\"键盘选中的那行\"是两件事:前者是打开的文件,\n // 后者可能只是选过还没打开(↑/↓ 移动,Enter 才打开)。两个标记都留着。\n // 左栏那份清单只关心工作区差异:提交明细打开时它不该跟着高亮。\n var openDiff = state.diff\n var openCommit = state.commit\n // 工作区差异(含未暂存/已暂存两页签)与提交差异是两种右栏内容。\n var openFile = openDiff !== null && openDiff.source !== 'commit' ? openDiff.file : null\n var commitOpenFile = openDiff !== null && openDiff.source === 'commit' ? openDiff.file : null\n\n // 会话归因视图与过滤后的清单:渲染期只算一次(标记、过滤、概览那行共用)。\n var sessionView = sessionChangeView(state)\n var sessionRows = visibleChangeRows(state)\n\n var emptyBody: React.ReactElement\n if (state.phase === 'error') {\n emptyBody = h('div', { className: 'dshh-empty' }, errorText(state.errorCode, t))\n } else if (!state.cwd) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.noSession'))\n } else if (state.phase === 'loading' && !state.status) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.notRepo'))\n }\n\n var listBody\n if (state.status && state.status.repo) {\n var status = state.status\n var repoChildren: React.ReactNode[] = [\n h(\n 'div',\n { className: 'dshh-meta' },\n h('span', { className: 'dshh-repo', title: status.repo }, status.repo),\n status.upstream\n ? h('span', { className: 'dshh-kv' }, t('panel.upstream') + ' ' + status.upstream)\n : null,\n status.ahead > 0\n ? h('span', { className: 'dshh-kv' }, '↑ ' + t('panel.ahead') + String(status.ahead))\n : null,\n status.behind > 0\n ? h('span', { className: 'dshh-kv' }, '↓ ' + t('panel.behind') + String(status.behind))\n : null,\n status.oid ? h('span', { className: 'dshh-kv' }, status.oid) : null,\n ),\n h('div', { className: 'dshh-sectionTitle' }, t('panel.summary')),\n h(CountChips, { counts: status.counts, t: t }),\n h(SessionChangesLine, {\n t: t,\n view: sessionView,\n onlySession: state.onlySession === true,\n onToggle: function () {\n store.toggleOnlySession()\n },\n onlyLatest: state.onlyLatest === true,\n onToggleLatest: function () {\n store.toggleOnlyLatest()\n },\n review: reviewView(state),\n onClearReviewed: function () {\n store.clearReviewed()\n },\n }),\n // 顺序:改动概览 → **改动清单** → 最近提交。清单是\"现在要处理的\",\n // 提交是背景信息;清单放前面,一屏不够时先看到的是眼前的事。\n sessionRows.length > 0\n ? h(ChangeList, {\n changes: sessionRows,\n truncated: status.truncated,\n t: t,\n onOpen: function (change: HudChange) {\n store.openDiff(change)\n },\n activeFile: openFile,\n cursor: commitMode ? null : state.cursor,\n listId: 'changes',\n sessionView: sessionView,\n review: reviewView(state),\n onToggleReviewed: function (file: string) {\n store.toggleReviewed(file)\n },\n })\n : state.status.changes.length > 0 && state.onlySession === true && sessionView !== null\n ? // 过滤器开着、本会话又什么都没改:说一句,而不是让清单整段消失。\n h('div', { className: 'dshh-note' }, t('session.empty'))\n : null,\n h(CommitList, {\n commits: state.commits,\n t: t,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 再点一次打开着的那条 = 收起明细(打开提交不改左栏,只高亮它)。\n onOpen: function (commit: HudLogEntry) {\n store.toggleCommit(commit)\n },\n }),\n h(TagList, {\n tags: state.tags,\n truncated: state.tagsTruncated,\n t: t,\n headSha: state.status && state.status.oid ? state.status.oid : null,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 点一个 tag = 看它指向的那次提交(tag 自己不是提交,所以把提交信息填进去)。\n onOpen: function (tag: HudTag) {\n store.toggleCommit({\n sha: tag.sha,\n subject: tag.subject || '',\n author: '',\n date: tag.date || '',\n committedAt: typeof tag.committedAt === 'number' ? tag.committedAt : null,\n refs: [tag.name],\n })\n },\n }),\n ]\n listBody = h('div', { className: 'dshh-body' }, ...repoChildren)\n } else {\n listBody = emptyBody\n }\n\n // 工作台:左栏**始终**是上面那份单栏内容(宽度也与单栏一致);打开提交明细时\n // 中间多一栏(这次提交的元信息 + 改动清单),差异固定在右边那一栏。\n // 左栏一个像素都不动,面板右边缘的抓握仍只调整差异栏——与 dsh-git 的 workbench 同形。\n var body = wide\n ? h(\n 'div',\n { className: 'dshh-workbench' },\n h('div', { className: 'dshh-leftpane', style: { width: PANEL_WIDTH } }, listBody),\n commitMode && openCommit !== null\n ? h(CommitPane, {\n t: t,\n commit: openCommit,\n onOpenFile: function (file) {\n store.openCommitDiff(file)\n },\n activeFile: commitOpenFile,\n cursor: state.cursor,\n width: typeof state.commitWidth === 'number' ? state.commitWidth : COMMIT_LIST_WIDTH,\n })\n : null,\n commitMode && diffMode\n ? h('div', {\n className: 'dshh-grip dshh-gripBetween',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n diffMode && openDiff !== null\n ? h(DiffView, { t: t, diff: openDiff, store: store, cwd: state.cwd })\n : null,\n )\n : listBody\n\n return h(\n 'div',\n { className: 'dshh-panel', 'data-dsh-hud': 'panel', style: panelStyle },\n h(\n 'div',\n {\n className: 'dshh-header',\n title: t('panel.dragHint'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onHeaderPointerDown(event)\n },\n onDoubleClick: onHeaderDoubleClick,\n },\n h('span', { className: 'dshh-title' }, t('panel.title')),\n state.status && state.status.repo\n ? h('span', { className: 'dshh-branch' }, branchLabel(state.status, t))\n : null,\n h('span', { className: 'dshh-spacer' }),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.refresh'),\n 'aria-label': t('panel.refresh'),\n onClick: function () {\n store.refresh()\n },\n },\n '⟳',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.reset'),\n 'aria-label': t('panel.reset'),\n onClick: function () {\n store.resetPanel()\n },\n },\n '⌖',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.close'),\n 'aria-label': t('panel.close'),\n onClick: function () {\n store.openPanel(false)\n },\n },\n '✕',\n ),\n ),\n body,\n diffMode\n ? h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip',\n title: t('diff.resize'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('diff', event)\n },\n })\n : commitMode\n ? // 只有提交栏:右边缘这条改成调它(否则按下去没反应)。\n h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n h(\n 'div',\n { className: 'dshh-footer' },\n h('span', null, t('panel.readonly')),\n h('span', null, state.updatedAt ? t('panel.updated') + timeLabel(state.updatedAt) : ''),\n ),\n )\n}\n","/**\n * 悬停气泡本体:位置由 store 里的 tip 快照给(`shell.overlay` 座位里渲染)。\n */\n\nimport type React from 'react'\n\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `PillTip` 的 props:共享 store(气泡文案与锚点都在它的快照里)。 */\nexport interface PillTipProps {\n store: HudStore\n}\n\n/**\n * 胶囊悬停提示的气泡。**自己画**,不用浏览器原生 `title`。\n *\n * 由一个独立的 `shell.overlay` 座位渲染、`position:fixed` 定位 —— 这样它不受 dock 那行的\n * 裁切与堆叠上下文限制。位置只用锚点矩形的 `top` 与 `right`:\n * 贴在那枚胶囊**上方**,横向按视口夹住(不量自己的宽度,避免\"先渲染再回填\"的抖动)。\n */\nexport function PillTip(props: PillTipProps): React.ReactElement | null {\n var store = props.store\n var state = useStore(store)\n var tip = state.tip\n\n if (tip === null || tip === undefined) return null\n var innerWidth = typeof window.innerWidth === 'number' ? window.innerWidth : 1200\n var innerHeight = typeof window.innerHeight === 'number' ? window.innerHeight : 800\n var margin = 8\n var left = Math.max(\n margin,\n Math.min(typeof tip.rect.left === 'number' ? tip.rect.left : margin, innerWidth - 428),\n )\n var bottom = Math.max(\n margin,\n innerHeight - (typeof tip.rect.top === 'number' ? tip.rect.top : 0) + 6,\n )\n\n return h(\n 'div',\n {\n className: 'dshh-tip',\n 'data-dsh-hud': 'pill-tip',\n role: 'tooltip',\n style: { left: String(left) + 'px', bottom: String(bottom) + 'px' },\n },\n tip.text,\n )\n}\n","/**\n * 侧栏那两处按需展示:⋯ 菜单里的会话摘要、悬停卡片。\n */\n\nimport { branchLabel, rowSummaryOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `⋯` 菜单摘要的 props(座位 `sidebar.workspaces.session.menu.item`)。 */\nexport interface SessionMenuSummaryProps {\n t?: Translate\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n store: HudStore\n}\n\n/** 悬停卡片的 props(座位 `sidebar.session.row.hover`)。 */\nexport interface SessionRowHoverChipProps {\n t?: Translate\n sessionId?: string | null\n store: HudStore\n}\n\n/**\n * `⋯` 菜单里的只读摘要(座位 `sidebar.workspaces.session.menu.item`)。\n *\n * 为什么要有这一处:会话行**行尾的常亮位置归产品**(`time` 自适应宽度 + 悬停才换上的动作条),\n * 插件在行尾拿不到常亮的座位;行首那格只有 16px,只能放一个双字符标记。所以要\"完整信息 +\n * 不用等悬停卡片那 800ms\",`⋯` 菜单是最实在的地方 —— 点一下就有,且不用把鼠标停住。\n *\n * 它是**只读**的:不做任何动作,所以做得像一条说明(弱化颜色、不响应 hover 高亮);\n * 工作区干净时也照实说\"干净\",而不是消失(那样用户会以为没有这个功能)。\n */\nexport function SessionMenuSummary(props: SessionMenuSummaryProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n\n var head: string\n if (summary.row.repo === true) {\n head = branchLabel(summary.row, t)\n if (summary.dirty === true) head += '*'\n var aheadOf = summary.row.ahead\n var behindOf = summary.row.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n var counts = summary.shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n head += counts === '' ? ' · ' + t('panel.clean') : ' · ' + counts\n } else if (summary.row.error !== null) {\n head = t('rows.error', { code: String(summary.row.error) })\n } else {\n head = t('rows.noRepo')\n }\n\n var own: string[] = []\n if (summary.mine > 0) own.push(t('session.pill', { n: String(summary.mine) }))\n if (summary.possible > 0) {\n own.push(t('rows.possible', { n: String(summary.possible), now: String(summary.possibleNow) }))\n }\n var ownLine = own.length === 0 ? t('rows.ownNone') : own.join(' · ')\n\n return h(\n 'div',\n {\n className: 'dshh-menureadout',\n 'data-dsh-hud': 'menu-readout',\n title: summary.row.cwd || undefined,\n },\n [\n h('div', { key: 'git', className: 'dshh-menureadoutLine' }, head),\n h('div', { key: 'own', className: 'dshh-menureadoutLine' }, ownLine),\n ],\n )\n}\n\n/**\n * 侧栏会话行**悬停卡片**里的那一块(座位 `sidebar.session.row.hover`)。\n *\n * 动作条那一枚只有图标级宽度(`!4 ●3`),明细放不下;悬停卡是宽敞的地方 ——\n * 分支/上游、分类型计数、这个会话自己的改动,都在这里说清楚。\n * 没有任何可说的(干净、且本会话没改过)就整块不画,卡片保持原来的样子。\n */\nexport function SessionRowHoverChip(props: SessionRowHoverChipProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n // 上面已确认 summary 非空,它带的 row 就是刚落下来的那一行(同一个对象)。\n var rowData = summary.row\n var mineCount = summary.mine\n var possibleCount = summary.possible\n var shorthands = summary.shorthands\n\n var lines: string[] = []\n if (rowData.repo === true) {\n var head = branchLabel(rowData, t)\n if (rowData.dirty === true) head += '*'\n var aheadOf = rowData.ahead\n var behindOf = rowData.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n if (shorthands.length > 0) {\n head +=\n ' · ' +\n shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n } else {\n // 干净也要说出来 —— 与悬停说明、⋯ 菜单同一个口径(空着看不出是\"干净\"还是\"没读到\")。\n head += ' · ' + t('panel.clean')\n }\n lines.push(head)\n } else if (rowData.repo === false && rowData.error === null) {\n lines.push(t('rows.noRepo'))\n }\n if (rowData.error) lines.push(t('rows.error', { code: String(rowData.error) }))\n var own: string[] = []\n if (mineCount > 0) own.push(t('session.pill', { n: String(mineCount) }))\n if (possibleCount > 0) {\n own.push(t('rows.possible', { n: String(possibleCount), now: String(summary.possibleNow) }))\n }\n // 和第二行对齐:没有改动记录时也明说一句 —— 否则卡片上会缺一行,看着像没加载出来。\n lines.push(own.length === 0 ? t('rows.ownNone') : own.join(' · '))\n if (lines.length === 0) return null\n\n return h(\n 'div',\n {\n className: 'dshh-rowhover',\n 'data-dsh-hud': 'row-hover',\n },\n lines.map(function (line, index) {\n return h('div', { key: String(index), className: 'dshh-rowhoverLine' }, line)\n }),\n )\n}\n","/**\n * 轮尾那一行「本轮图片改动」:只列本轮动过的图,点缩略图走内置预览,⇄ 进 hud 看前后对比。\n */\n\nimport React from 'react'\n\nimport { looksLikeImage } from './image-diff.js'\nimport type { ImagePairEntry } from './image-diff.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudChange,\n HudStore,\n ImagePreview,\n ImageRequest,\n SessionChange,\n SessionChangeCandidate,\n SessionChangesState,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\ninterface TurnSessionSummaryLike {\n cwd?: string | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\ninterface TurnSessionsSnapshotLike {\n byId?: Record<string, TurnSessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\ntype TurnUseSessions = <T>(selector: (snapshot: TurnSessionsSnapshotLike) => T) => T\n\n/** 「本轮改过的一张图」:文件、档位(确定/命令旁听)、那条命令与最近触达时刻。 */\nexport interface TurnImagePick {\n file: string\n name: string\n tier: 'confirmed' | 'possible'\n reason: string\n at: number\n}\n\n/** `imageChangesOf()` 的结果:挑出来的条目 + `exact`(标题该说\"本轮\"还是\"本会话\")。 */\nexport interface TurnImageChanges {\n files: TurnImagePick[]\n exact: boolean\n}\n\n/** `TurnImages` 的 props:store + 框架注入的会话/回合信息 + 内置预览的打开函数。 */\nexport interface TurnImagesProps {\n t: Translate\n store: HudStore\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n useSessions?: TurnUseSessions | null\n /** 内置右侧文档预览的\"打开这个文件\"(缩略图点击用)。 */\n openFile?: ((path: string) => void) | null\n /** 槽位给的 TurnLocation(宿主内部形状),只用来取回合号。 */\n turn?: unknown\n}\n\n/**\n * 轮尾那条「本轮图片改动」:每项一张缩略图 + 文件名。\n *\n * 内置的改动卡片点一行是打开 review tab(图片在那儿看不见),这条是补上的门:\n * **点缩略图 → 槽位给的 `openFile(path)`**,即内置的右侧文档预览(图片原生渲染、可缩放);\n * 旁边那个 `⇄` 才打开 dsh-hud 面板看前后两张 —— 前后对比只有我们有。\n */\nexport function TurnImages(props: TurnImagesProps): React.ReactElement | null {\n var t = props.t\n var store = props.store\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var openFile = props.openFile\n var turnNumber = turnNumberOf(props.turn)\n\n // useSessions 是选择器 hook:选择器参数是必需的。\n var sessions =\n typeof useSessions === 'function'\n ? useSessions(function (snapshot) {\n return snapshot\n })\n : null\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n var state = useStore(store)\n // 缩略图要等字节;缓存本身不在快照里,所以取完自增一下触发重画。\n var tick = React.useState(0)\n var bump = tick[1]\n\n // 轮尾每个已完成的回合都会挂一次:绑定会话 + 没取过会话改动就取一次(宿主侧是内存读,很便宜)。\n React.useEffect(\n function () {\n if (sessionId === undefined || cwd === undefined) return undefined\n store.bindSession(sessionId, cwd)\n if (state.sessionChanges === undefined || state.sessionChanges.phase === 'idle')\n store.loadSessionChanges()\n return undefined\n },\n [sessionId, cwd],\n )\n\n var picked = imageChangesOf(state.sessionChanges, turnNumber)\n var shown = picked.files.slice(0, 4)\n var keys = shown\n .map(function (entry) {\n return entry.file\n })\n .join('\\u0000')\n\n React.useEffect(\n function () {\n if (keys === '' || typeof cwd !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`cwd` 是 `var`,TS 在回调里会重新放宽成可空)。\n const cwdOf = cwd\n var cancelled = false\n var wanted: ImageRequest[] = shown\n .map(function (entry) {\n return { cwd: cwdOf, path: entry.file, side: 'worktree' }\n })\n .filter(function (request) {\n return store.cachedImagePair(request) === null\n })\n if (wanted.length === 0) return undefined\n Promise.all(\n wanted.map(function (request) {\n return store.loadImagePair(request)\n }),\n ).then(function () {\n if (!cancelled)\n bump(function (count) {\n return count + 1\n })\n })\n return function () {\n cancelled = true\n }\n },\n [keys, cwd],\n )\n\n // 会话改动还没到、或这一轮没动过图:整条不渲染(不在每条轮尾留个空壳)。\n if (state.sessionChanges === undefined || state.sessionChanges.phase !== 'ready') return null\n if (shown.length === 0) return null\n\n var rest = picked.files.length - shown.length\n\n function chip(entry: TurnImagePick): React.ReactElement {\n var thumb = thumbSideOf(\n typeof cwd === 'string'\n ? store.cachedImagePair({ cwd: cwd, path: entry.file, side: 'worktree' })\n : null,\n )\n var dot = entry.name.lastIndexOf('.')\n var extension = dot > 0 ? entry.name.slice(dot + 1).toUpperCase() : '?'\n // `◐` 是命令旁听来的(猜的):说清这一点,并把那条命令放进悬停里。\n var hint = entry.file + ' · ' + t('turnImages.open')\n if (entry.tier === 'possible' && entry.reason !== '')\n hint += ' · ' + t('session.possibleTitle', { reason: entry.reason })\n return h(\n 'span',\n {\n key: entry.file,\n className: 'dshh-imgstripItem',\n 'data-dsh-hud': 'turn-image',\n 'data-file': entry.file,\n 'data-tier': entry.tier,\n },\n [\n h(\n 'button',\n {\n key: 'open',\n type: 'button',\n className: 'dshh-imgstripOpen',\n title: hint,\n onClick: function () {\n if (typeof openFile === 'function') openFile(entry.file)\n },\n },\n [\n thumb === null\n ? h(\n 'span',\n { key: 'ph', className: 'dshh-imgstripThumb dshh-imgstripThumbEmpty' },\n extension,\n )\n : h('img', {\n key: 'img',\n className: 'dshh-imgstripThumb',\n alt: entry.name,\n src: 'data:' + thumb.mime + ';base64,' + String(thumb.base64 || ''),\n }),\n entry.tier === 'possible'\n ? h('span', { key: 'tier', className: 'dshh-imgstripTier', title: hint }, '◐')\n : null,\n h('span', { key: 'name', className: 'dshh-imgstripName' }, entry.name),\n ],\n ),\n h(\n 'button',\n {\n key: 'compare',\n type: 'button',\n className: 'dshh-imgstripCompare',\n title: t('turnImages.compare'),\n 'data-dsh-hud': 'turn-image-compare',\n onClick: function () {\n // store 边界:这里只知道文件路径,而 `openDiff` 只读 file/path/origFile,按调用契约补形状。\n var change = { file: entry.file, path: entry.file, origFile: null } as HudChange\n store.openPanel(true)\n store.openDiff(change)\n },\n },\n '⇄',\n ),\n ],\n )\n }\n\n return h('div', { className: 'dshh-imgstrip', 'data-dsh-hud': 'turn-images' }, [\n h(\n 'span',\n { key: 'title', className: 'dshh-imgstripTitle' },\n picked.exact ? t('turnImages.title') : t('turnImages.titleSession'),\n ),\n h('span', { key: 'items', className: 'dshh-imgstripItems' }, shown.map(chip)),\n rest > 0\n ? h(\n 'span',\n { key: 'more', className: 'dshh-imgstripMore' },\n t('turnImages.more', { n: String(rest) }),\n )\n : null,\n ])\n}\n\n/**\n * 从会话改动里挑出\"这一轮改过的图片\",最近的在前。\n *\n * **两档都收**:`●` 确定改过(写文件 / 编辑工具)与 `◐` 命令旁听(`>`、`tee`、`cp`、`rm`…)。\n * 只收确定档不够用 —— 图几乎都是脚本/命令产出的(`python gen.py`、`cp`、`curl -o`),\n * 那些永远进不了确定档,而\"点一下看图\"对它们一样有用。`◐` 是猜的,所以 chip 上带 `◐`、\n * 悬停给出那条命令(措辞与面板里改动清单一致)。\n *\n * 回合号拿不到(`turnNumberOf` 回 null)就不按回合收窄,退回\"本会话最近的图片改动\"。\n *\n * @returns `{ files, exact }`;`exact` 为 false 表示标题该说\"本会话\"而不是\"本轮\"。\n */\nexport function imageChangesOf(\n changes: SessionChangesState,\n turnNumber: number | null,\n): TurnImageChanges {\n if (changes === null || changes === undefined) return { files: [], exact: turnNumber !== null }\n var confirmed: Record<string, SessionChange> = changes.files ? changes.files : {}\n var guessed: Record<string, SessionChangeCandidate> = changes.possible ? changes.possible : {}\n var picked: TurnImagePick[] = []\n var seen: Record<string, true> = {}\n\n for (var file in confirmed) {\n if (!Object.prototype.hasOwnProperty.call(confirmed, file)) continue\n if (!looksLikeImage(file)) continue\n var record: Partial<SessionChange> = confirmed[file] || {}\n if (turnNumber !== null) {\n var turns = Array.isArray(record.turns) ? record.turns : []\n if (turns.indexOf(turnNumber) < 0 && record.lastTurn !== turnNumber) continue\n }\n seen[file] = true\n picked.push({\n file: file,\n name: baseNameOf(file),\n tier: 'confirmed',\n reason: '',\n at: typeof record.lastAt === 'number' ? record.lastAt : 0,\n })\n }\n\n for (var guess in guessed) {\n if (!Object.prototype.hasOwnProperty.call(guessed, guess)) continue\n if (seen[guess] === true) continue\n if (!looksLikeImage(guess)) continue\n var item: Partial<SessionChangeCandidate> = guessed[guess] || {}\n // 命令旁听只记\"最近一次\"的回合号(没有 turns 列表),所以只能按 lastTurn 收窄。\n if (turnNumber !== null && item.lastTurn !== turnNumber) continue\n seen[guess] = true\n picked.push({\n file: guess,\n name: baseNameOf(guess),\n tier: 'possible',\n reason: typeof item.reason === 'string' ? item.reason : '',\n at: typeof item.lastAt === 'number' ? item.lastAt : 0,\n })\n }\n\n picked.sort(function (left, right) {\n return right.at - left.at\n })\n return { files: picked, exact: turnNumber !== null }\n}\n\n/** 缩略图用哪一侧的字节:优先工作区,其次基线(新增/删除时只有一侧有)。 */\nexport function thumbSideOf(entry: unknown): ImagePreview | null {\n // store 边界:图片缓存条目的形状由 store 的 image 片构造,这里只收窄成声明过的联合。\n var pair =\n entry !== null && entry !== undefined && typeof entry === 'object'\n ? (entry as ImagePairEntry)\n : null\n if (pair === null || pair.phase !== 'ready' || !pair.value) return null\n var value = pair.value\n if (value.after && value.after.kind === 'image') return value.after\n if (value.before && value.before.kind === 'image') return value.before\n return null\n}\n\n/**\n * 槽位给的是宿主的 `TurnLocation`。\n *\n * 形状属于宿主内部约定(槽位文档只说\"receives the Turn\"),所以这里宽松取值:\n * 取不到就回 null,由调用方退到\"本会话\"的说法,而不是整条不显示。\n */\nexport function turnNumberOf(turn: unknown): number | null {\n if (typeof turn === 'number' && isFinite(turn)) return turn\n if (turn !== null && turn !== undefined && typeof turn === 'object') {\n // 槽位边界:TurnLocation 的字段形状属于框架内部约定,这里只取用到的 `turn`。\n var record = turn as { turn?: unknown }\n if (typeof record.turn === 'number') return record.turn\n }\n return null\n}\n\n/** 只取文件名(chip 上放不下整条路径)。 */\nexport function baseNameOf(file: string): string {\n var parts = String(file).split('/')\n return parts[parts.length - 1] || String(file)\n}\n","/**\n * 输入框(composer)读写与 ↑ 召回。\n *\n * 这里全部是**纯 DOM 操作**(Lexical 的 contenteditable),最讲究的是\"写进去之后到底成没成\":\n * 粘贴是异步落地的,所以每次写完都要等一拍再核对(`settleComposer`),否则会写第二遍。\n */\n\nimport { probe } from './shared.js'\nimport type { HudStore } from './shared.js'\n\n/* ------------------------------------------------------- 输入框的 DOM 面 */\n\n/**\n * 插件只用到输入框根节点的这几样:找根(`getAttribute` / `isContentEditable` /\n * `parentElement`)、读文本(`innerText` / `textContent`)、写回(`textContent` /\n * `dispatchEvent`)。用**结构类型**而不是 `HTMLElement`:测试里的假 DOM 也满足它,\n * 真实节点天然满足 —— 这里要的本来也不是\"一个 HTMLElement\",而是\"能当输入框用的节点\"。\n */\ninterface ComposerRoot {\n getAttribute?: (name: string) => string | null\n isContentEditable?: boolean\n parentElement?: ComposerRoot | null\n innerText?: string\n textContent?: string | null\n dispatchEvent?: (event: Event) => boolean\n}\n\n/**\n * 结构类型 → 真 `Node`。运行时它本来就是 contenteditable 元素;`ComposerRoot` 只是为了让\n * 测试里的假 DOM 也能满足,所以这里不做运行时检查,只把类型交回给 DOM API。\n */\nfunction nodeOf(root: ComposerRoot): Node {\n return root as unknown as Node\n}\n\n/** 历史翻页方向。 */\ntype HistoryDirection = 'up' | 'down'\n/** 翻完之后光标摆在哪一端。 */\ntype HistoryCaret = 'start' | 'end'\n\n/** 翻页状态机自己的状态:不在历史里时 `index === -1`。 */\ninterface PromptHistoryNavState {\n index: number\n draft: string | null\n}\n\n/** 翻页输入:历史条目(新的在前)与当前输入框的文本/光标。 */\ninterface PromptHistoryInput {\n entries: string[]\n text?: string\n caret?: number\n}\n\n/** 状态机结果:没接管时调用方什么都不做;接管时必然给出要写入的文本与光标位置。 */\ntype PromptHistoryStep =\n | { handled: false; state: PromptHistoryNavState }\n | { handled: true; state: PromptHistoryNavState; text: string; caret: HistoryCaret }\n\n/* ------------------------------------------------------------- 文案 */\n\n/* --------------------------------------------------------- 本地 UI 状态 */\n\n/**\n * 光标此刻允许上下翻历史吗?—— 照抄 OpenCode Desktop 的 `canNavigateHistoryAtCursor`。\n *\n * 它的规矩比 Codex 保守得多:**不在历史里时,`↑` 只在输入框完全为空时才接管**。\n * 于是多行草稿里 `↑` 永远是光标移动,跟内置的 `/` 命令、`@` 提及弹窗也天然不冲突\n * (弹窗必然带着文字)。进了历史之后,只要光标在两端就能继续翻。\n */\nexport function canNavigatePromptHistory(\n direction: HistoryDirection,\n text: string,\n caret: number,\n inHistory: boolean,\n): boolean {\n var length = typeof text === 'string' ? text.length : 0\n var position = Math.max(0, Math.min(typeof caret === 'number' ? caret : 0, length))\n if (inHistory) return position === 0 || position === length\n if (direction === 'up') return position === 0 && length === 0\n return position === length\n}\n\n/**\n * `↑` / `↓` 翻 prompt 历史的**纯状态机**(语义照抄 OpenCode Desktop 的 `navigatePromptHistory`)。\n *\n * `index === -1` 表示\"不在历史里\";`entries[0]` 是最新一条。往旧翻时先把当前草稿存进\n * `draft`,一直翻到最新再往下就把草稿**原样放回**(连光标位置都按方向摆好:往旧放开头、\n * 往新放末尾)。没接管的情况一律回 `handled: false`,调用方据此**不**调用 preventDefault。\n */\nexport function promptHistoryStep(\n state: PromptHistoryNavState,\n direction: HistoryDirection,\n input: PromptHistoryInput | null | undefined,\n): PromptHistoryStep {\n var entries = (input && input.entries) || []\n var text = input && typeof input.text === 'string' ? input.text : ''\n var caret = input && typeof input.caret === 'number' ? input.caret : 0\n var index = state.index\n var draft = state.draft\n\n if (!canNavigatePromptHistory(direction, text, caret, index >= 0))\n return { handled: false, state: state }\n\n if (direction === 'up') {\n if (entries.length === 0) return { handled: false, state: state }\n if (index === -1)\n return { handled: true, state: { index: 0, draft: text }, text: entries[0], caret: 'start' }\n if (index < entries.length - 1) {\n return {\n handled: true,\n state: { index: index + 1, draft: draft },\n text: entries[index + 1],\n caret: 'start',\n }\n }\n return { handled: false, state: state } // 已经到最旧一条:把 ↑ 还给编辑器\n }\n\n if (index > 0)\n return {\n handled: true,\n state: { index: index - 1, draft: draft },\n text: entries[index - 1],\n caret: 'end',\n }\n if (index === 0) {\n return {\n handled: true,\n state: { index: -1, draft: null },\n text: draft === null || draft === undefined ? '' : draft,\n caret: 'end',\n }\n }\n return { handled: false, state: state }\n}\n\n/* -------------------------------------------- 输入框的 ↑ 召回(纯文本) */\n\n/**\n * 从一个事件目标往上找\"输入框根节点\"。\n *\n * DSH 的输入框是 **Lexical** 的 contenteditable(根节点带 `data-lexical-editor=\"true\"`),\n * 不是 textarea —— 所以不能读 `value`。手写 parentElement 上溯(不用 `closest`):\n * 一来少依赖一个 API,二来深度有上限,绝不会误判到页面别处。\n */\nexport function composerRootOf(target: unknown): ComposerRoot | null {\n // 事件目标是 DOM 边界:调用方可能是 EventTarget,我们按结构类型读它的属性。\n var node = target as ComposerRoot | null | undefined\n for (var depth = 0; depth < 6 && node !== null && node !== undefined; depth += 1) {\n if (typeof node.getAttribute === 'function') {\n var marker = node.getAttribute('data-lexical-editor')\n var editable =\n typeof node.isContentEditable === 'boolean'\n ? node.isContentEditable\n : node.getAttribute('contenteditable') === 'true'\n if (marker === 'true' || editable) return node\n }\n node = node.parentElement\n }\n return null\n}\n\n/** 输入框里的纯文本(Lexical 会把内容画在 DOM 里,`innerText` 最贴近用户看到的)。 */\nexport function composerText(root: ComposerRoot | null | undefined): string {\n if (root === null || root === undefined) return ''\n var text =\n typeof root.innerText === 'string'\n ? root.innerText\n : typeof root.textContent === 'string'\n ? root.textContent\n : ''\n return text.replace(/\\u200b/g, '')\n}\n\n/**\n * \"输入框实际上是空的吗\"用的归一化文本。\n *\n * Lexical 的空根节点在 DOM 里通常是 `<p><br></p>`,`innerText` 会给出一个换行符 ——\n * 拿它当\"有内容\"就会让 `↑` 永远排不上队(整个功能静默失效)。所以去掉零宽字符与\n * **只在末尾的空白**:开头的空格是用户真的敲进去的,不能吞。\n */\nexport function composerTextKey(root: ComposerRoot | null | undefined): string {\n return composerText(root).replace(/\\s+$/, '')\n}\n\n/** 写入是否成功:两边都归一化再比 —— Lexical 可能补一个尾换行,那不是\"没写进去\"。 */\nexport function composerTextMatches(text: string, expected: string): boolean {\n return text.replace(/\\s+$/, '') === expected.replace(/\\s+$/, '')\n}\n\n/** 光标在输入框里的字符偏移;没有选区、有选中内容、或拿不到选区时回 null(一律不接管)。 */\nexport function composerCaretOffset(root: ComposerRoot): number | null {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed !== true) return null\n var node = selection.focusNode\n if (!node) return null\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n var before = range.cloneRange()\n try {\n before.setEnd(node, selection.focusOffset)\n } catch (error) {\n return null\n }\n return before.toString().length\n}\n\n/** 把光标放到开头或末尾(`↑` 往旧翻放开头,`↓` 往回翻放末尾 —— 与 OpenCode 一致)。 */\nexport function placeCaretInComposer(root: ComposerRoot, position: HistoryCaret): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n range.collapse(position !== 'end')\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 光标没摆好不影响内容已经写进去了\n }\n}\n\n/** 全选输入框内容(模拟粘贴前先选中,粘贴才能\"替换\"而不是\"追加\")。 */\nexport function selectComposerContents(root: ComposerRoot): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 选不中也还能走 execCommand 那条退路\n }\n}\n\n/** 等\"写入落地\":微任务总有,有 rAF 的浏览器里再多等一帧(Lexical 的粘贴是帧后落 DOM)。 */\nexport function settleComposer(): Promise<void> {\n var chain = Promise.resolve()\n if (typeof window.requestAnimationFrame === 'function') {\n chain = chain.then(function () {\n return new Promise(function (resolve) {\n window.requestAnimationFrame(function () {\n resolve()\n })\n })\n })\n }\n return chain\n}\n\n/**\n * 把一段纯文本写进 Lexical 输入框 —— **三层依次降级,每层都等落地了再回读**。\n *\n * 为什么要等:Lexical 的粘贴处理是**异步**的(它得先把剪贴板读出来)。写完立刻回读会读到\n * 旧内容、误判成失败,于是下一层再插一次 —— 实测表现就是**文字出现两遍**(第一次真机上\n * 就是这样:粘贴插了一遍,`insertText` 又追加了一遍)。\n *\n * 三层次序:① 模拟粘贴(Lexical 的正规插入通道)② `execCommand(\"insertText\")`\n * ③ 直接改文本 + 派发 `input`。**每次降级前都重新全选**,让重试是\"替换\"而不是\"追加\"。\n */\nexport function writeComposerText(\n root: ComposerRoot,\n value: string,\n caret: HistoryCaret,\n): Promise<boolean> {\n function satisfied() {\n return composerTextMatches(composerText(root), value)\n }\n\n return settleComposer()\n .then(function () {\n if (typeof window.ClipboardEvent !== 'function' || typeof window.DataTransfer !== 'function')\n return false\n if (typeof root.dispatchEvent !== 'function') return false\n try {\n selectComposerContents(root)\n var data = new window.DataTransfer()\n data.setData('text/plain', value)\n root.dispatchEvent(\n new window.ClipboardEvent('paste', {\n clipboardData: data,\n bubbles: true,\n cancelable: true,\n }),\n )\n } catch (error) {\n return false\n }\n return settleComposer().then(satisfied)\n })\n .then(function (wrote) {\n if (wrote) return true\n try {\n if (typeof document.execCommand === 'function') {\n selectComposerContents(root)\n if (document.execCommand('insertText', false, value) === true) {\n return settleComposer().then(satisfied)\n }\n }\n } catch (error) {\n // 落到最后一层\n }\n return settleComposer().then(function () {\n if (satisfied()) return true\n try {\n selectComposerContents(root)\n root.textContent = value\n // 上一层的不变量(dispatchEvent 存在)不跨回调作用域,这里用 ! 保留原来的\"抛了就被 catch 掉\"语义。\n if (typeof window.Event === 'function')\n root.dispatchEvent!(new window.Event('input', { bubbles: true }))\n return true\n } catch (error) {\n return false\n }\n })\n })\n .then(function (wrote) {\n if (!wrote) return false\n placeCaretInComposer(root, caret)\n // 重写内容会让编辑器失焦/滚动跳,下一帧再摆一次光标(OpenCode 也是这么做的)。\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(function () {\n placeCaretInComposer(root, caret)\n })\n }\n return true\n })\n}\n\n/**\n * 把\"输入框里按 ↑/↓ 召回上一条 prompt\"接到窗口上 —— 只接线,判断与翻页逻辑都在纯函数里。\n *\n * 门禁(任一不满足就**完全不介入**,`↑`/`↓` 行为与不装插件时一模一样):\n * 默认行为没被别处吃掉、不是输入法组合中、无修饰键、目标是输入框、历史非空、\n * 选区收起、光标位置通过 `canNavigatePromptHistory`。**接管了才 preventDefault**。\n */\nexport function bindPromptHistory(store: HudStore): () => void {\n var nav: { sessionId: string | null; index: number; draft: string | null; writing: boolean } = {\n sessionId: null,\n index: -1,\n draft: null,\n writing: false,\n }\n\n function onKeyDown(event: KeyboardEvent): void {\n if (event === null || event === undefined) return\n if (event.defaultPrevented === true || event.isComposing === true) return\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n if (event.altKey === true || event.ctrlKey === true || event.metaKey === true) return\n if (typeof event.preventDefault !== 'function') return\n\n var root = composerRootOf(event.target)\n if (root === null) return\n\n var snapshot = store.getSnapshot().promptHistory\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId !== null\n ? snapshot.value\n : null\n var entries =\n value !== null &&\n value !== undefined &&\n value.available === true &&\n Array.isArray(value.prompts)\n ? value.prompts\n : []\n if (entries.length === 0) return\n\n if (nav.sessionId !== snapshot.sessionId) {\n nav.sessionId = snapshot.sessionId\n nav.index = -1\n nav.draft = null\n }\n\n var caret = composerCaretOffset(root)\n if (caret === null) return\n var direction: HistoryDirection = event.key === 'ArrowUp' ? 'up' : 'down'\n // 归一化后再判\"空不空\"与\"光标在哪端\":Lexical 空输入框里的那个换行不算内容。\n var text = composerTextKey(root)\n var step = promptHistoryStep({ index: nav.index, draft: nav.draft }, direction, {\n entries: entries,\n text: text,\n caret: Math.max(0, Math.min(caret, text.length)),\n })\n if (!step.handled) return\n\n // 到这里才确定要接管:先挡住默认行为(别让 ↑ 先把光标移走),再写内容。\n event.preventDefault()\n if (typeof event.stopPropagation === 'function') event.stopPropagation()\n\n // 写入是异步的(粘贴要等落地),但翻页状态先记下:连按 ↑ 时不会两次都从同一条开始。\n // 真写不进去再回滚 —— 宁可状态与画面都对不上时退回一步,也不要悄悄漂移。\n var previous = { index: nav.index, draft: nav.draft }\n nav.index = step.state.index\n nav.draft = step.state.draft\n nav.writing = true\n\n writeComposerText(root, step.text, step.caret).then(function (wrote) {\n nav.writing = false\n if (wrote === true) {\n probe.log('prompt-history', {\n index: nav.index,\n entries: entries.length,\n direction: direction,\n })\n return\n }\n nav.index = previous.index\n nav.draft = previous.draft\n probe.log('prompt-history: 写不进输入框(三层都失败),已回滚并放弃接管', {\n entries: entries.length,\n })\n })\n }\n\n /** 用户自己动了内容(不是我们写进去的)→ 退出历史、草稿作废,免得把草稿还原到新内容上。 */\n function onInput(event: Event): void {\n if (nav.writing) return\n if (composerRootOf(event && event.target) === null) return\n nav.index = -1\n nav.draft = null\n }\n\n if (typeof window.addEventListener !== 'function') return function () {}\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('input', onInput, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('input', onInput, true)\n }\n}\n","/**\n * 两份词典(`dshHud` 命名空间)。键一致、值分别是中文与英文;`t()` 取不到会回落成键本身,\n * 所以\"漏翻\"会直接显示给人看 —— 有一条用例专门盯这件事(它扫的是源码,不是产物)。\n */\n\nexport const zh = {\n 'panel.title': 'Git',\n 'panel.refresh': '刷新',\n 'panel.reset': '复位位置',\n 'panel.close': '收起面板',\n 'panel.dragHint': '拖动标题可移动面板,双击标题复位',\n 'panel.loading': '读取中…',\n 'panel.clean': '工作区干净,没有未提交的改动',\n 'panel.notRepo': '当前会话的工作区不是 Git 仓库',\n 'panel.noSession': '还没有绑定到会话的工作区',\n 'panel.summary': '改动概览',\n 'panel.changes': '改动清单',\n 'panel.truncated': '仅显示前 200 条',\n 'panel.readonly': '只读',\n 'panel.upstream': '上游',\n 'panel.updated': '更新于 ',\n 'panel.ahead': '领先 ',\n 'panel.behind': '落后 ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': '点击一行查看该文件的差异',\n 'panel.rowKeys': '在面板里按过之后再按 ↑/↓ 选行、Enter 打开',\n 'panel.commits': '最近提交',\n 'panel.tags': '标签',\n 'panel.tagsEmpty': '这个仓库还没有标签',\n 'panel.tagsCapped': '只显示最近 {n} 个',\n 'tag.click': '点一下看它指向的提交',\n 'tag.annotated': '附注标签',\n 'tag.lightweight': '轻量标签',\n 'diff.close': '关闭差异',\n 'diff.resize': '拖动右边缘调整差异宽度',\n 'diff.resizeCommit': '拖动调整提交明细栏宽度',\n 'diff.worktree': '未暂存',\n 'diff.index': '已暂存',\n 'diff.wrap': '自动换行',\n 'diff.copy': '复制差异',\n 'diff.copied': '已复制',\n 'diff.empty': '这一侧没有差异',\n 'diff.binary': '二进制文件,没有可显示的文本差异',\n // ── 图片预览(二进制改动里认得出的那几种) ──\n 'image.loading': '读取图片…',\n 'image.pair': '并排',\n 'image.flip': '切换',\n 'image.beforeWorktree': '索引',\n 'image.afterWorktree': '工作区',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': '索引',\n 'image.beforeCommit': '父提交',\n 'image.afterCommit': '这次提交',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': '尺寸 {from} → {to}',\n 'image.tooLarge': '超过 {limit} MB 预览上限({size})',\n 'image.notImage': '不是可预览的图片({size})',\n 'image.missing': '这一侧没有这个文件',\n 'image.zoomOut': '缩小',\n 'image.zoomIn': '放大',\n 'image.fitWidth': '适应宽度',\n 'image.zoomValue': '{percent}%',\n // ── 会话计时胶囊 ──\n 'timer.title': '本次会话运行时长',\n 'timer.created': '创建于 {time}',\n 'timer.work': '其中模型 {llm}、工具 {tool}',\n 'timer.turns': '{turns} 轮 / {steps} 步',\n // ── Session timer capsule ──\n // ── 轮尾的「本轮图片改动」条 ──\n 'turnImages.title': '本轮图片改动',\n 'turnImages.titleSession': '本会话图片改动',\n 'turnImages.open': '在内置预览里打开',\n 'turnImages.compare': '在 dsh-hud 里前后对比',\n 'turnImages.more': '还有 {n} 个',\n 'diff.truncated': '差异过大,仅显示前面部分',\n 'diff.untrackedHint': '未跟踪文件,与空文件对比',\n 'diff.commitHint': '这次提交相对其父提交的差异(只读)',\n 'diff.hunkToggle': '点这一行折叠 / 展开这个 hunk(滚动到它后按 Space 一样)',\n 'commit.files': '这次提交改动',\n 'commit.close': '收起提交明细',\n 'commit.empty': '这次提交没有改动文件(空提交)',\n 'commit.loading': '读取这次提交…',\n 'commit.merge': '合并提交,按第一父提交比较',\n 'commit.unknown': '找不到这次提交:可能已被 rebase / amend 换掉,刷新面板再看',\n 'commit.click': '点击查看这次提交改了什么',\n // ── 会话归因(本会话改过哪些文件) ──\n 'session.line': '本会话改过 {n} 个文件',\n 'tools.now': '现在在跑:{name}',\n 'tools.total': '本会话共 {n} 次工具调用',\n 'tools.byTool': '各工具:',\n 'tools.skills': 'skills:',\n 'tools.recent': '最近:',\n 'rows.ownNone': '本会话没有改动记录',\n 'rows.noRepo': '不在 git 仓库里',\n 'rows.error': '读取失败({code})',\n 'rows.possible': '命令旁听到 {n} 个可能改动的路径({now} 个现在有差异)',\n 'review.count': '已审 {n}/{total}',\n 'review.staleCount': '{n} 个改动已变,需要重看',\n 'review.mark': '标成「这一版我审过了」(键盘:v)',\n 'review.unmark': '取消「已审」(键盘:v)',\n 'review.stale': '改动已变,需要重看(标记的是更早的那一版)',\n 'review.clear': '清空这个工作区的「已审」标记',\n 'review.clearShort': '清空已审',\n 'session.latestTurn': '最近一轮 {n} 个',\n 'session.turn': '第 {n} 轮',\n 'session.turnWithPrompt': '第 {n} 轮:{prompt}',\n 'session.onlyLatest': '只看最近一轮',\n 'session.onlyLatestTitle': '把清单收窄到最近这一轮动过的文件(「这一轮改了什么」)',\n 'session.possible': '另有 {n} 个可能相关(命令旁听)',\n 'session.possibleDropped': '还有 {n} 个超出上限未记',\n 'session.possibleTitle': '可能相关(未确认):{reason}',\n 'session.commands': '最近跑过 {n} 条命令:',\n 'session.commandsMore': '…另有 {n} 条会写文件的命令',\n 'session.commandFailed': '(这条失败了)',\n 'session.gone': '{n} 个已不在 git 现状里(改回去了 / 被忽略 / 被覆盖)',\n 'session.dropped': '另有 {n} 个超出上限未记录',\n 'session.only': '只看本会话改的',\n 'session.onlyTitle':\n '把清单过滤成本会话动过的文件(确定的 + 命令旁听到的「可能相关」,与 git 现状求交)',\n 'session.mine': '本会话改的',\n 'session.other': '不是本会话改的(可能是你自己、编辑器或另一个会话)',\n 'session.writes': '写入 {n} 次',\n 'session.edits': '编辑 {n} 次',\n 'session.since': '自 {time} 起记录(宿主进程启动之前的不算)',\n 'session.pill': '本会话改过 {n} 个文件',\n 'session.empty': '本会话还没有改动(清掉上面的过滤器看看工作区现状)',\n 'pill.loading': '读取中…',\n 'pill.notRepo': '非 git 仓库',\n 'pill.error': '读取失败',\n 'pill.title': '点击展开 Git 面板',\n 'count.modified': '已修改',\n 'count.added': '已暂存新增',\n 'count.deleted': '已删除',\n 'count.renamed': '重命名',\n 'count.copied': '复制',\n 'count.typeChanged': '类型改变',\n 'count.untracked': '未跟踪',\n 'count.conflicted': '冲突',\n 'err.timeout': 'git 超时:仓库过大或磁盘繁忙',\n 'err.no-cwd': '工作区目录不存在',\n 'err.git-missing': '找不到 git 可执行文件',\n 'err.invalid-cwd': '无效的工作区路径',\n 'err.git-failed': 'git 执行失败',\n 'err.invalid-path': '无效的文件路径',\n 'err.stale-host': '宿主半还没有这个端点:插件刚更新,需要重启 DSH 让宿主半重新加载',\n 'err.transport': '无法连接到宿主半',\n}\n\nexport const en = {\n 'panel.title': 'Git',\n 'panel.refresh': 'Refresh',\n 'panel.reset': 'Reset position',\n 'panel.close': 'Collapse panel',\n 'panel.dragHint': 'Drag the title to move the panel; double-click it to reset',\n 'panel.loading': 'Loading…',\n 'panel.clean': 'Working tree clean — nothing uncommitted',\n 'panel.notRepo': 'The current session workspace is not a Git repository',\n 'panel.noSession': 'Not bound to a session workspace yet',\n 'panel.summary': 'Summary',\n 'panel.changes': 'Changes',\n 'panel.truncated': 'Showing the first 200 entries only',\n 'panel.readonly': 'Read-only',\n 'panel.upstream': 'upstream',\n 'panel.updated': 'Updated ',\n 'panel.ahead': 'ahead ',\n 'panel.behind': 'behind ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': 'Click a file to see its diff',\n 'panel.rowKeys': 'Once you have clicked in the panel: ↑/↓ to select a row, Enter to open it',\n 'panel.commits': 'Recent commits',\n 'panel.tags': 'Tags',\n 'panel.tagsEmpty': 'No tags in this repository yet',\n 'panel.tagsCapped': 'Showing the newest {n}',\n 'tag.click': 'Click to open the commit it points at',\n 'tag.annotated': 'Annotated tag',\n 'tag.lightweight': 'Lightweight tag',\n 'diff.close': 'Close diff',\n 'diff.resize': 'Drag the right edge to resize the diff',\n 'diff.resizeCommit': 'Drag to resize the commit pane',\n 'diff.worktree': 'Unstaged',\n 'diff.index': 'Staged',\n 'diff.wrap': 'Wrap lines',\n 'diff.copy': 'Copy diff',\n 'diff.copied': 'Copied',\n 'diff.empty': 'No changes on this side',\n 'diff.binary': 'Binary file — no text diff to show',\n // ── Image preview (binary changes we can recognise) ──\n 'image.loading': 'Reading image…',\n 'image.pair': 'Side by side',\n 'image.flip': 'Flip',\n 'image.beforeWorktree': 'index',\n 'image.afterWorktree': 'worktree',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': 'index',\n 'image.beforeCommit': 'parent',\n 'image.afterCommit': 'this commit',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': 'size {from} → {to}',\n 'image.tooLarge': 'over the {limit} MB preview limit ({size})',\n 'image.notImage': 'not a previewable image ({size})',\n 'image.missing': 'not present on this side',\n 'image.zoomOut': 'Zoom out',\n 'image.zoomIn': 'Zoom in',\n 'image.fitWidth': 'Fit width',\n 'image.zoomValue': '{percent}%',\n // ── Session timer capsule ──\n 'timer.title': \"This session's running time\",\n 'timer.created': 'started {time}',\n 'timer.work': 'model {llm}, tools {tool}',\n 'timer.turns': '{turns} turns / {steps} steps',\n // ── Turn-tail \"images this turn\" strip ──\n 'turnImages.title': 'Images this turn',\n 'turnImages.titleSession': 'Images this session',\n 'turnImages.open': 'Open in the built-in preview',\n 'turnImages.compare': 'Compare before/after in dsh-hud',\n 'turnImages.more': '{n} more',\n 'diff.truncated': 'Diff is large; showing the beginning only',\n 'diff.untrackedHint': 'Untracked file, diffed against an empty file',\n 'diff.commitHint': 'What this commit changed against its parent (read-only)',\n 'diff.hunkToggle':\n 'Click this row to collapse or expand the hunk (same as Space once it is the current one)',\n 'commit.files': 'Files in this commit',\n 'commit.close': 'Close the commit detail',\n 'commit.empty': 'This commit changes no files (empty commit)',\n 'commit.loading': 'Reading this commit…',\n 'commit.merge': 'Merge commit, compared against its first parent',\n 'commit.unknown':\n 'Commit not found: it may have been rebased or amended away — refresh the panel',\n 'commit.click': 'Click to see what this commit changed',\n 'session.line': '{n} file(s) changed by this session',\n 'tools.now': 'Running now: {name}',\n 'tools.total': '{n} tool call(s) this session',\n 'tools.byTool': 'By tool:',\n 'tools.skills': 'skills:',\n 'tools.recent': 'Recent:',\n 'rows.ownNone': 'this session has no recorded changes',\n 'rows.noRepo': 'not in a git repository',\n 'rows.error': 'read failed ({code})',\n 'rows.possible': '{n} path(s) commands may have touched ({now} still differ)',\n 'review.count': '{n}/{total} reviewed',\n 'review.staleCount': '{n} changed since reviewed',\n 'review.mark': 'Mark this revision as reviewed (key: v)',\n 'review.unmark': 'Unmark reviewed (key: v)',\n 'review.stale': 'Changed since you reviewed it (your mark was an earlier revision)',\n 'review.clear': 'Clear the reviewed marks for this workspace',\n 'review.clearShort': 'Clear reviewed',\n 'session.latestTurn': '{n} in the latest turn',\n 'session.turn': 'turn {n}',\n 'session.turnWithPrompt': 'turn {n}: {prompt}',\n 'session.onlyLatest': 'Latest turn only',\n 'session.onlyLatestTitle': 'Narrow the list to the files the latest turn changed',\n 'session.possible': '{n} more possibly related (seen in commands)',\n 'session.possibleDropped': '{n} more over the cap',\n 'session.possibleTitle': 'Possibly related (unconfirmed): {reason}',\n 'session.commands': 'Last {n} command(s) run:',\n 'session.commandsMore': '…plus {n} more that write files',\n 'session.commandFailed': '(this one failed)',\n 'session.gone': '{n} no longer in the git status (reverted, ignored, or overwritten)',\n 'session.dropped': '{n} more not recorded (over the cap)',\n 'session.only': \"Only this session's\",\n 'session.onlyTitle':\n 'Filter the list to files this session touched (confirmed plus command-inferred, intersected with git)',\n 'session.mine': 'Changed by this session',\n 'session.other': 'Not changed by this session (you, your editor, or another session)',\n 'session.writes': '{n} write(s)',\n 'session.edits': '{n} edit(s)',\n 'session.since': 'Recorded since {time} (anything before the host process started is not)',\n 'session.pill': '{n} file(s) changed by this session',\n 'session.empty':\n 'This session has not changed anything yet (clear the filter above to see the workspace)',\n 'pill.loading': 'Loading…',\n 'pill.notRepo': 'not a git repo',\n 'pill.error': 'read failed',\n 'pill.title': 'Click to open the Git panel',\n 'count.modified': 'modified',\n 'count.added': 'added',\n 'count.deleted': 'deleted',\n 'count.renamed': 'renamed',\n 'count.copied': 'copied',\n 'count.typeChanged': 'type changed',\n 'count.untracked': 'untracked',\n 'count.conflicted': 'conflicted',\n 'err.timeout': 'git timed out: the repository is large or the disk is busy',\n 'err.no-cwd': 'The workspace directory does not exist',\n 'err.git-missing': 'git executable not found',\n 'err.invalid-cwd': 'Invalid workspace path',\n 'err.git-failed': 'git failed',\n 'err.invalid-path': 'Invalid file path',\n 'err.stale-host':\n 'The host half does not have this endpoint yet: the plugin was just updated, restart DSH so the host half reloads',\n 'err.transport': 'Cannot reach the host half',\n}\n","/**\n * 「会话归因与审阅」这一片 store:本会话改过哪些文件、哪些标记为已审阅。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { ReviewedMap, SessionChange, SessionChangeCandidate } from '../shared.js'\nimport type {\n AttributionHelpers,\n AttributionSlice,\n GetState,\n SessionChangesPayload,\n StoreContext,\n StoreSet,\n} from './types.js'\n\nimport { CHANNEL, probe, reviewKeyOf, writeReviewed } from '../shared.js'\n\nexport function createAttributionSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: AttributionHelpers,\n): AttributionSlice {\n /** 会话归因那次请求(在途合并)。 */\n var sessionInflight: Promise<unknown> | null = null\n\n /** 胶囊调用:把当前会话的工作区写进 store,变了才重新取数。 */\n function bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void {\n if (getState().sessionId === sessionId && getState().cwd === (cwd || null)) return\n set({\n sessionId: sessionId === undefined ? null : sessionId,\n cwd: cwd || null,\n status: null,\n errorCode: null,\n commits: null,\n commitsOid: null,\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n // 差异与提交明细都绑定在某个工作区上:换了工作区就把它们收起,\n // 否则会显示另一个仓库的文件或提交。键盘光标同理,它指向的是清单里的行。\n diff: null,\n commit: null,\n cursor: null,\n phase: cwd ? 'loading' : 'idle',\n })\n // 工具活动同理:换会话就清空,别显示上一个会话正在跑的工具。\n set({\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n })\n // 归因是\"这个会话\"的事实:换会话就先清空,别把上一个会话的标记留在新会话的清单上。\n set({\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n })\n // 回合信息同理:换会话就按新会话重新接(旧的先解掉)。\n helpers.bindTurns(sessionId)\n helpers.refresh()\n }\n /**\n * 取\"本会话改过哪些文件\"。\n *\n * 与 `status` 同一条命:状态刷新时顺带取一次(都是本进程内的读,很便宜)。失败就保持\n * `files: null`(界面不打标记、也不过这个滤),不会挡住 git 那半边。\n */\n function loadSessionChanges(): Promise<unknown> {\n var cwd = getState().cwd\n var sessionId = getState().sessionId\n if (!cwd || !sessionId) return Promise.resolve()\n if (sessionInflight) return sessionInflight\n if (getState().sessionChanges.phase === 'idle')\n set({ sessionChanges: Object.assign({}, getState().sessionChanges, { phase: 'loading' }) })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionChangesPayload>(CHANNEL, 'session-changes', {\n args: { cwd: cwd, sessionId: sessionId },\n })\n })\n .then(function (result) {\n sessionInflight = null\n if (getState().cwd !== cwd || getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value && result.value.cwdMismatch === true) {\n probe.log('session-changes', { cwdMismatch: true })\n }\n if (result && result.ok === true && result.value && result.value.cwdMismatch !== true) {\n var files: Record<string, SessionChange> = {}\n var list = Array.isArray(result.value.files) ? result.value.files : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.file === 'string' && item.file !== '') files[item.file] = item\n }\n var possible: Record<string, SessionChangeCandidate> = {}\n var possibleList = Array.isArray(result.value.possible) ? result.value.possible : []\n for (var pIndex = 0; pIndex < possibleList.length; pIndex += 1) {\n var possibleItem = possibleList[pIndex]\n if (possibleItem && typeof possibleItem.file === 'string' && possibleItem.file !== '') {\n possible[possibleItem.file] = possibleItem\n }\n }\n var since = typeof result.value.since === 'number' ? result.value.since : null\n var dropped = typeof result.value.dropped === 'number' ? result.value.dropped : 0\n probe.log('session-changes', {\n files: Object.keys(files).length,\n possible: Object.keys(possible).length,\n commands: Array.isArray(result.value.commands) ? result.value.commands.length : 0,\n dropped: dropped,\n since: since === null ? null : new Date(since).toISOString(),\n })\n set({\n sessionChanges: {\n phase: 'ready',\n files: files,\n possible: possible,\n commands: Array.isArray(result.value.commands) ? result.value.commands : [],\n since: since,\n dropped: dropped,\n possibleDropped:\n typeof result.value.possibleDropped === 'number' ? result.value.possibleDropped : 0,\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-changes-failed', { code: code || 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: code || 'transport',\n },\n })\n })\n .catch(function () {\n sessionInflight = null\n if (getState().cwd !== cwd) return\n probe.log('session-changes-failed', { code: 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: 'transport',\n },\n })\n })\n\n sessionInflight = task\n return task\n }\n /**\n * 标记 / 取消\"这个文件的这一版我审过了\"。\n *\n * 签名用的是**当前**这一版(宿主 stat 出来的 size+mtimeMs):下次改动一落盘签名就变,\n * 标记自动降级成\"改动已变,需要重看\" —— 而不是继续冒充\"已审\"。\n */\n function toggleReviewed(file: string): void {\n if (typeof file !== 'string' || file.length === 0) return\n var signature = getState().review ? getState().review[file] : undefined\n if (typeof signature !== 'string') {\n probe.log('review-no-signature', { file: file })\n return\n }\n var next: ReviewedMap = {}\n for (var key in getState().reviewed)\n if (Object.prototype.hasOwnProperty.call(getState().reviewed, key))\n next[key] = getState().reviewed[key]\n var markKey = helpers.reviewedKeyOf(file)\n if (next[markKey] !== undefined && next[markKey].signature === signature) delete next[markKey]\n else next[markKey] = { signature: signature, at: Date.now() }\n set({ reviewed: writeReviewed(next) })\n }\n\n /** 清掉**当前工作区**的全部\"已审\"标记(别的仓库不动)。 */\n function clearReviewed(): void {\n var prefix = reviewKeyOf(getState().cwd, '')\n var next: ReviewedMap = {}\n var removed = 0\n for (var key in getState().reviewed) {\n if (!Object.prototype.hasOwnProperty.call(getState().reviewed, key)) continue\n if (key.indexOf(prefix) === 0) {\n removed += 1\n continue\n }\n next[key] = getState().reviewed[key]\n }\n if (removed === 0) return\n set({ reviewed: writeReviewed(next) })\n }\n\n return {\n bindSession: bindSession,\n clearReviewed: clearReviewed,\n loadSessionChanges: loadSessionChanges,\n toggleReviewed: toggleReviewed,\n }\n}\n","/**\n * 「提交与标签」这一片 store:打开某次提交、看它里面某个文件的差异、标签列表。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudCommitFile } from '../shared.js'\nimport type {\n CommitDiffPayload,\n CommitPayload,\n CommitRef,\n CommitsHelpers,\n CommitsSlice,\n GetState,\n LogPayload,\n StoreContext,\n StoreSet,\n TagsPayload,\n} from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, probe, readUi, readZoom, sameCommit } from '../shared.js'\n\nexport function createCommitsSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: CommitsHelpers,\n): CommitsSlice {\n /** 提交那几次请求(在途合并)。 */\n var commitInflight: Promise<unknown> | null = null\n /** 提交里某个文件的差异:与工作区差异**分开**合并(拆分前两者共用一个变量,\n * 拆分后留在差异片里,这里再引用就会 ReferenceError)。 */\n var commitDiffInflight: Promise<unknown> | null = null\n\n /**\n * 打开「某次提交」的视图:先取这次提交的改动清单,再自动打开第一个文件的差异,\n * 这样一次点击就能看到\"这次提交改了什么\"。\n *\n * 提交是历史事实,取回来就不再变,因此不参与轮询;只有 HEAD 变了它也不受影响\n * (面板显示的是你点的那一次提交)。\n */\n function openCommit(commit: CommitRef): Promise<unknown> {\n if (!getState().cwd || !commit || !commit.sha) return Promise.resolve()\n var cwd = getState().cwd\n var sha = commit.sha\n\n set({\n commit: {\n phase: 'loading',\n sha: sha,\n shortSha:\n typeof commit.sha === 'string' && commit.sha.length > 7 ? commit.sha.slice(0, 7) : sha,\n subject: commit.subject || '',\n author: commit.author || '',\n date: commit.date || '',\n committedAt: typeof commit.committedAt === 'number' ? commit.committedAt : null,\n refs: commit.refs || [],\n merge: false,\n files: [],\n truncated: false,\n errorCode: null,\n },\n diff: null,\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitPayload>(CHANNEL, 'commit', {\n args: { cwd: cwd, sha: sha },\n })\n })\n .then(function (result) {\n commitInflight = null\n var current = getState().commit\n // 期间用户可能已经返回清单、点了别的提交,或换了工作区。\n if (!current || current.sha !== sha || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var files = Array.isArray(value.files) ? value.files : []\n var meta = value.commit\n if (meta !== null && meta !== undefined) {\n set({\n commit: {\n phase: 'ready',\n sha: meta.sha || sha,\n shortSha: meta.shortSha || sha,\n subject: meta.subject || '',\n author: meta.author || '',\n date: meta.date || '',\n committedAt: typeof meta.committedAt === 'number' ? meta.committedAt : null,\n refs: meta.refs || [],\n merge: meta.merge === true,\n files: files,\n truncated: value.truncated === true,\n errorCode: null,\n },\n })\n // 空提交(或者清单被截断到没内容)就停在左栏,不去开一个空差异。\n // 有内容就顺手打开第一个文件——一次点击就看到\"改了什么\";这里把\n // diff 的 promise 交回去,于是 await openCommit 就是等整次点击完成。\n if (files.length > 0) return openCommitDiff(files[0])\n return undefined\n }\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n commit: Object.assign({}, current, {\n phase: 'error',\n files: [],\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitInflight = null\n var current = getState().commit\n if (!current || current.sha !== sha) return\n set({ commit: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitInflight = task\n return task\n }\n /**\n * 打开这次提交里某个文件的差异。与工作区差异共用右栏,但只有一个\"侧\"\n * (提交相对其父提交),因此工具栏不显示 未暂存/已暂存 两个页签。\n */\n function openCommitDiff(file: HudCommitFile): Promise<unknown> {\n var current = getState().commit\n if (!getState().cwd || !current || !file || !file.file) return Promise.resolve()\n var cwd = getState().cwd\n var sha = current.sha\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n\n set({\n cursor: file.file,\n diff: {\n source: 'commit',\n phase: 'loading',\n sha: sha,\n shortSha: current.shortSha,\n subject: current.subject,\n path: file.path || file.file,\n file: file.file,\n origPath: file.origFile || null,\n data: null,\n side: 'commit',\n wrap: wrap,\n zoom:\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi()),\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitDiffPayload>(CHANNEL, 'commit-diff', {\n args: { cwd: cwd, sha: sha, path: file.file, origPath: file.origFile || null },\n })\n })\n .then(function (result) {\n commitDiffInflight = null\n var now = getState().diff\n // 期间可能已经返回清单、点了别的文件,或换了提交。\n if (\n !now ||\n now.source !== 'commit' ||\n now.sha !== sha ||\n now.file !== file.file ||\n getState().cwd !== cwd\n )\n return\n if (result && result.ok === true && result.value) {\n set({\n diff: Object.assign({}, now, { phase: 'ready', data: result.value, errorCode: null }),\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: Object.assign({}, now, {\n phase: 'error',\n data: null,\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitDiffInflight = null\n var now = getState().diff\n if (!now || now.source !== 'commit' || now.file !== file.file) return\n set({ diff: Object.assign({}, now, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitDiffInflight = task\n return task\n }\n /**\n * 点最近提交里的一行:打开这次提交的明细;点已经打开的那条则收起。\n * (明细占的是右栏,左栏主面板只是高亮那一行。)\n */\n function toggleCommit(commit: CommitRef): Promise<unknown> | void {\n if (!commit || !commit.sha) return Promise.resolve()\n var openCommitState = getState().commit\n if (openCommitState !== null && sameCommit(commit.sha, openCommitState.sha))\n return closeCommit()\n return openCommit(commit)\n }\n /** 收起提交明细:它带来的差异一起收起来(清单换了,光标也要清)。 */\n function closeCommit(): void {\n if (getState().commit === null) return\n var openDiff = getState().diff\n var keep = openDiff && openDiff.source === 'commit' ? null : openDiff\n set({ commit: null, diff: keep, cursor: null })\n }\n /**\n * 取最近提交。失败只让这一段不显示,不影响面板其余部分——\n * 提交是背景信息,不该抢走「工作区状态」的注意力。\n */\n function loadCommits(cwd: string, oid: string | null): Promise<unknown> {\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<LogPayload>(CHANNEL, 'log', {\n args: { cwd: cwd, count: 10 },\n })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var commits =\n result && result.ok === true && result.value && Array.isArray(result.value.commits)\n ? result.value.commits\n : []\n set({ commits: commits, commitsOid: oid })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n set({ commits: [], commitsOid: oid })\n })\n }\n /** 取标签列表(面板开着时才取)。失败只让这一段不显示。 */\n function loadTags(cwd: string): Promise<unknown> {\n // 10s 新鲜度窗口:面板每 8s 刷新一次,这里避免每次都对 git 跑一遍 for-each-ref;\n // 又能让\"刚打的新 tag\"在十来秒内出现(HEAD 没动也得看到)。\n if (getState().tags !== null && Date.now() - getState().tagsAt < 10000) return Promise.resolve()\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<TagsPayload>(CHANNEL, 'tags', { args: { cwd: cwd } })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var value = result && result.ok === true ? result.value : null\n var tags = value && Array.isArray(value.tags) ? value.tags : []\n probe.log('tags', {\n count: tags.length,\n truncated: value ? value.truncated === true : false,\n })\n set({\n tags: tags,\n tagsAt: Date.now(),\n tagsTruncated: value ? value.truncated === true : false,\n })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n probe.log('tags-failed', { code: 'transport' })\n set({ tags: [], tagsAt: Date.now(), tagsTruncated: false })\n })\n }\n\n return {\n closeCommit: closeCommit,\n loadCommits: loadCommits,\n loadTags: loadTags,\n openCommit: openCommit,\n openCommitDiff: openCommitDiff,\n toggleCommit: toggleCommit,\n }\n}\n","/**\n * 「差异视图」这一片 store:打开哪份差异、页签、换行,清单里上下走与 hunk 折叠。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudChange, WorktreeDiffPayload } from '../shared.js'\nimport type { DiffHelpers, DiffSlice, GetState, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, readUi, readZoom } from '../shared.js'\n\nexport function createDiffSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: DiffHelpers,\n): DiffSlice {\n /** 差异那次请求(在途合并)。 */\n var diffInflight: Promise<unknown> | null = null\n\n /**\n * 打开一个改动文件的差异视图:一次取回**两侧**的 rows,\n * 默认落在有内容的那一侧(工作区优先),页签切换因此不需要再发请求。\n */\n function openDiff(change: HudChange): Promise<unknown> {\n if (!getState().cwd || !change || !change.file) return Promise.resolve()\n var cwd = getState().cwd\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n var zoom =\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi())\n set({\n // 工作区差异与「某次提交的明细」是互斥的两种右栏内容,换过去就收起明细。\n commit: null,\n cursor: change.file,\n diff: {\n source: 'worktree',\n phase: 'loading',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: wrap,\n zoom: zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<WorktreeDiffPayload>(CHANNEL, 'diff', {\n args: { cwd: cwd, path: change.file, origPath: change.origFile || null },\n })\n })\n .then(function (result) {\n diffInflight = null\n var current = getState().diff\n // 期间用户可能已经返回清单或换了文件。\n if (!current || current.file !== change.file || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var worktreeRows = value.worktree && value.worktree.rows ? value.worktree.rows.length : 0\n var indexRows = value.index && value.index.rows ? value.index.rows.length : 0\n set({\n diff: {\n source: 'worktree',\n phase: 'ready',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: value,\n side: worktreeRows > 0 || indexRows === 0 ? 'worktree' : 'index',\n wrap: current.wrap,\n zoom: current.zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: {\n source: 'worktree',\n phase: 'error',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: current.wrap,\n errorCode: code || 'git-failed',\n },\n })\n })\n .catch(function () {\n diffInflight = null\n var current = getState().diff\n if (!current || current.file !== change.file) return\n set({ diff: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n // 到这里 task 一定已被赋值;先挂进在途合并变量再返回(返回类型才不是可空的)。\n diffInflight = task\n return task\n }\n function closeDiff(): void {\n if (getState().diff === null) return\n set({ diff: null })\n }\n function setDiffSide(side: string): void {\n var diff = getState().diff\n if (!diff || diff.source === 'commit' || (side !== 'worktree' && side !== 'index')) return\n // 换侧等于换了一份 rows:hunk 序号与折叠状态都不再对应得上。\n set({ diff: Object.assign({}, diff, { side: side, hunk: 0, collapsed: [] }) })\n }\n /**\n * 在当前清单里上下移动一格(↑/↓ 只移动;`[`/`]` 移动并直接打开)。\n *\n * 还没选过时:向下从第一条开始,向上从最后一条开始 —— 这样 ↑ 和 ↓ 都能\n * \"先落在一个合理的位置上\",不需要用户先点一行。\n *\n * @param delta - 1 向下、-1 向上。\n * @param open - 移动后是否立刻打开。\n * @returns 选中的那条,清单为空时返回 null。\n */\n function stepFile(delta: number, open: boolean): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n\n var index = -1\n for (var cursor = 0; cursor < list.length; cursor += 1) {\n if (list[cursor].file === getState().cursor) {\n index = cursor\n break\n }\n }\n\n var next =\n index === -1\n ? delta > 0\n ? 0\n : list.length - 1\n : Math.min(Math.max(0, index + delta), list.length - 1)\n var entry = list[next]\n if (!entry || !entry.file) return null\n\n set({ cursor: entry.file })\n if (open) helpers.openListEntry(entry)\n return entry\n }\n /** Enter:打开键盘选中的那条;还没选过就打开第一条。 */\n function openCursor(): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n for (var index = 0; index < list.length; index += 1) {\n if (list[index].file === getState().cursor) {\n helpers.openListEntry(list[index])\n return list[index]\n }\n }\n return stepFile(1, true)\n }\n /**\n * n / p:跳到下一个 / 上一个 hunk。跳到折叠着的 hunk 会顺手展开它 ——\n * 不然按键看起来什么都没发生。\n */\n function stepHunk(delta: number): void {\n var diff = getState().diff\n if (!diff) return\n var total = helpers.hunkCount()\n if (total === 0) return\n var current = typeof diff.hunk === 'number' ? diff.hunk : 0\n var next = Math.min(Math.max(0, current + delta), total - 1)\n var collapsed = (diff.collapsed || []).filter(function (index) {\n return index !== next\n })\n set({ diff: Object.assign({}, diff, { hunk: next, collapsed: collapsed }) })\n }\n /** 折叠 / 展开一个 hunk(点 hunk 头,或 Space 作用于当前 hunk)。 */\n function toggleHunk(index: number | undefined): void {\n var diff = getState().diff\n if (!diff || typeof index !== 'number' || index < 0) return\n var collapsed = diff.collapsed || []\n var next =\n collapsed.indexOf(index) >= 0\n ? collapsed.filter(function (item) {\n return item !== index\n })\n : collapsed.concat([index])\n set({ diff: Object.assign({}, diff, { hunk: index, collapsed: next }) })\n }\n function toggleDiffWrap(): void {\n var diff = getState().diff\n if (!diff) return\n set({ diff: Object.assign({}, diff, { wrap: diff.wrap !== true }) })\n helpers.persist()\n }\n\n return {\n closeDiff: closeDiff,\n openCursor: openCursor,\n openDiff: openDiff,\n setDiffSide: setDiffSide,\n stepFile: stepFile,\n stepHunk: stepHunk,\n toggleDiffWrap: toggleDiffWrap,\n toggleHunk: toggleHunk,\n }\n}\n","import { CHANNEL, imagePairKey } from '../shared.js'\nimport type { ImageRequest, ImagePayload } from '../shared.js'\nimport type { ImageSlice, ImageView, StoreContext } from './types.js'\n\n/**\n * 「图片预览」这一片 store:二进制改动两侧的字节与尺寸,带一个同步缓存。\n */\n\nexport function createImageSlice(ctx: StoreContext): ImageSlice {\n var imagePairs = new Map<string, ImageView>()\n /** 这一片自己的缓存上限(照宿主半的 `CACHE_MAX_ENTRIES` 同一个量级)。 */\n var IMAGE_CACHE_MAX = 8\n\n /**\n * 同步看一眼缓存:命中就返回条目,否则 null。\n *\n * 有了它,`ImageDiff` 的**第一次渲染**就能把已经取过的图画出来 ——\n * 关掉再打开同一张图不该先闪一下\"读取图片…\",也不该再发一次请求。\n */\n function cachedImagePair(request: ImageRequest): ImageView | null {\n if (request === null || request === undefined) return null\n var entry = imagePairs.get(imagePairKey(request))\n return entry === undefined ? null : entry\n }\n\n /** 供 `ImageDiff` 用:取一个二进制改动两侧的字节与尺寸(同一个请求只发一次)。 */\n function loadImagePair(request: ImageRequest): Promise<ImageView> {\n var key = imagePairKey(request)\n var cached = imagePairs.get(key)\n if (cached !== undefined) return Promise.resolve(cached)\n\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<ImagePayload>(CHANNEL, 'image', { args: request })\n })\n .then(function (result) {\n var entry: ImageView =\n result && result.ok === true && result.value\n ? { phase: 'ready', value: result.value }\n : {\n phase: 'error',\n code:\n result && result.error && result.error.details ? result.error.details.code : null,\n }\n if (imagePairs.size >= IMAGE_CACHE_MAX) imagePairs.clear()\n imagePairs.set(key, entry)\n return entry\n })\n .catch(function () {\n return { phase: 'error', code: 'transport' }\n })\n }\n\n return { cachedImagePair: cachedImagePair, loadImagePair: loadImagePair }\n}\n","/**\n * 「会话信息」这一片 store:会话运行时长(3 秒 / 60 秒两档 TTL)与 prompt 历史。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`(由 `createStore` 注入)。\n */\n\nimport type { HudPromptHistory, HudSessionTime } from '../shared.js'\nimport type { GetState, SessionInfoSlice, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, PROMPT_HISTORY_MAX } from '../shared.js'\n\nexport function createSessionInfoSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n): SessionInfoSlice {\n /**\n * 会话运行时长的缓存:每分钟最多问一次(统计数字不需要更密),同会话复用。\n *\n * 与图片那种\"重\"缓存分开存:这个条目只有几十字节,TTL 到期直接覆盖。\n */\n var sessionTimes = new Map<string, { at: number; value: HudSessionTime }>()\n /** 拿到数的缓存时长。 */\n var SESSION_TIME_TTL_MS = 60000\n /**\n * **没拿到**时的重问间隔。\n *\n * 会话是异步恢复到宿主 `sessions` 存储里的:重启后头几百毫秒问 `session-time` 得到的是\n * \"这个会话还不存在\"(实测启动 0.6 秒时 live=0,30 秒后才 live=1)。\n * 把这种答案按 60 秒缓存,就会让计时胶囊在你重启后一两分钟才出现 —— 所以负面结果只记 3 秒。\n */\n var SESSION_TIME_RETRY_MS = 3000\n /** 取一个会话的运行时长(含内置 `sessionStats` 的累计数字);拿不到回 `available: false`。 */\n function loadSessionTime(sessionId: string): Promise<HudSessionTime> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false })\n var cached = sessionTimes.get(sessionId)\n if (cached !== undefined) {\n var ttl =\n cached.value && cached.value.available === true\n ? SESSION_TIME_TTL_MS\n : SESSION_TIME_RETRY_MS\n if (Date.now() - cached.at < ttl) return Promise.resolve(cached.value)\n }\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudSessionTime>(CHANNEL, 'session-time', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n var value: HudSessionTime =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId }\n if (sessionTimes.size >= 16) sessionTimes.clear()\n sessionTimes.set(sessionId, { at: Date.now(), value: value })\n var previous: HudSessionTime | null =\n getState().sessionTime && getState().sessionTime.sessionId === sessionId\n ? getState().sessionTime.value\n : null\n // 深度比较没必要:这两个字段(会话创建时刻 + 累计墙钟)就是整份值里会变的部分。\n var same =\n previous !== null &&\n previous.available === value.available &&\n previous.createdAt === value.createdAt &&\n previous.llmMs === value.llmMs &&\n previous.toolMs === value.toolMs &&\n previous.turns === value.turns &&\n previous.steps === value.steps\n if (!same) set({ sessionTime: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId }\n })\n }\n /**\n * prompt 历史:宿主从内置 `turnOutline` 投影里取,比客户端自己记一份可靠得多\n * (分页、压实、重启、换设备都不影响)。prompt 只在\"一整轮发出去\"之后才会变,\n * 所以 5 秒 TTL 足够,而且顺手把值写进快照 —— 按键那侧只读快照,不必再 await。\n */\n var promptHistories = new Map<string, { at: number; value: HudPromptHistory }>()\n var PROMPT_HISTORY_TTL_MS = 5000\n\n function loadPromptHistory(sessionId: string): Promise<HudPromptHistory> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false, prompts: [] })\n var cached = promptHistories.get(sessionId)\n if (cached !== undefined && Date.now() - cached.at < PROMPT_HISTORY_TTL_MS)\n return Promise.resolve(cached.value)\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudPromptHistory>(CHANNEL, 'prompt-history', {\n args: { sessionId: sessionId, limit: PROMPT_HISTORY_MAX },\n })\n })\n .then(function (result) {\n var value: HudPromptHistory =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId, prompts: [] }\n if (promptHistories.size >= 16) promptHistories.clear()\n promptHistories.set(sessionId, { at: Date.now(), value: value })\n var previous: HudPromptHistory | null =\n getState().promptHistory && getState().promptHistory.sessionId === sessionId\n ? getState().promptHistory.value\n : null\n var same =\n previous !== null &&\n previous.available === value.available &&\n (previous.prompts || []).length === (value.prompts || []).length &&\n (previous.prompts || []).every(function (item, index) {\n return item === value.prompts[index]\n })\n if (!same) set({ promptHistory: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId, prompts: [] }\n })\n }\n\n return { loadPromptHistory: loadPromptHistory, loadSessionTime: loadSessionTime }\n}\n","/**\n * 「悬停提示」这一片 store:把气泡的文案与锚点放进快照,由 overlay 里那枚座位去画。\n */\n\nimport type { GetState, StoreSet, TipAnchor, TipHandlers, TipSlice } from './types.js'\n\n/** 事件里的锚点矩形:真实 DOM 给的是 DOMRect(结构上就是这三个数)。 */\ninterface AnchorTarget {\n getBoundingClientRect?: () => TipAnchor\n}\n\nexport function createTipSlice(getState: GetState, set: StoreSet): TipSlice {\n /** 显示某枚胶囊的悬停提示(`rect` 由调用方从 `getBoundingClientRect()` 给)。 */\n function showTip(key: string, text: string, rect: TipAnchor | null): void {\n if (typeof text !== 'string' || text.length === 0) return\n var at: TipAnchor =\n rect && typeof rect === 'object'\n ? { left: rect.left, top: rect.top, right: rect.right }\n : { left: 0, top: 0, right: 0 }\n set({ tip: { key: key, text: text, rect: at } })\n }\n\n /** 收起悬停提示。带 `key` 时只在\"还是这一枚\"的情况下收 —— 免得旧的 leave 把新的提示关掉。 */\n function hideTip(key?: string): void {\n var current = getState().tip\n if (current === null || current === undefined) return\n if (typeof key === 'string' && current.key !== key) return\n set({ tip: null })\n }\n\n /**\n * 一枚胶囊的悬停接线:算出锚点矩形,把文案交给 store。\n *\n * 为什么走 store 而不是就地渲染气泡:气泡必须**逃出行内盒**(不被 dock 那行的裁切与\n * 堆叠上下文限制),所以它由 `shell.overlay` 里一枚单独的座位画,位置用 `position:fixed`。\n */\n function tipHandlers(key: string, textOf: () => string): TipHandlers {\n return {\n onPointerEnter: function (event) {\n // 事件目标是 DOM 边界:只按 `getBoundingClientRect` 这一项能力收窄。\n var target = event && event.currentTarget ? (event.currentTarget as AnchorTarget) : null\n var rect =\n target && typeof target.getBoundingClientRect === 'function'\n ? target.getBoundingClientRect()\n : null\n showTip(key, textOf(), rect)\n },\n onPointerLeave: function () {\n hideTip(key)\n },\n onPointerDown: function () {\n hideTip(key)\n },\n }\n }\n\n return { hideTip: hideTip, showTip: showTip, tipHandlers: tipHandlers }\n}\n","/**\n * 「工具活动 / 侧栏摘要 / 回合信息」这一片 store:三样都跟会话走、与工作区无关,各自一条轮询节拍;回合信息还带一个 sessions 动态注入。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { HudSessionOverviewRow, HudToolActive, TurnsState } from '../shared.js'\nimport type {\n GetState,\n SessionStatusesPayload,\n SessionToolsPayload,\n StoreContext,\n SessionsService,\n StoreSet,\n ToolsSlice,\n TurnFace,\n TurnSession,\n} from './types.js'\n\nimport { CHANNEL, probe } from '../shared.js'\n\n/** 回合投影的订阅记账:`{ sessionId, dispose }`;`null` 表示没接。 */\ninterface TurnsBinding {\n sessionId: string | null\n dispose: (() => void) | null\n}\n\nexport function createToolsSlice(ctx: StoreContext, getState: GetState, set: StoreSet): ToolsSlice {\n /** 工具活动那次请求(在途合并)。 */\n var toolsInflight: Promise<unknown> | null = null\n /** 侧栏摘要那次请求(在途合并)。 */\n var rowsInflight: Promise<unknown> | null = null\n /** 回合投影的订阅:`{ sessionId, dispose }`;`null` 表示没接。 */\n var turnsBound: TurnsBinding | null = null\n /** `sessions` 客户端服务(动态注入;没到齐就是 null,回合信息不显示)。 */\n var turnSessions: SessionsService | null = null\n\n // 回合信息要的 `sessions` 客户端服务:**动态注入**等它到齐(软依赖)——\n // 缺它时 git、归因、胶囊一切照旧,只是不显示\"第几轮\"。到齐后把当前会话接上。\n if (typeof ctx.inject === 'function') {\n ctx.effect(function () {\n return ctx.inject(['sessions'], function (scope) {\n turnSessions = scope.sessions\n probe.log('turn-services', { sessions: true })\n bindTurns(getState().sessionId)\n return function () {\n turnSessions = null\n unbindTurns()\n }\n })\n }, 'dsh-hud: turn services')\n }\n\n /**\n * 接上 `turnOutline` 投影(\"第几轮改的\")。\n *\n * 软依赖:`sessions` 服务、已物化的会话、这个投影三者缺一个就安静地不接 —— 归因、\n * git、胶囊一切照旧,只是不显示回合信息。整段包在 try/catch 里,读不到不该把面板带崩。\n */\n function unbindTurns(): void {\n if (turnsBound === null) return\n try {\n if (typeof turnsBound.dispose === 'function') turnsBound.dispose()\n } catch (error) {\n // 解绑失败不影响接下来的绑定。\n }\n turnsBound = null\n set({ turns: { available: false, prompts: {}, latest: null } })\n }\n\n function bindTurns(sessionId: string | null | undefined): void {\n if (turnsBound !== null && turnsBound.sessionId === sessionId) return\n unbindTurns()\n if (!sessionId || turnSessions === null) return\n try {\n var bound = turnSessions.binding(sessionId)\n var live = bound ? bound.session : undefined\n if (!live || !live.projections || typeof live.projections.faceOf !== 'function') return\n var face = live.projections.faceOf('turnOutline')\n if (!face || typeof face.getSnapshot !== 'function') return\n // 已用 typeof 确认 getSnapshot 是函数:这里断言成\"带 getSnapshot 的 face\",\n // 让闭包里可以保持 this 绑定地调用它(把方法拆出来再调会丢 this)。\n var liveFace = face as TurnFace & { getSnapshot: () => unknown }\n var apply = function () {\n try {\n set({ turns: normalizeTurns(liveFace.getSnapshot()) })\n } catch (error) {\n probe.log('turns-read-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n apply()\n var dispose = typeof liveFace.subscribe === 'function' ? liveFace.subscribe(apply) : null\n turnsBound = { sessionId: sessionId, dispose: dispose }\n } catch (error) {\n // 会话还没物化、投影还没注册、注入没到位 —— 都只意味着\"这一轮拿不到回合信息\"。\n probe.log('turns-bind-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n /** 归一 `turnOutline` 快照:`{ turns: [{ turn, prompt }], draft }`。 */\n function normalizeTurns(snapshot: unknown): TurnsState {\n // 投影快照是外部服务给的 unknown:先收成记录,再逐项判断。\n var record =\n snapshot !== null && typeof snapshot === 'object' ? (snapshot as { turns?: unknown }) : null\n var entries = record !== null && Array.isArray(record.turns) ? record.turns : null\n if (entries === null) return { available: false, prompts: {}, latest: null }\n var prompts: Record<number, string> = {}\n var latest: number | null = null\n for (var index = 0; index < entries.length; index += 1) {\n var entry = entries[index]\n if (!entry || typeof entry !== 'object') continue\n // 每条 turn 也是外部形状:同样先收成记录再逐字段判断。\n var item = entry as { turn?: unknown; prompt?: unknown }\n if (typeof item.turn !== 'number') continue\n if (typeof item.prompt === 'string' && item.prompt.length > 0)\n prompts[item.turn] = item.prompt\n if (latest === null || item.turn > latest) latest = item.turn\n }\n return { available: true, prompts: prompts, latest: latest }\n }\n\n /**\n * 取侧栏每条会话行要的摘要(**一次批量**,不是 N 次 RPC)。\n *\n * 会话列表由**宿主**枚举(它有 `sessions` 服务),客户端不用把名单端过去;宿主没有\n * 那个服务时回 `unavailable: true`,那些 chip 就整片不画(其余功能照旧)。\n */\n function refreshRows(): Promise<unknown> {\n if (rowsInflight) return rowsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionStatusesPayload>(CHANNEL, 'session-statuses', {\n args: {},\n })\n })\n .then(function (result) {\n rowsInflight = null\n if (result && result.ok === true && result.value) {\n var byId: Record<string, HudSessionOverviewRow> = {}\n var list = Array.isArray(result.value.sessions) ? result.value.sessions : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.sessionId === 'string') byId[item.sessionId] = item\n }\n probe.log('session-statuses', {\n sessions: list.length,\n unavailable: result.value.unavailable === true,\n worktrees: typeof result.value.worktrees === 'number' ? result.value.worktrees : null,\n truncated: result.value.truncated === true,\n })\n set({\n rows: {\n phase: 'ready',\n byId: byId,\n unavailable: result.value.unavailable === true,\n truncated: result.value.truncated === true,\n updatedAt: Date.now(),\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-statuses-failed', { code: code || 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n rowsInflight = null\n probe.log('session-statuses-failed', { code: 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n rowsInflight = task\n return task\n }\n\n /** 取本会话的工具活动(正在跑哪个 / 跑过哪些 / 各几次)。 */\n function loadTools(): Promise<unknown> {\n var sessionId = getState().sessionId\n if (!sessionId) return Promise.resolve()\n if (toolsInflight) return toolsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionToolsPayload>(CHANNEL, 'session-tools', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n toolsInflight = null\n if (getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var active: HudToolActive | null =\n value.active && typeof value.active === 'object' ? value.active : null\n var tools = {\n phase: 'ready',\n active: active,\n recent: Array.isArray(value.recent) ? value.recent : [],\n counts: Array.isArray(value.counts) ? value.counts : [],\n skills: Array.isArray(value.skills) ? value.skills : [],\n skillsDropped: typeof value.skillsDropped === 'number' ? value.skillsDropped : 0,\n total: typeof value.total === 'number' ? value.total : 0,\n updatedAt: Date.now(),\n }\n // 内容没变就不要 set:这个节拍很密(见 TOOLS_POLL_MS),无谓的重渲染会到处都是。\n var previous = getState().tools\n var same =\n previous !== null && previous.active !== null && tools.active !== null\n ? previous.active.callId === tools.active.callId && previous.total === tools.total\n : previous !== null &&\n previous.active === null &&\n tools.active === null &&\n previous.total === tools.total &&\n previous.recent.length === tools.recent.length &&\n (previous.recent.length === 0 || previous.recent[0].at === tools.recent[0].at)\n if (same) return\n probe.log('session-tools', {\n active: tools.active === null ? null : tools.active.name,\n total: tools.total,\n kinds: tools.counts.length,\n // 技能单独露面:排查\"技能名有没有记到\"时不必再靠猜(这一版新增的字段)。\n skills: tools.skills.length,\n })\n set({ tools: tools })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-tools-failed', { code: code || 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n toolsInflight = null\n probe.log('session-tools-failed', { code: 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n toolsInflight = task\n return task\n }\n\n return {\n bindTurns: bindTurns,\n loadTools: loadTools,\n refreshRows: refreshRows,\n unbindTurns: unbindTurns,\n }\n}\n","/**\n * 共享 store 的组装层:自己管状态、订阅、持久化与布局,七片各自管自己的域。\n *\n * 它对外只暴露浏览器半需要的那一套(打开面板 / 差异 / 提交 / 会话列表摘要 / 审阅 …),\n * 每个\"片\"的入口都只是转发到这个文件外面的小工厂。\n */\n\nimport { diffPayload } from '../components/diff-view.js'\nimport type { HudChange, HudState, HudStore, HudStatus, PanelPos } from '../shared.js'\nimport {\n CHANNEL,\n COMMIT_LIST_WIDTH,\n COMMIT_MIN_WIDTH,\n DIFF_DEFAULT_WIDTH,\n DIFF_MIN_WIDTH,\n ZOOM_STEPS,\n readReviewed,\n readUi,\n reviewKeyOf,\n visibleChangeRows,\n writeUi,\n} from '../shared.js'\nimport type { StoreContext } from './types.js'\nimport { createAttributionSlice } from './attribution.js'\nimport { createCommitsSlice } from './commits.js'\nimport { createDiffSlice } from './diff.js'\nimport { createImageSlice } from './image.js'\nimport { createSessionInfoSlice } from './session-info.js'\nimport { createTipSlice } from './tip.js'\nimport { createToolsSlice } from './tools.js'\nexport function createStore(ctx: StoreContext): HudStore {\n var imageSlice = createImageSlice(ctx)\n var ui = readUi()\n var pos: PanelPos | null =\n ui.pos &&\n typeof ui.pos === 'object' &&\n typeof ui.pos.left === 'number' &&\n typeof ui.pos.top === 'number'\n ? { left: ui.pos.left, top: ui.pos.top }\n : null\n\n var state: HudState = {\n phase: 'idle',\n sessionId: null,\n cwd: null,\n status: null,\n errorCode: null,\n updatedAt: null,\n panelOpen: ui.open === true,\n pos: pos,\n /** 胶囊所在 dock 行的视口顶部坐标:面板据此贴到它上方,而不是用写死的偏移。 */\n anchorTop: null,\n /** 正在查看的差异;`null` 表示面板停在改动清单。 */\n diff: null,\n /**\n * 正在查看的提交(点了「最近提交」里的一行);`null` 表示左栏是工作区清单。\n * 打开时左栏换成这次提交的改动清单,右栏打开它第一个文件的差异。\n */\n commit: null,\n /**\n * 键盘高亮的那个文件(清单里的 `file`);`null` 表示还没用键盘选过。\n * 指向 `file` 而不是下标:轮询刷新后清单条目可能增减,下标会指错行。\n */\n cursor: null,\n /** 最近提交;`commitsOid` 记住它是哪个 HEAD 取的,oid 变了才重取。 */\n commits: null,\n commitsOid: null,\n /**\n * 标签列表。与提交不同,**新 tag 可以打在老提交上**(HEAD 不动),所以不能只按 oid 判断;\n * 改用\"上次取的时刻\"做新鲜度窗口:面板开着时每次刷新都看一眼,但 10s 内不重复问 git。\n */\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n /** 差异栏宽度(面板右边缘可拖),持久化。 */\n diffWidth: typeof ui.diffWidth === 'number' ? ui.diffWidth : DIFF_DEFAULT_WIDTH,\n /** 提交明细栏宽度(它与差异栏之间那条抓握可拖),持久化。 */\n commitWidth: typeof ui.commitWidth === 'number' ? ui.commitWidth : COMMIT_LIST_WIDTH,\n /**\n * 会话归因:本会话改过哪些文件(宿主半按 fs 事件记的)。\n *\n * `files` 为 `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)——\n * 界面据此不打标记、也不过这个滤。\n */\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n /** 「只看本会话改的」开关(持久化,跟\"自动换行\"一样)。 */\n onlySession: ui.onlySession === true,\n /** 「只看最近一轮」开关:把清单收窄到最近这一轮动过的文件。 */\n onlyLatest: ui.onlyLatest === true,\n /** 这一版每个改动文件的签名(`file → signature`);空表表示\"这次没要\"。 */\n review: {},\n /**\n * 本会话的工具活动:`active`(正在跑哪个)+ `recent` + 计数。\n *\n * 实时秒数由客户端本地算(`active.startedAt`),不靠把轮询压到亚秒级。\n */\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n // 会话时长(计时胶囊用):`{ sessionId, value }`;取数写进快照,渲染那边就是纯函数。\n sessionTime: { sessionId: null, value: null },\n // prompt 历史(输入框 `↑` 召回用):`{ sessionId, value }`,value 里 prompts 是**新的在前**。\n promptHistory: { sessionId: null, value: null },\n // 胶囊悬停提示:`{ key, text, rect }` 或 null。**自己画**,不用浏览器原生 tooltip ——\n // 原生那个在真机上按了没反应(试过三种节点/容器查证,都不是被谁挡住),\n // 而它延迟、样式、能否出现我们都控制不了;自己画则位置、换行、颜色全说了算。\n tip: null,\n /** 侧栏行内 chip 要的摘要:一次批量取回,按 sessionId 索引。 */\n rows: { phase: 'idle', byId: {}, unavailable: false, truncated: false, updatedAt: null },\n /**\n * 我审过的版本:`工作区 + NUL + 文件 → { signature, at }`,存在自己的键里\n * (`dsh-hud.reviewed.v1`,不跟界面开关混在一起)。\n */\n reviewed: readReviewed(),\n /**\n * 回合信息(`turnOutline` 投影):`第 N 轮 → 那一轮的请求预览`。\n *\n * `available: false` 表示这个宿主没有这个投影(或还没读到)——界面这时不提回合,\n * 也绝不因为读不到它影响别的部分。\n */\n turns: { available: false, prompts: {}, latest: null },\n }\n\n var listeners = new Set<() => void>()\n var inflight: Promise<unknown> | null = null\n /** 会话归因那次请求(在途合并)。 */\n\n function getSnapshot(): HudState {\n return state\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return function () {\n listeners.delete(listener)\n }\n }\n\n function emit(): void {\n listeners.forEach(function (listener) {\n listener()\n })\n }\n\n function set(patch: Partial<HudState>): void {\n state = Object.assign({}, state, patch)\n emit()\n }\n\n // 悬停提示片:用到 state/set,所以在两者定义之后再组装。\n var tipSlice = createTipSlice(function () {\n return state\n }, set)\n var sessionInfoSlice = createSessionInfoSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var toolsSlice = createToolsSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var commitsSlice = createCommitsSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n closeDiff: function () {\n return diffSlice.closeDiff()\n },\n },\n )\n var attributionSlice = createAttributionSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 惰性包装:这几样分属工具片与组装层,用包装就不必纠结创建顺序。\n bindTurns: function (sessionId) {\n return toolsSlice.bindTurns(sessionId)\n },\n loadTools: function () {\n return toolsSlice.loadTools()\n },\n refresh: function () {\n return refresh()\n },\n refreshRows: function () {\n return toolsSlice.refreshRows()\n },\n reviewedKeyOf: reviewedKeyOf,\n },\n )\n var diffSlice = createDiffSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 这两样暂住在 store 里(清单选择 / 提交差异),等提交那一片搬走时再挪过去。\n hunkCount: hunkCount,\n openCommitDiff: commitsSlice.openCommitDiff,\n openListEntry: openListEntry,\n persist: persist,\n visibleFiles: visibleFiles,\n },\n )\n\n function persist(): void {\n var diff = state.diff\n writeUi({\n onlySession: state.onlySession === true,\n onlyLatest: state.onlyLatest === true,\n open: state.panelOpen,\n pos: state.pos,\n wrap: diff ? diff.wrap === true : false,\n zoom:\n diff && typeof diff.zoom === 'number' && ZOOM_STEPS.indexOf(diff.zoom) >= 0\n ? diff.zoom\n : null,\n diffWidth: state.diffWidth,\n commitWidth: state.commitWidth,\n })\n }\n\n /** 拖动提交栏右边那条抓握时只改内存;松手才落盘。 */\n function setCommitWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(COMMIT_MIN_WIDTH, Math.round(width))\n if (state.commitWidth === next) return\n set({ commitWidth: next })\n }\n\n /** 拖动右边缘时只改内存;松手才落盘(persistLayout)。 */\n function setDiffWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(DIFF_MIN_WIDTH, Math.round(width))\n if (state.diffWidth === next) return\n set({ diffWidth: next })\n }\n\n /* --------------------------------------------------- 图片预览(二进制) */\n\n /**\n * 图片两侧的缓存:键与请求一一对应。\n *\n * 一张图的 base64 可能上兆,所以命中就不重取;条目数有上限(超了整表清空,\n * 和宿主的 `CACHE_MAX_ENTRIES` 同一个思路:宁可少缓存,也不要无界增长)。\n */\n\n /**\n * 收起右栏。提交明细把差异包在里面,所以它优先整体退掉;\n * 只有工作区差异时才只是关掉差异。\n */\n function closeDetail(): void {\n if (state.commit !== null) return commitsSlice.closeCommit()\n return diffSlice.closeDiff()\n }\n\n /**\n * 当前能上下选的那份清单:打开提交明细时是这次提交的改动,否则是工作区清单。\n *\n * 清单必须跟视图一致 —— 否则打开着提交明细时按 ↑/↓ 会去动工作区的行,\n * 高亮和打开的文件对不上。\n */\n function visibleFiles(): HudChange[] {\n if (state.commit !== null) {\n return Array.isArray(state.commit.files) ? state.commit.files : []\n }\n // 与面板渲染**共用同一份**过滤结果:过滤只发生在一个地方,↑/↓ 才不会落到看不见的行上。\n return visibleChangeRows(state)\n }\n\n /** 打开清单里的一条:提交明细打开时走提交差异,否则走工作区差异。 */\n function openListEntry(file: HudChange): Promise<unknown> {\n if (state.commit !== null) return commitsSlice.openCommitDiff(file)\n return diffSlice.openDiff(file)\n }\n\n /** 当前差异里有几个 hunk —— n / p 的边界就是它。 */\n function hunkCount(): number {\n var payload = diffPayload(state.diff)\n var rows = payload && payload.rows ? payload.rows : []\n var total = 0\n for (var index = 0; index < rows.length; index += 1) {\n if (rows[index].kind === 'hunk') total += 1\n }\n return total\n }\n\n /**\n * 图片缩放的档位(`null` = 适应宽度)。\n *\n * 放在 store 而不是组件里:一来记住(和\"自动换行\"一样落盘),二来组件的 hooks\n * 在这个假 React 里驱动不了嵌套状态 —— store 的状态任何位置都读得到。\n */\n function setZoom(value: number | null): void {\n var diff = state.diff\n if (!diff) return\n var next = value === null || ZOOM_STEPS.indexOf(value) >= 0 ? value : null\n set({ diff: Object.assign({}, diff, { zoom: next }) })\n persist()\n }\n\n /** dock 行的位置变了才写,避免每次测量都触发重渲染。 */\n function setAnchorTop(top: number | null): void {\n if (state.anchorTop === top) return\n set({ anchorTop: top })\n }\n\n function refresh(): Promise<unknown> {\n // 会话归因跟 status 同一条命(两个都是\"当前会话 + 当前工作区\"的只读事实)。\n attributionSlice.loadSessionChanges()\n // 工具活动只跟会话有关(没有工作区也该显示\"正在跑什么\")。\n toolsSlice.loadTools()\n // 回合投影:会话还没物化时这里会安静地不接,下一次刷新再试。\n toolsSlice.bindTurns(state.sessionId)\n if (!state.cwd) return Promise.resolve()\n if (inflight) return inflight\n var cwd = state.cwd\n\n set({ phase: state.status ? 'ready' : 'loading' })\n\n inflight = Promise.resolve()\n .then(function () {\n // 面板打开时才要\"这一版\"的签名:胶囊那一路不需要,白付一次 stat。\n return ctx.connection.rpc.call<HudStatus>(CHANNEL, 'status', {\n args: { cwd: cwd, review: state.panelOpen === true },\n })\n })\n .then(function (result) {\n inflight = null\n if (state.cwd !== cwd) return\n if (result && result.ok === true) {\n var statusValue = result.value || null\n set({\n phase: 'ready',\n status: statusValue,\n // 送回来的\"这一版\"签名(没要或没改动就是空表)。\n review:\n statusValue && statusValue.review && typeof statusValue.review === 'object'\n ? statusValue.review\n : {},\n errorCode: null,\n updatedAt: Date.now(),\n })\n // 提交只取一次:面板打开时取,且只有 HEAD 变了(提交过、切分支、拉取)才重取。\n if (\n statusValue &&\n state.panelOpen &&\n (state.commits === null || state.commitsOid !== statusValue.oid)\n ) {\n commitsSlice.loadCommits(cwd, statusValue.oid)\n }\n // 标签与 HEAD 无关(新 tag 可以打在老提交上),所以只要面板开着就问一次(10s 窗口)。\n if (statusValue && state.panelOpen) commitsSlice.loadTags(cwd)\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({ phase: 'error', errorCode: code || 'git-failed' })\n })\n .catch(function () {\n inflight = null\n if (state.cwd !== cwd) return\n set({ phase: 'error', errorCode: 'transport' })\n })\n\n return inflight\n }\n\n /** 审阅存档的键:**按工作区分开**,同一个文件名在别的仓库里是另一码事。 */\n function reviewedKeyOf(file: string): string {\n return reviewKeyOf(state.cwd, file)\n }\n\n /** 「只看最近一轮」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlyLatest(): void {\n set({ onlyLatest: state.onlyLatest !== true })\n persist()\n }\n\n /** 「只看本会话改的」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlySession(): void {\n set({ onlySession: state.onlySession !== true })\n persist()\n }\n\n function openPanel(open: boolean): void {\n if (state.panelOpen === open) return\n set({ panelOpen: open })\n persist()\n if (open) refresh()\n }\n\n function togglePanel(): void {\n openPanel(!state.panelOpen)\n }\n\n /** 拖动过程中的临时位置:只改内存,不写 localStorage。 */\n function movePanel(next: PanelPos): void {\n set({ pos: next })\n }\n\n /** 松开指针后的位置:落盘。 */\n function commitPanel(next: PanelPos): void {\n set({ pos: next })\n persist()\n }\n\n function resetPanel(): void {\n set({ pos: null })\n persist()\n }\n\n return {\n getSnapshot: getSnapshot,\n subscribe: subscribe,\n bindSession: attributionSlice.bindSession,\n refresh: refresh,\n openPanel: openPanel,\n togglePanel: togglePanel,\n movePanel: movePanel,\n commitPanel: commitPanel,\n resetPanel: resetPanel,\n setAnchorTop: setAnchorTop,\n setDiffWidth: setDiffWidth,\n setCommitWidth: setCommitWidth,\n persistLayout: persist,\n openDiff: diffSlice.openDiff,\n loadImagePair: imageSlice.loadImagePair,\n cachedImagePair: imageSlice.cachedImagePair,\n loadSessionChanges: attributionSlice.loadSessionChanges,\n loadSessionTime: sessionInfoSlice.loadSessionTime,\n loadPromptHistory: sessionInfoSlice.loadPromptHistory,\n showTip: tipSlice.showTip,\n hideTip: tipSlice.hideTip,\n tipHandlers: tipSlice.tipHandlers,\n closeDiff: diffSlice.closeDiff,\n closeDetail: closeDetail,\n setDiffSide: diffSlice.setDiffSide,\n toggleDiffWrap: diffSlice.toggleDiffWrap,\n setZoom: setZoom,\n openCommit: commitsSlice.openCommit,\n openCommitDiff: commitsSlice.openCommitDiff,\n toggleCommit: commitsSlice.toggleCommit,\n closeCommit: commitsSlice.closeCommit,\n stepFile: diffSlice.stepFile,\n openCursor: diffSlice.openCursor,\n stepHunk: diffSlice.stepHunk,\n toggleHunk: diffSlice.toggleHunk,\n toggleOnlySession: toggleOnlySession,\n toggleOnlyLatest: toggleOnlyLatest,\n refreshRows: toolsSlice.refreshRows,\n loadTools: toolsSlice.loadTools,\n toggleReviewed: attributionSlice.toggleReviewed,\n clearReviewed: attributionSlice.clearReviewed,\n }\n}\n","/**\n * 样式落地:把 `styles.ts` 那份表插进 `<head>`(用 `CSS_TAG_ID` 防重复注入),\n * 以及从已注入的样式里取一个值的助手。\n */\n\nimport { CSS } from './styles.js'\nimport { CSS_TAG_ID } from './shared.js'\n/**\n * 审阅进度:`{ marks: {file → 'fresh'|'stale'}, done, total, staleCount }`。\n *\n * 判据是**签名**:宿主 stat 出来的 size+mtimeMs 和存档里的是同一个才算\"这一版审过了\"。\n * 签名变了(又被改动)就把那条标记降级成 `stale`,界面显示\"改动已变,需要重看\" ——\n * 而不是继续冒充已审。没有签名的文件(宿主这次没算)不参与统计。\n */\n\n/**\n * 从自有样式表里抠出计数芯片的横向间隔。\n *\n * 用来回答\"页面上跑着的到底是哪一版 CSS\"—— 改完样式没生效时,这一眼就能分清是\n * \"bundle 还是旧的\"还是\"样式规则没生效\"。\n */\nexport function cssChipsGap(css: string): string | null {\n var match = /\\.dshh-chips\\{[^}]*gap:([^;}]+)/.exec(css)\n return match === null ? null : match[1].trim()\n}\n\n/* ---------------------------------------------------------------- 样式 */\n\n/* ---------------------------------------------------------------- 样式注入 */\n\n/**\n * 幂等注入自有样式表,并**自己打上 `data-plugin`**。\n *\n * 框架的 client-modules 会在每次模块物化后 `claimStyles(id)`:把当时所有\n * **没有 `data-plugin`** 的 `<style>` 认领给刚物化的那个模块,然后在 HMR 的\n * replace / invalidate / prune 路径上 `removeOwnedStyles(id)` 按 `data-plugin`\n * 删掉。不打标的表会被别的模块领走,在那次 rebuild 里连同别人的表一起删掉 ——\n * 而我们的注入只在插件激活时跑一次,于是页面永久退回骨架(原生 `button`、\n * 没有间隙 / 省略号 / 居中几何)。\n *\n * 框架自己的包与同门的 dsh-git-graph 都按这个约定打标;tag 存在时复用同一节点\n * (HMR 会先把旧表删掉,再由新的 fiber 重新调用本函数)。\n *\n * @returns 该样式节点;没有 `document` 时返回 `null`。\n */\nexport function ensureStyles(): HTMLStyleElement | null {\n if (typeof document === 'undefined') return null\n var selector = 'style[data-plugin-css=\"' + CSS_TAG_ID + '\"]'\n // 选择器只可能命中我们自己注入的 <style>(tag id 是插件私有的):这里断言成 HTMLStyleElement。\n var tag = (\n typeof document.querySelector === 'function' ? document.querySelector(selector) : null\n ) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n // 值必须等于客户端模块 id(= 包名):客户端的 `removeOwnedStyles(id)` 靠它回收样式。\n tag.setAttribute('data-plugin', '@konnga/dsh-hud')\n tag.setAttribute('data-plugin-css', CSS_TAG_ID)\n tag.setAttribute('data-dsh-hud', 'styles')\n document.head.appendChild(tag)\n }\n tag.textContent = CSS\n return tag\n}\n","/**\n * dsh-hud — 浏览器半(`dsh.client` bundle)的**插件面**。\n *\n * 两个座位,一个共享 store:\n *\n * - `conversation.input.dock` → 胶囊:输入框上方那一行里靠左的紧凑状态胶囊\n * (分支摘要,或\"非 git 仓库\"),点击开关面板。这个座位是**会话作用域**,\n * 框架直接把当前 `sessionId` 交给它,它再从 `useSessions` 快照里取该会话的\n * `cwd` 写进共享 store —— 这是 root 作用域的面板**读不到**的东西。\n * - `shell.overlay` → 悬浮面板:收起来时渲染 `null`(不留会挡住输入框的气泡),\n * 展开时是悬浮面板,默认停在输入框上方。\n *\n * 所有 git 工作都在宿主半(`/dsh-hud-rpc`);这里只负责座位注册、轮询节拍、\n * 语言字典与样式注入 —— 具体行为都在 store / hooks / components 里。\n *\n * ⚠️ `lib/client.js` 是从 `src/client/**` **打包出来的产物**(`npm run build`):浏览器半只支持\n * \"一个经典脚本\",所以源码分模块、产物是单文件(见 `rolldown.config.mjs`)。别直接改产物。\n */\n\nimport { CSS } from './styles.js'\nimport { HudDockPill } from './components/dock-pill.js'\nimport { HudFloatingPanel } from './components/floating-panel.js'\nimport { PillTip } from './components/pill-tip.js'\nimport { SessionMenuSummary, SessionRowHoverChip } from './components/sidebar.js'\nimport { TurnImages } from './components/turn-images.js'\nimport { bindPromptHistory } from './composer.js'\nimport { en, zh } from './i18n.js'\nimport { NS, POLL_MS, ROWS_POLL_MS, TOOLS_POLL_MS, probe } from './shared.js'\nimport { createStore } from './store/index.js'\nimport type { StoreContext } from './store/types.js'\nimport { cssChipsGap, ensureStyles } from './styles-apply.js'\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\n\nexport var inject = ['slots', 'connection', 'locale']\n\n/* --------------------------------------------------------- 框架注入面 */\n\n/**\n * 客户端插件上下文里用到的部分(框架给的 `ctx`)。\n *\n * 座位定义在注册时才拿到 `sessionId`(会话作用域座位有、root 作用域没有),\n * 所以这里把 `inject` 的形参声明出来,让回调里的 `sessionId` 有类型可依。\n */\ninterface HudSlotDefinition {\n name: string\n id: string\n order?: number\n locale?: string\n inject?: (sessionId?: string) => Record<string, unknown>\n}\n\ninterface HudSlots {\n inject(slot: string, callback: () => unknown): unknown\n register(definition: HudSlotDefinition, component: unknown): unknown\n}\n\ninterface HudLocale {\n register(\n namespace: string,\n dictionaries: { zh: Record<string, string>; en: Record<string, string> },\n ): (() => void) | void\n}\n\ninterface HudPluginContext extends StoreContext {\n slots: HudSlots\n locale: HudLocale\n}\n\n/* ---------------------------------------------------------------- 插件 */\n\n/**\n * 客户端插件主体。\n * @param ctx - 客户端根上下文(slots、connection、locale)。\n */\nexport function apply(ctx: HudPluginContext): void {\n ctx.effect(function () {\n return ctx.locale.register(NS, { zh: zh, en: en })\n }, 'dsh-hud: dictionaries')\n\n ctx.effect(function () {\n var element = ensureStyles()\n return function () {\n if (element && element.parentNode) element.parentNode.removeChild(element)\n }\n }, 'dsh-hud: styles')\n\n var store = createStore(ctx)\n\n // 起手留一行:诊断里**有没有这一行**就足以区分\"客户端半压根没加载\"与\"加载了但\n // 哪一环没接上\"(这一行由 apply 发,与后面的投影/名单无关)。\n probe.log('apply', {\n inject: typeof ctx.inject === 'function',\n slots: typeof ctx.slots === 'object',\n // 跑着的那一版自有样式表里,计数芯片的间隔是多少:改完样式没生效时用它分辨\n // \"bundle 还是旧的\"还是\"规则没生效\"。\n chips: cssChipsGap(CSS),\n })\n\n // 面板与胶囊都要能反映磁盘上的实时变化;页面不可见时不打请求。\n // 顺手补一次样式表:框架的 HMR 记账会把插件的 `<style>` 删掉,删掉就自愈。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n ensureStyles()\n store.refresh()\n }, POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: status poll')\n\n // 从别处回到页面时立刻补一次(定时器在隐藏期间被跳过)。\n ctx.effect(function () {\n var onFocus = function () {\n ensureStyles()\n store.refresh()\n }\n window.addEventListener('focus', onFocus)\n return function () {\n window.removeEventListener('focus', onFocus)\n }\n }, 'dsh-hud: focus refresh')\n\n // 悬停气泡单独一枚 overlay 座位:它要浮在 dock 行之外(行内会被裁切/堆叠上下文限制)。\n // order 比面板小 —— 面板开着时仍然压在最上层。\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-pill-tip',\n order: 10,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n PillTip,\n )\n })\n\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-panel',\n order: 20,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n HudFloatingPanel,\n )\n })\n\n // 侧栏行内 chip 的数据:起手取一次,之后按自己的节奏更新(页面隐藏时跳过)。\n ctx.effect(function () {\n store.refreshRows()\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.refreshRows()\n }, ROWS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: session rows poll')\n\n // 工具活动单独一条节拍(3s;宿主是内存读,密一点才看得到\"正在跑\")。\n //\n // 走 `store.loadTools()`:这里**曾经是裸 `loadTools()`**,而那个函数只存在于 store 内部、\n // 也没暴露出来 —— 于是这条节拍每 3 秒抛一次 ReferenceError(被 interval 吞掉,只在控制台\n // 留痕),工具读数实际靠 `refresh()` 的 8 秒节拍在维持。已暴露并改为经 store 调用。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.loadTools()\n }, TOOLS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: tool activity poll')\n\n // `⋯` 菜单里的一条只读摘要:行尾拿不到常亮座位,这里是最实在的\"完整信息\"落点。\n ctx.slots.inject('sidebar.workspaces.session.menu.item', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.workspaces.session.menu.item',\n id: 'dsh-hud-session-menu',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionMenuSummary,\n )\n })\n\n // 悬停卡片里那块详情(宽敞的地方;动作条那枚放不下的东西都在这儿)。\n ctx.slots.inject('sidebar.session.row.hover', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.session.row.hover',\n id: 'dsh-hud-session-hover',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionRowHoverChip,\n )\n })\n\n ctx.slots.inject('conversation.input.dock', function () {\n return ctx.slots.register(\n {\n name: 'conversation.input.dock',\n id: 'dsh-hud-pill',\n order: 20,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n HudDockPill,\n )\n })\n\n // 轮尾(内置「已编辑 N 个文件」卡片所在的那条尾巴)的一扇门:本轮动过图,\n // 点缩略图就在内置文档预览里打开;`⇄` 才进本面板看前后对比。\n ctx.slots.inject('conversation.chat.turnTail', function () {\n return ctx.slots.register(\n {\n name: 'conversation.chat.turnTail',\n id: 'dsh-hud-turn-images',\n order: 30,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n TurnImages,\n )\n })\n\n // 输入框里的 ↑ 召回(纯文本):**最后接线** —— 逻辑全在纯函数里,这一层只负责\n // 读值 / 写值 / 摆光标,且**真接管了才拦默认行为**。刻意排在最后注册,好让\n // \"第一个 keydown 监听是面板那条\"这个既有事实保持不变。\n ctx.effect(function () {\n return bindPromptHistory(store)\n }, 'dsh-hud: prompt history (↑ 召回)')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAa,MAAM;CAIjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAKA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,EAAE;;;;;;;;;;ACzPT,IAAW,IAAIA,cAAM;AA0lBrB,IAAW,UAAU;AACrB,IAAW,KAAK;AAEhB,IAAW,SAAS;;;;;;;;AAQpB,IAAW,YAAY;;AAGvB,IAAW,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"}
1
+ {"version":3,"file":"client.js","names":["React","React","React","React","React","React","React","React"],"sources":["../src/client/styles.ts","../src/client/shared.ts","../src/client/format.ts","../src/client/components/balance-pill.ts","../src/client/hooks/use-store.ts","../src/client/components/dock-pill.ts","../src/client/components/session-changes.ts","../src/client/dom.ts","../src/client/components/change-list.ts","../src/client/components/commit-list.ts","../src/client/components/commit-pane.ts","../src/client/components/count-chips.ts","../src/client/components/image-diff.ts","../src/client/components/diff-view.ts","../src/client/components/tag-list.ts","../src/client/hooks/panel.ts","../src/client/components/floating-panel.ts","../src/client/components/pill-tip.ts","../src/client/components/token-panel.ts","../src/client/components/sidebar.ts","../src/client/components/turn-images.ts","../src/client/composer.ts","../src/client/i18n.ts","../src/client/model-watch.ts","../src/client/store/account.ts","../src/client/store/provider.ts","../src/client/store/attribution.ts","../src/client/store/commits.ts","../src/client/store/diff.ts","../src/client/store/image.ts","../src/client/store/session-info.ts","../src/client/store/tip.ts","../src/client/store/tools.ts","../src/client/store/index.ts","../src/client/styles-apply.ts","../src/client/plugin.ts"],"sourcesContent":["/**\n * 面板与胶囊的样式表。\n *\n * 放在数组里逐段拼(不是模板字符串),是为了每段都能单独读懂;\n * `ensureStyles()` 把它插进 `<head>`,用 `CSS_TAG_ID` 防重复注入。\n */\n\n/**\n * 只覆盖本插件自己的类名,配色一律走主题 CSS 变量,亮/暗主题自动跟随。\n * 不碰 document.body、window 或产品的 DOM 选择器。\n */\nexport const CSS = [\n // dock 座位给的是整栏宽的一行,而输入卡片有自己的最大宽度并居中\n // (--dsh-composer-card-max-width)。外层容器复刻框架自带 dock 占位元素\n // (队列公告)的几何,胶囊才会真正贴在输入卡片上方左端,而不是飘到栏的最左。\n '.dshh-dock{box-sizing:border-box;display:flex;align-items:center;gap:6px;justify-content:flex-start;',\n 'width:calc(100% - var(--dsh-composer-side-clearance,16px) - var(--dsh-composer-side-clearance,16px)',\n ' - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'max-width:calc(var(--dsh-composer-card-max-width,952px) - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'margin:0 auto;}',\n '.dshh-pill{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.6;',\n 'width:fit-content;max-width:100%;',\n 'display:inline-flex;align-items:center;gap:4px;',\n 'padding:2px 8px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:999px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-pill:hover{color:var(--dsw-alias-label-primary,#e5e7eb);border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n // 会话计时胶囊:只读读数,所以指针不该是\"可点\";数字用等宽数字,跳动时宽度不抖。\n '.dshh-timer{cursor:default;gap:5px;}',\n // 悬停气泡:`pointer-events:none` 保证它永远不挡指针;`pre-wrap` 保留调用方给的多行结构。\n '.dshh-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:420px;padding:6px 10px;border-radius:8px;' +\n 'background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,#2c2c2e));' +\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));' +\n 'box-shadow:0 6px 20px rgba(0,0,0,0.28);color:var(--dsw-alias-label-primary,#f9fafb);' +\n 'font-size:11.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;pointer-events:none;}',\n // 气泡逐行结构:标签(「余额」「账号已用」…)用次要色、数字用主色 ——\n // 一眼扫到数字,而不是让标签和数字抢同样的注意力。\n '.dshh-tipLine{display:block;}',\n // 行内分段的普通部分继承整行的颜色,只有带 tone 的段自己变色(见 `.dshh-tipSuccess` 等)。\n '.dshh-tipSeg{white-space:pre-wrap;}',\n // 数字用等宽数字:一屏里有好几处读数时,位数变化不会让文字左右抖。\n '.dshh-tipAccent,.dshh-tipSuccess,.dshh-tipWarn,.dshh-tipValue{font-variant-numeric:tabular-nums;}',\n '.dshh-tipLabel{color:var(--dsw-alias-label-secondary,#9aa0a6);}',\n '.dshh-tipValue{color:var(--dsw-alias-label-primary,#f9fafb);margin-left:2px;}',\n // 数字的分类色。三套都取**框架自己的状态/品牌令牌**(它们深浅主题各有取值,\n // 且框架本身就把它们当文字色用),不自己写死十六进制 —— 否则换主题就不对了。\n '.dshh-tipSuccess{color:var(--dsw-alias-state-success-primary,#22c55e);}',\n '.dshh-tipWarn{color:var(--dsw-alias-state-warn-primary,#f59e0b);}',\n '.dshh-tipAccent{color:var(--dsw-alias-state-business-primary,#7aaaff);}',\n '.dshh-tipMuted{color:var(--dsw-alias-label-tertiary,#6b7280);}',\n '.dshh-timerGlyph{flex:none;opacity:.85;}',\n '.dshh-timerValue{font-variant-numeric:tabular-nums;}',\n // 余额胶囊:数字用等宽数字(余额跳动时宽度不抖)。\n // 币种符号由 `formatBalance` 的输出自带,样式里**不另画字形**(早先多画一个就成了 `¥ ¥8.60`)。\n '.dshh-balance{cursor:pointer;gap:2px;font-variant-numeric:tabular-nums;}',\n '.dshh-balanceValue{letter-spacing:.2px;}',\n // 赠送余额的 `+`:不是数字,别让它抢走小数点的注意力。\n '.dshh-balanceBonus{opacity:.75;margin-left:1px;}',\n // `?` = 这一份是上一次的数(刷新失败):用警示色标出来,但不刺眼。\n '.dshh-balanceStale{color:var(--dsw-alias-state-warning-primary,#d7a14a);margin-left:2px;}',\n // 低余额:`⚠` 与金额一起用警示色(金额本身也变色,扫一眼就知道该充值了)。\n '.dshh-balanceWarn{flex:none;color:var(--dsw-alias-state-warning-primary,#d7a14a);margin-right:2px;}',\n '.dshh-balanceLow{color:var(--dsw-alias-state-warning-primary,#d7a14a);}',\n // 「已用」前缀:比数字弱一档,读的时候不会把它当成余额的一部分。\n '.dshh-balanceUsed{flex:none;opacity:.65;margin-right:1px;}',\n // 读不到余额时那枚中性胶囊:整枚压暗,不与有数的胶囊争夺注意力。\n '.dshh-balanceUnknown{opacity:.7;}',\n '.dshh-balanceProvider{flex:none;margin-right:3px;opacity:.8;}',\n '.dshh-panel{color:var(--dsw-alias-label-primary,#e5e7eb);font-size:12.5px;line-height:1.6;}',\n // 后台令牌设置面板:浮在胶囊上方,宽度锁死(凭据是一行长字符串,宽了反而看不过来)。\n '.dshh-tokenPanel{position:fixed;left:50%;transform:translateX(-50%);bottom:96px;z-index:2147483000;',\n 'width:min(420px,calc(100vw - 32px));box-sizing:border-box;display:flex;flex-direction:column;gap:8px;padding:12px 14px;',\n 'border-radius:12px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,0.28));',\n 'background:var(--dsw-alias-bg-layer-1,#1c1d21);color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'box-shadow:0 12px 32px rgba(0,0,0,0.32);font-size:12.5px;line-height:1.55;}',\n '.dshh-tokenHead{display:flex;align-items:baseline;gap:6px;}',\n '.dshh-tokenTitle{font-size:13px;font-weight:600;}',\n '.dshh-tokenProvider{font-family:ui-monospace,Consolas,monospace;font-size:11px;padding:1px 6px;border-radius:6px;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tokenHint{font-size:11.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tokenInput{box-sizing:border-box;width:100%;height:30px;padding:0 9px;border-radius:8px;font:inherit;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:12px;',\n 'border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,0.36));',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.10));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-tokenInput:focus{outline:none;border-color:var(--dsw-alias-state-business-primary,#4b8bf5);}',\n '.dshh-tokenRef{font-size:10.5px;color:var(--dsw-alias-label-tertiary,#6b7280);',\n 'font-family:ui-monospace,Consolas,monospace;}',\n '.dshh-tokenActions{display:flex;gap:8px;justify-content:flex-end;}',\n '.dshh-tokenSave,.dshh-tokenCancel{height:26px;padding:0 12px;border-radius:7px;font:inherit;font-size:12px;cursor:pointer;}',\n '.dshh-tokenSave{border:1px solid transparent;background:var(--dsw-alias-button-primary-fill,#4b8bf5);',\n 'color:var(--dsw-alias-label-primary-foreground,#fff);}',\n '.dshh-tokenSave:disabled{opacity:.45;cursor:default;}',\n '.dshh-tokenCancel{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,0.36));background:transparent;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tokenError{font-size:11.5px;color:var(--dsw-alias-state-error-primary,#f87171);}',\n '.dshh-header{display:flex;align-items:center;gap:8px;padding:6px 8px;flex:none;cursor:move;line-height:1.4;',\n 'user-select:none;touch-action:none;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-title{font-size:13px;font-weight:600;flex:none;}',\n '.dshh-branch{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;padding:1px 6px;border-radius:6px;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-spacer{flex:1;}',\n // 固定盒模型:⟳/⌖/✕ 这类符号可能落到行高夸张的 fallback 字体上,\n // 不锁死宽高就会把标题栏整个撑高(面板上方出现一片空白)。\n '.dshh-icon{font:inherit;font-size:12px;line-height:1;width:22px;height:22px;padding:0;border-radius:6px;',\n 'display:inline-flex;align-items:center;justify-content:center;flex:none;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-icon:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-body{padding:10px;display:flex;flex-direction:column;gap:8px;overflow:auto;}',\n '.dshh-meta{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;}',\n // 不用 direction:rtl 做左省略号:它会把绝对路径开头的 “/” 重排到末尾。\n '.dshh-repo{font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-kv{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sectionTitle{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 计数芯片之间要留出横向间隔:11.5px 字号的描边胶囊挨在一起时看着是贴住的。\n '.dshh-chips{display:flex;flex-wrap:wrap;gap:6px 12px;}',\n '.dshh-chip{display:inline-flex;align-items:center;font-size:11.5px;padding:1px 8px;border-radius:999px;border:1px solid currentColor;}',\n '.dshh-clean{font-size:11.5px;color:var(--dsw-alias-state-success-primary,#3fb950);}',\n // 会话归因:概览下面那行 + 「只看本会话改的」开关 + 清单行首的标记。\n '.dshh-session{display:flex;align-items:baseline;gap:6px 10px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sessionText{min-width:0;}',\n '.dshh-sessionToggle{font:inherit;font-size:11px;padding:0 6px;border-radius:999px;white-space:nowrap;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.35));background:transparent;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-sessionToggle:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));',\n 'color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-sessionToggle:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:1px;}',\n '.dshh-sessionToggleOn{border-color:var(--dsw-alias-brand-primary,#4b8bf5);',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-marker{flex:none;width:8px;text-align:center;font-size:9px;line-height:inherit;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-markerPossible{color:var(--dsw-alias-state-warn-primary,#d9a63c);opacity:.75;}',\n // 行尾的审阅标记:`○` 未审 / `✓` 这一版已审 / `✓`(黄) 改动已变。\n // `⋯` 菜单里那条只读摘要:像一条说明,不像一个可点的菜单项。\n '.dshh-menureadout{display:flex;flex-direction:column;gap:2px;padding:4px 10px 6px;',\n 'font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary,#8a8f98));',\n 'cursor:default;user-select:text;max-width:260px;}',\n '.dshh-menureadoutLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 悬停卡片里那块详情:跟着卡片自己的字号,只做行距与弱化。\n '.dshh-rowhover{display:flex;flex-direction:column;gap:2px;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);min-width:0;}',\n '.dshh-rowhoverLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 「已审 x/y」是个可点控件:同样不能用 label-dimmed(反相表面色,1.26:1 / 1.64:1),\n // 平时用次级文字色,悬停才升到正文色。\n '.dshh-review{flex:none;margin-left:auto;padding:0 2px;font-size:10.5px;cursor:pointer;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-review:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-reviewOn{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-reviewStale{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-ctag{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n // 行首的 `○`(与本会话无关):要\"最不显眼\",但同样得看得见 —— 与上面同一个理由。\n '.dshh-markerOther{color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 工具活动胶囊:与 Git 胶囊同形,但它是只读读数,指针不该是“可点”。\n // 与 codex-hud 对齐的几支色(它的默认配色表:git=magenta、gitBranch=cyan、project=yellow):\n // 产品没有青/品红的语义变量(只有 button-info-*,那是按钮用的),所以这两支写死值,\n // 但都走变量、可被覆盖。\n '.dshh-workspace{color:var(--dsw-alias-state-warn-primary,#d9a63c);flex:none;}',\n '.dshh-gitWrap{color:var(--dsh-hud-magenta,#d946ef);flex:none;}',\n '.dshh-branch{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-ahead{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-workspaceSep{color:var(--dsw-alias-label-tertiary,rgba(138,143,152,0.6));flex:none;}',\n '.dshh-toolsPill{cursor:default;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 配色对齐 codex-hud 的 activity-lines:只有状态符上色,名字/次数/分隔符都用正文色。\n '.dshh-toolsGlyph{flex:none;}',\n '.dshh-toolsGlyphOk{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-toolsGlyphRun{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-toolsGlyphBad{color:var(--dsw-alias-state-error-primary,#f06a6a);}',\n // 唯一一处写死的颜色:codex-hud 把\"正在跑的工具名\"画成青色,产品没有对应的语义变量\n // (只有 button-info-*,那是按钮用的)。留 `--dsh-hud-tool-name` 作为覆盖钩子。\n '.dshh-toolsName{color:var(--dsh-hud-cyan,#22d3ee);}',\n // 技能名与工具名同色系但不同支:工具那支是青,技能走品牌色(一眼能分开)。\n '.dshh-skillName{color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-section{display:flex;flex-direction:column;gap:4px;}',\n '.dshh-list{display:flex;flex-direction:column;gap:1px;max-height:30vh;overflow:auto;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 改动行是按钮(点击看差异),所以要把浏览器的按钮默认样式清掉。\n '.dshh-row{display:flex;align-items:baseline;gap:8px;padding:2px 3px;border-radius:4px;',\n 'font:inherit;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-row:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-row:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n '.dshh-status{flex:none;width:10px;text-align:center;font-family:ui-monospace,Consolas,monospace;font-weight:600;}',\n '.dshh-path{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 和改动清单一样:标题下面一条分隔线(最近提交是最后一段,用它把两段分开)。\n '.dshh-commits{display:flex;flex-direction:column;gap:1px;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 提交行是按钮(点击看这次提交改了什么),因此要清掉浏览器默认的按钮样式。\n '.dshh-commit{display:flex;align-items:baseline;gap:8px;padding:1px 2px;border-radius:4px;font-size:11.5px;',\n 'font:inherit;font-size:11.5px;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-commit:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-commit:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // 打开着的那条提交(明细在右栏,左栏只高亮它)。\n '.dshh-commitActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n // 中间那一栏(提交明细,默认 240px):元信息头 + 这次提交的改动清单。\n // 与左栏同形:各自 border-right 分隔,内部自己滚。\n // 注意 flex 是 `0 1 auto` 而不是 `none`:窗口太窄时由它和差异栏一起被压缩,\n // 而不是把左栏挤出去(左栏是主面板,保持固定 380px)。\n '.dshh-commitpane{flex:0 1 auto;box-sizing:border-box;display:flex;flex-direction:column;',\n 'min-width:0;min-height:0;overflow:hidden;border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitHead{flex:none;display:flex;flex-direction:column;gap:2px;padding:6px 8px;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitTitle{display:flex;align-items:baseline;gap:6px;min-width:0;}',\n '.dshh-commitSubject{font-size:12.5px;line-height:1.5;color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-commitMeta{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 清单吃掉这一栏剩下的高度(标题跟着一起滚,栏本身不滚动)。\n '.dshh-commitFiles{flex:1 1 auto;min-height:0;overflow:auto;padding:6px 8px 8px;}',\n '.dshh-commitFiles .dshh-list{max-height:none;}',\n '.dshh-csha{flex:none;font-family:ui-monospace,Consolas,monospace;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-csubject{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-chead{flex:none;font-size:10px;line-height:1.4;padding:0 4px;border-radius:4px;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);border:1px solid currentColor;}',\n '.dshh-cwhen{flex:none;font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-note{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-empty{padding:14px 12px;font-size:12px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 图片预览(二进制改动) ----\n '.dshh-imgWrap{flex:1 1 auto;min-height:0;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:8px;}',\n '.dshh-imgMeta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgDims{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-imgBytes{font-family:ui-monospace,Consolas,monospace;}',\n '.dshh-imgPair{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap;}',\n '.dshh-imgPairSingle{flex-wrap:nowrap;}',\n // 固定档位(非\"适应宽度\")时:图片按固有像素算宽度,容器随内容撑开,外层横向滚动。\n '.dshh-imgPairZoomed{flex-wrap:nowrap;width:max-content;align-items:flex-start;}',\n '.dshh-imgPairZoomed .dshh-imgCell{flex:0 0 auto;}',\n '.dshh-imgPairZoomed .dshh-img{max-height:none;}',\n '.dshh-imgZoom{display:inline-flex;align-items:center;gap:2px;margin-left:auto;}',\n '.dshh-imgZoomBtn{border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;cursor:pointer;font:inherit;',\n 'min-width:22px;height:20px;padding:0 6px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;}',\n '.dshh-imgZoomBtn:hover{color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n '.dshh-imgZoomValue{min-width:74px;font-variant-numeric:tabular-nums;}',\n '.dshh-imgCell{flex:1 1 150px;min-width:0;display:flex;flex-direction:column;gap:4px;}',\n '.dshh-imgLabel{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgNote{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);padding:6px 0;}',\n // 透明像素要有底色才看得出边界:棋盘格由四段半透明渐变拼出来,两种主题都不用另配颜色。\n '.dshh-img{max-width:100%;max-height:420px;object-fit:contain;border-radius:6px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.28));',\n 'background-image:linear-gradient(45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(-45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(45deg,transparent 75%,rgba(127,127,127,0.12) 75%),',\n 'linear-gradient(-45deg,transparent 75%,rgba(127,127,127,0.12) 75%);',\n 'background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0;}',\n // ---- 轮尾「本轮图片改动」条 ----\n '.dshh-imgstrip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripTitle{flex:none;}',\n '.dshh-imgstripItems{display:flex;gap:6px;flex-wrap:wrap;}',\n '.dshh-imgstripItem{display:inline-flex;align-items:center;gap:2px;padding:2px 4px 2px 2px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:8px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);}',\n '.dshh-imgstripOpen{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;',\n 'color:inherit;cursor:pointer;padding:0;font:inherit;}',\n '.dshh-imgstripOpen:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-imgstripThumb{width:28px;height:28px;object-fit:cover;border-radius:5px;display:block;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));}',\n '.dshh-imgstripThumbEmpty{align-items:center;justify-content:center;font-size:8px;letter-spacing:0.02em;',\n 'display:flex;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripName{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-imgstripTier{flex:none;opacity:.7;}',\n '.dshh-imgstripCompare{border:0;background:transparent;color:inherit;cursor:pointer;padding:0 2px;',\n 'font:inherit;opacity:.7;}',\n '.dshh-imgstripCompare:hover{opacity:1;color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-imgstripMore{flex:none;}',\n '.dshh-footer{display:flex;justify-content:space-between;gap:8px;padding:6px 10px;flex:none;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));',\n 'font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 差异视图 ----\n '.dshh-workbench{display:flex;flex:1 1 auto;min-height:0;align-items:stretch;}',\n '.dshh-leftpane{flex:none;box-sizing:border-box;display:flex;flex-direction:column;min-height:0;overflow:hidden;',\n 'border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-leftpane .dshh-list{max-height:none;}',\n '.dshh-rowActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));}',\n // 键盘选中的那行:选过还没打开时只它一个亮着,所以用描边而不是底色,\n // 与 .dshh-rowActive(正在看的那份差异)区分得开。\n '.dshh-rowCursor{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // hunk 头可点(折叠 / 展开),并显示\"当前 hunk\"——键盘 n/p 与 Space 作用在它身上。\n '.dshh-difrow-hunk{cursor:pointer;user-select:none;}',\n '.dshh-difrow-hunk:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));}',\n '.dshh-difrow-hunkActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-hunkMark{text-align:right;}',\n '.dshh-grip{position:absolute;top:0;right:0;bottom:0;width:6px;cursor:col-resize;z-index:5;}',\n // 栏间那条抓握**必须占宽**:它是提交栏与差异栏之间的真实一格。绝对定位会让它贴到面板右边缘,\n // 与外面那条叠在一起(面板右边缘那条不占宽,靠宽度公式里预留的 6px 让位)。\n '.dshh-gripBetween{position:static;flex:none;width:6px;}',\n '.dshh-grip:hover{background:var(--dsw-alias-border-l2,rgba(127,127,127,0.35));}',\n '.dshh-diffview{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;}',\n '.dshh-toolbar{display:flex;align-items:center;gap:4px;padding:5px 8px;flex:none;overflow:hidden;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-difpath{font-family:ui-monospace,Consolas,monospace;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:38%;}',\n '.dshh-difcounts{display:inline-flex;gap:6px;flex:none;font-family:ui-monospace,Consolas,monospace;font-size:11px;}',\n '.dshh-tab{font:inherit;font-size:11.5px;padding:2px 7px;border-radius:6px;flex:none;cursor:pointer;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tab:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-tabActive{color:var(--dsw-alias-label-primary,#e5e7eb);background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.18));}',\n '.dshh-diff{flex:1 1 auto;min-height:0;overflow:auto;padding:4px 0 8px;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.5;}',\n '.dshh-difrow{display:flex;align-items:flex-start;white-space:pre;}',\n '.dshh-difrow-hunk{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-difrow-meta{color:var(--dsw-alias-label-secondary,#8a8f98);opacity:.85;}',\n '.dshh-difno{flex:none;width:46px;text-align:right;padding-right:8px;user-select:none;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);font-variant-numeric:tabular-nums;}',\n '.dshh-diftext{flex:1 1 auto;min-width:0;white-space:pre;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-diftext.dshh-wrap{white-space:pre-wrap;word-break:break-word;overflow:visible;text-overflow:clip;}',\n].join('')\n","/**\n * 浏览器半的**内核**:常量、存储助手、诊断探针,以及几个到处都用得着的纯函数。\n *\n * 它是最底层 —— store 各片、hook、组件都从这里 import,它自己不 import 任何本包模块,\n * 于是依赖方向永远是\"由外向内\",不会绕成环。\n */\n\nimport React from 'react'\n\nexport var h = React.createElement\n\n/* --------------------------------------------------------------- 领域形状 */\n\n/**\n * 下面这些 interface 是**浏览器半读到的线路形状**(宿主半 `src/host/types.ts` 的镜像)。\n *\n * 为什么集中放在这里:store 各片、hook、组件都从 shared import;类型只写一份,\n * 字段漂移才能一眼看见。宿主半与浏览器半各自打包,不能互相 import 类型,所以\n * 这是刻意的\"同形两份\",改线路形状时两边一起改。\n */\n\n/** 改动类型的八个键(与宿主半 `counts` 的键一致)。 */\nexport type HudCountKey =\n | 'modified'\n | 'added'\n | 'deleted'\n | 'renamed'\n | 'copied'\n | 'typeChanged'\n | 'untracked'\n | 'conflicted'\n\n/** git status 的分组计数(porcelain-v2 的八类)。 */\nexport interface HudStatusCounts {\n modified: number\n added: number\n deleted: number\n renamed: number\n copied: number\n typeChanged: number\n untracked: number\n conflicted: number\n}\n\n/** 改动清单里的一条:porcelain-v2 的两个字母 + 路径。 */\nexport interface HudChange {\n /** 展示用字母:优先 index 侧,index 为 `.` 时取 worktree 侧。 */\n status: string\n /** index 侧字母(未跟踪/未合并的记录是 `.` / `!`)。 */\n index?: string\n /** worktree 侧字母。 */\n worktree?: string\n /** 展示用路径;重命名/复制是 `旧路径 → 新路径`。 */\n path: string\n /** 新路径那一侧(git 命令当 pathspec 用)。 */\n file: string\n /** 重命名/复制的原路径;普通改动没有这个字段。 */\n origFile?: string | null\n}\n\n/** 文件 → “这一版”签名(`size.mtimeMs`,或 `missing`)。 */\nexport type ReviewSignatures = Record<string, string>\n\n/** `status` 端点的结果(工作区概览 + 改动清单)。 */\nexport interface HudStatus {\n /** 仓库根;不在仓库里时为 null。 */\n repo: string | null\n /** 当前分支(detached 时为短 oid);空仓库里也为 null。 */\n branch: string | null\n detached: boolean\n /** 短 oid(7 位);还没有提交时为 null。 */\n oid: string | null\n upstream: string | null\n ahead: number\n behind: number\n dirty: boolean\n counts: HudStatusCounts\n changes: HudChange[]\n truncated: boolean\n /** 「审阅进度」要的“这一版”签名;只在客户端明确要的时候才附上。 */\n review?: ReviewSignatures\n}\n\n/** diff 行的种类:增 / 删 / 上下文 / hunk 头 / git 自己的文件头。 */\nexport type DiffRowKind = 'add' | 'del' | 'ctx' | 'hunk' | 'meta'\n\n/** 差异里的一行。 */\nexport interface DiffRow {\n kind: DiffRowKind\n text: string\n /** 旧侧行号;新增行与 hunk/meta 为 null。 */\n oldLine: number | null\n /** 新侧行号;删除行与 hunk/meta 为 null。 */\n newLine: number | null\n}\n\n/** 一侧 diff 的解析结果。 */\nexport interface DiffSide {\n rows: DiffRow[]\n additions: number\n deletions: number\n hunks: number\n binary: boolean\n truncated: boolean\n}\n\n/** 工作区差异端点载荷:一个改动文件的两侧(工作区 / 已暂存)。 */\nexport interface WorktreeDiffPayload {\n repo: string | null\n path: string\n origPath: string | null\n /** 工作区里的未跟踪文件(`--no-index` 那条分支)。 */\n untracked: boolean\n worktree: DiffSide\n index: DiffSide\n}\n\n/** 右栏差异的 `data`:工作区差异是两侧,提交差异是单侧。 */\nexport type DiffPayload = WorktreeDiffPayload | DiffSide\n\n/** 差异的两种来源。 */\nexport type DiffSource = 'worktree' | 'commit'\n\n/** 差异的三种“侧”。 */\nexport type DiffSideName = 'worktree' | 'index' | 'commit'\n\n/** 右侧差异视图的状态(`state.diff`)。 */\nexport interface HudDiffState {\n source: DiffSource\n phase: string\n sha?: string\n shortSha?: string\n subject?: string\n path: string\n file: string\n origPath: string | null\n data: DiffPayload | null\n side: DiffSideName\n wrap?: boolean\n zoom?: number | null\n hunk?: number\n collapsed?: number[]\n errorCode?: string | null\n}\n\n/** 一侧图片可以预览:内容以 base64 附带,尺寸认出来才给。 */\nexport interface ImagePreview {\n kind: 'image'\n mime: string\n bytes: number\n width: number | null\n height: number | null\n base64: string\n}\n\n/** 一侧图片超过单侧上限:只报字节数,不塞内容。 */\nexport interface ImageTooLarge {\n kind: 'too-large'\n bytes: number\n limit: number\n}\n\n/** 一侧内容认不出是图片。 */\nexport interface ImageNotImage {\n kind: 'not-image'\n bytes: number\n}\n\n/** 一侧不存在(新文件还没有索引副本 / 文件被删掉)。 */\nexport interface ImageMissing {\n kind: 'missing'\n}\n\n/** 一侧内容的可预览状态。 */\nexport type ImageSide = ImagePreview | ImageTooLarge | ImageNotImage | ImageMissing\n\n/** `image` 端点载荷:一个二进制(图片)改动的两侧字节与像素尺寸。 */\nexport interface ImagePayload {\n repo: string | null\n /** 提交明细时才有(工作区/索引视图没有)。 */\n shortSha?: string\n path: string\n origPath: string | null\n side: string\n sha: string | null\n before: ImageSide\n after: ImageSide\n}\n\n/** `image` 请求:缓存键按这几个字段算。 */\nexport interface ImageRequest {\n cwd: string\n path: string\n origPath?: string | null\n side?: string | null\n sha?: string | null\n}\n\n/** 「本会话确定改过」的一个文件。 */\nexport interface SessionChange {\n path: string\n file: string\n writes: number\n edits: number\n firstAt: number\n lastAt: number\n lastTurn: number | null\n turns: number[]\n}\n\n/** 「可能相关」的一个路径(命令旁听来的启发式)。 */\nexport interface SessionChangeCandidate {\n path: string\n file: string\n hits: number\n firstAt: number\n lastAt: number\n reason: string\n lastTurn: number | null\n}\n\n/** 本会话跑过的一条 shell 命令。 */\nexport interface SessionCommand {\n tool: string\n command: string\n at: number\n turn: number | null\n failed: boolean\n readOnly: boolean\n}\n\n/** 「会话归因」这一片 store 的状态(`state.sessionChanges`)。 */\nexport interface SessionChangesState {\n phase: string\n /** `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)。 */\n files: Record<string, SessionChange> | null\n possible: Record<string, SessionChangeCandidate> | null\n commands: SessionCommand[]\n since: number | null\n dropped: number\n possibleDropped: number\n errorCode: string | null\n}\n\n/** 「第几轮 → 那一轮的请求预览」(`state.turns`,宿主 `turnOutline` 的归一结果)。 */\nexport interface TurnsState {\n available: boolean\n prompts: Record<number, string>\n latest: number | null\n}\n\n/** 归因与 git 现状核对后的视图(`sessionChangeView()` 的结果)。 */\nexport interface SessionChangeView {\n /** 本会话改过、且工作区里还有差异。 */\n mine: Record<string, SessionChange>\n /** 命令旁听出来的「可能相关」(只标没被确定认领的)。 */\n possible: Record<string, SessionChangeCandidate>\n turnPrompts: Record<number, string>\n latestTurn: number | null\n latestCount: number\n /** 第几轮 → 那一轮改了几个文件。 */\n byTurn: Record<number, number>\n mineCount: number\n possibleCount: number\n commandCount: number\n writingCommands: number\n commands: SessionCommand[]\n /** 本会话改过、但工作区里已经没有差异的文件。 */\n gone: string[]\n /** 本会话记录过的文件总数。 */\n tracked: number\n since: number | null\n dropped: number\n possibleDropped: number\n /** 工作区里有差异、但不是本会话动的条数。 */\n others: number\n}\n\n/** 审阅标记:这一版审过了(fresh)/ 之后又变了(stale)。 */\nexport type ReviewMark = 'fresh' | 'stale'\n\n/** 「审阅进度」视图(`reviewView()` 的结果)。 */\nexport interface ReviewView {\n marks: Record<string, ReviewMark>\n known: Record<string, true>\n done: number\n total: number\n staleCount: number\n}\n\n/** 存档里一个\"已审\"标记。 */\nexport interface ReviewedEntry {\n signature: string\n at: number\n}\n\n/** 存档:`工作区 + NUL + 文件 → { signature, at }`。 */\nexport type ReviewedMap = Record<string, ReviewedEntry>\n\n/** 面板位置。 */\nexport interface PanelPos {\n left: number\n top: number\n}\n\n/** 右栏里打开着哪几栏(`panelLayout()` 的结果)。 */\nexport interface PanelLayout {\n diff: boolean\n commit: boolean\n}\n\n/** 本地 UI 记忆(`dsh-hud.ui.v1`):只记界面开关与布局,不混业务数据。 */\nexport interface UiPrefs {\n onlySession?: boolean\n onlyLatest?: boolean\n open?: boolean\n pos?: PanelPos | null\n wrap?: boolean\n zoom?: number | null\n diffWidth?: number\n commitWidth?: number\n}\n\n/**\n * 悬停气泡里的一行。\n *\n * 三种写法:\n * - `{ text, tone }` —— 一整行一种颜色(如「更新于 …」用次要色);\n * - `{ label, value }` —— 左边标签次要色、右边数字主色(余额那几行);\n * - `{ text }` —— 默认主色。\n *\n * 之所以要结构而不是一段纯文本:用户要求「数字亮、标签灰」,\n * 而一整段字符串在 DOM 里只有一个文本节点,一种颜色,做不到。\n */\nexport interface TipSegment {\n text: string\n /** 只给\"读数\"上色;不给就是普通文字色。 */\n tone?: TipLine['tone']\n}\n\nexport interface TipLine {\n text?: string\n label?: string\n value?: string\n /**\n * 行内分段(比 `label + value` 更细):一句白话里只把数字挑出来高亮。\n *\n * 为什么需要它:会话计时、工具统计这些气泡是**一整句话**\n *(「…其中模型 3h 24m、工具 50m · 62 轮 / 1469 步」),\n * 排版不能动,但其中的数字要突出。用分段就能只染色、不改句式和换行。\n */\n segments?: TipSegment[]\n /**\n * 这一行里\"被强调的部分\"用什么颜色。\n *\n * - `primary`(默认):主文字色,深浅主题自动反色;\n * - `muted`:次要信息(抬头、更新时间、变化量);\n * - `success`:**还剩多少**(余额)—— 绿色是\"可用额度\"的通行读法;\n * - `warn`:**已经花掉多少**(两个已用)—— 琥珀只是\"这是消耗\"的分类色,\n * 不是在报警;\n * - `accent`:中性数字(API 请求次数)—— 用产品自己的品牌蓝,深浅主题各有一套取值。\n *\n * 为什么不全用同一种彩色:四个数里三个是钱、一个是次数,颜色分开才一眼分得清\n * 哪行是\"还剩\"、哪行是\"花掉\"、哪行根本不是金额。\n */\n tone?: 'primary' | 'muted' | 'success' | 'warn' | 'accent'\n}\n\n/** 气泡内容:单段文本(老写法)或逐行结构。 */\nexport type TipContent = string | TipLine[]\n\n/**\n * 一行在\"拼平\"(日志/断言/兼容 `state.text`)时长的样子。\n *\n * 标签与数值之间补一个空格:屏幕上它们之间是 CSS 的 `margin-left`,拼平之后没这个\n * 间距,`余额$742.23` 读起来是错的,日志里也不好 grep。\n */\nexport function tipLineText(line: TipLine): string {\n if (Array.isArray(line.segments)) {\n return line.segments\n .map(function (segment) {\n return segment.text\n })\n .join('')\n }\n if (typeof line.text === 'string') return line.text\n var label = line.label ?? ''\n var value = line.value ?? ''\n if (label !== '' && value !== '') return label + ' ' + value\n return label + value\n}\n\n/**\n * 把一句白话按给定的\"读数\"切成段:命中的部分高亮,其余保持普通文字。\n *\n * 做法是**字面量匹配**而不是猜数字:调用方知道这句话里哪些是读数\n *(`3h 24m`、`1469`、`$742.23`),交给我们按出现顺序切段。\n * 不去用 `\\d+` 自动扫 —— 那样会把「2026-10-20 12:41」这类时间戳也染上,\n * 满屏都是颜色,等于没有重点。\n *\n * @param text - 整句白话。\n * @param readings - 要突出的字面量(按长度从长到短匹配,避免短串先吃长串)。\n * @param tone - 用什么颜色。\n * @returns 分段数组(没有命中就退化成一段)。\n */\nexport function emphasize(\n text: string,\n readings: (string | null | undefined)[],\n tone: TipLine['tone'] = 'accent',\n): TipSegment[] {\n var hits = readings\n .filter(function (item) {\n return typeof item === 'string' && item !== ''\n })\n .map(function (item) {\n return item as string\n })\n .sort(function (left, right) {\n return right.length - left.length\n })\n if (hits.length === 0 || text === '') return [{ text: text }]\n var segments: TipSegment[] = []\n var plain = ''\n var index = 0\n while (index < text.length) {\n var matched: string | null = null\n for (var at = 0; at < hits.length; at += 1) {\n if (text.startsWith(hits[at], index)) {\n matched = hits[at]\n break\n }\n }\n if (matched === null) {\n plain += text[index]\n index += 1\n continue\n }\n if (plain !== '') {\n segments.push({ text: plain })\n plain = ''\n }\n segments.push({ text: matched, tone: tone })\n index += matched.length\n }\n if (plain !== '') segments.push({ text: plain })\n return segments.length === 0 ? [{ text: text }] : segments\n}\n\n/**\n * 把气泡内容归一成「逐行结构 + 拼平文本」。\n *\n * 两处都用它:真 store 的 tip 片(渲染用 `lines`,`text` 留给日志与断言),\n * 以及测试 harness 的假 `showTip`(让老用例继续按字符串断言,不必全部重写)。\n */\nexport function normalizeTipContent(content: TipContent): { lines: TipLine[]; text: string } {\n var lines: TipLine[] =\n typeof content === 'string'\n ? [{ text: content }]\n : Array.isArray(content)\n ? content.filter(function (line) {\n return line !== null && typeof line === 'object'\n })\n : []\n return {\n lines: lines,\n text: lines\n .map(function (line) {\n return tipLineText(line)\n })\n .join('\\n'),\n }\n}\n\n/** 悬停气泡快照(`state.tip`)。 */\nexport interface TipState {\n key: string\n text: string\n /** 逐行结构(渲染用);`text` 是它的拼平版本。 */\n lines: TipLine[]\n rect: { left: number; top: number; right: number }\n}\n\n/** 最近提交里的一行(`log` 端点载荷里的一条)。 */\nexport interface HudLogEntry {\n sha: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n subject: string\n}\n\n/** 提交元信息(`commit` 端点载荷里的 `commit`)。 */\nexport interface HudCommitSummary {\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n}\n\n/** 提交明细里的一个改动文件(`git diff --name-status -z` 的一条)。 */\nexport interface HudCommitFile {\n status: string\n path: string\n file: string\n origFile?: string | null\n}\n\n/** 提交明细(右栏视图)的状态(`state.commit`)。 */\nexport interface HudCommitState {\n phase: string\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n files: HudCommitFile[]\n truncated: boolean\n errorCode: string | null\n}\n\n/** 标签列表里的一条(`tags` 端点里的一条)。 */\nexport interface HudTag {\n name: string\n sha: string\n annotated: boolean\n date: string\n committedAt: number | null\n subject: string\n}\n\n/** 正在跑的工具(胶囊显示实时秒数用:秒数由客户端本地计时)。 */\nexport interface HudToolActive {\n callId: string | null\n name: string\n summary: string | null\n startedAt: number\n}\n\n/** 最近一次工具调用。 */\nexport interface HudToolRecent {\n name: string\n summary: string | null\n ok: boolean\n at: number\n ms: number | null\n}\n\n/** 某个工具跑了多少次、最近一次成功没有。 */\nexport interface HudToolCount {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 某个技能用了几次、最近一次成功没有。 */\nexport interface HudToolSkill {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 「工具活动」这一片 store 的状态(`state.tools`)。 */\nexport interface ToolsState {\n phase: string\n active: HudToolActive | null\n recent: HudToolRecent[]\n counts: HudToolCount[]\n skills: HudToolSkill[]\n skillsDropped: number\n total: number\n updatedAt: number | null\n}\n\n/** `session-time` 端点载荷:创建时刻 + 累计墙钟与轮次/步数。 */\nexport interface HudSessionTime {\n available: boolean\n sessionId?: string | null\n createdAt?: number | null\n turns?: number | null\n steps?: number | null\n llmMs?: number | null\n toolMs?: number | null\n now?: number\n}\n\n/** 会话时长快照(`state.sessionTime`):`sessionId` 用来判断\"这份数还是不是这个会话的\"。 */\nexport interface SessionTimeState {\n sessionId: string | null\n value: HudSessionTime | null\n}\n\n/** `prompt-history` 端点载荷。 */\nexport interface HudPromptHistory {\n available: boolean\n sessionId?: string | null\n prompts: string[]\n}\n\n/** prompt 历史快照(`state.promptHistory`)。 */\nexport interface PromptHistoryState {\n sessionId: string | null\n value: HudPromptHistory | null\n}\n\n/** 一个钱包的余额(充值钱包与赠送钱包同形)。`balance` 是平台给的十进制字符串,保精度不转数字。 */\nexport interface HudBalanceWallet {\n currency: string\n balance: string\n}\n\n/**\n * 账号余额快照(`state.balance`)。\n *\n * 这是**账号级**读数,与会话无关:换会话、换工作区都不该让它消失。\n * 值来自 DSH 自己的 `remote.account.getBalance`,本插件只读、不落盘\n * (余额是会变的账目数字,留一个过期值比没有更糟)。\n */\nexport interface HudBalance {\n /** 充值钱包;一个可展示的钱包都没有时整份快照为 `null`,胶囊不画。 */\n wallets: HudBalanceWallet[]\n /** 赠送钱包(已过滤掉 0 余额的)。 */\n bonus: HudBalanceWallet[]\n /** 这份数字是什么时候取到的(本地时刻)。 */\n at: number\n /** 最近一次读取失败:`at` 仍停在上一次成功的时刻,界面据此标\"可能过期\"。 */\n failed: boolean\n /** 平台用量页(累计消耗 / 本月消耗这些账目只在那里;右键胶囊会打开它)。 */\n usageUrl?: string | null\n /** 平台充值页。 */\n topUpUrl?: string | null\n /**\n * 上一次成功读到的钱包与时刻,用来算\"较上次刷新变化了多少\"。\n *\n * 只在内存里:跨重启比较没有意义(中间发生过什么不知道),所以不落盘。\n * 失败的那一轮不动它 —— 它永远指向**最近一次成功**的读数。\n */\n prev?: { wallets: HudBalanceWallet[]; at: number } | null\n}\n\n/** 侧栏每条会话行要的摘要(`session-statuses` 里的一行)。 */\nexport interface HudSessionOverviewRow {\n sessionId: string\n cwd: string | null\n /** 确定改过的文件数(归因记录)。 */\n mine: number\n /** “可能相关”的历史候选路径数。 */\n possible: number\n /** “可能相关”里此刻仍然有差异的个数。 */\n possibleNow: number\n repo: boolean\n error: string | null\n /** 该工作区的 git 摘要还没算出来。 */\n pending?: boolean\n branch?: string | null\n detached?: boolean\n dirty?: boolean\n ahead?: number\n behind?: number\n counts?: HudStatusCounts\n}\n\n/** 「侧栏摘要」这一片 store 的状态(`state.rows`)。 */\nexport interface RowsState {\n phase: string\n byId: Record<string, HudSessionOverviewRow>\n unavailable: boolean\n truncated: boolean\n updatedAt: number | null\n}\n\n/** 紧凑计数简写(胶囊与侧栏 chip 共用)。 */\nexport interface CountShorthand {\n key: HudCountKey\n mark: string\n value: number\n}\n\n/** 侧栏一枚状态 chip 的\"事实摘要\"(`rowSummaryOf()` 的结果)。 */\nexport interface RowSummary {\n row: HudSessionOverviewRow\n mine: number\n possibleNow: number\n possible: number\n shorthands: CountShorthand[]\n changed: number\n dirty: boolean\n hasContent: boolean\n}\n\n/** 浏览器半共享 store 的快照。 */\nexport interface HudState {\n phase: string\n sessionId: string | null\n cwd: string | null\n status: HudStatus | null\n errorCode: string | null\n updatedAt: number | null\n panelOpen: boolean\n pos: PanelPos | null\n /** 胶囊所在 dock 行的视口顶部坐标。 */\n anchorTop: number | null\n diff: HudDiffState | null\n commit: HudCommitState | null\n /** 键盘高亮的那个文件(清单里的 `file`)。 */\n cursor: string | null\n commits: HudLogEntry[] | null\n commitsOid: string | null\n tags: HudTag[] | null\n tagsAt: number\n tagsTruncated: boolean\n diffWidth: number\n commitWidth: number\n sessionChanges: SessionChangesState\n onlySession: boolean\n onlyLatest: boolean\n /** 这一版每个改动文件的签名(`file → signature`)。 */\n review: ReviewSignatures\n tools: ToolsState\n sessionTime: SessionTimeState\n promptHistory: PromptHistoryState\n /** 账号余额(账号级;还没读到、或这个账号没有可展示的钱包时是 `null`)。 */\n balance: HudBalance | null\n /** 当前会话模型来源的账本(见 `HudProviderLedger`)。 */\n provider: HudProviderLedger\n /** 后台凭据设置面板(点胶囊时按需打开)。 */\n tokenPanel: TokenPanelState\n tip: TipState | null\n rows: RowsState\n reviewed: ReviewedMap\n turns: TurnsState\n}\n\n/**\n * 共享 store 的对外接口(hook / 组件只依赖它,不依赖 `createStore` 的实现)。\n *\n * 只列浏览器半真正用到的那些;方法参数用线路形状(`HudChange` / `ImageRequest` …),\n * 这样 hook 里传参也一起被检查到。\n */\nexport interface HudStore {\n getSnapshot(): HudState\n subscribe(listener: () => void): () => void\n bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void\n refresh(): Promise<unknown>\n openPanel(open: boolean): void\n togglePanel(): void\n movePanel(pos: PanelPos): void\n commitPanel(pos: PanelPos): void\n resetPanel(): void\n setAnchorTop(top: number | null): void\n setDiffWidth(width: number): void\n setCommitWidth(width: number): void\n persistLayout(): void\n openDiff(change: HudChange): Promise<unknown>\n loadImagePair(request: ImageRequest): Promise<unknown>\n cachedImagePair(request: ImageRequest): unknown\n loadSessionChanges(): Promise<unknown>\n loadSessionTime(sessionId: string): Promise<unknown>\n loadPromptHistory(sessionId: string): Promise<unknown>\n /** 取账号余额(`force` 跳过 TTL;胶囊点击就用它)。拿不到账号能力时安静地回 `null`。 */\n loadBalance(force?: boolean): Promise<HudBalance | null>\n /** 取\"当前会话模型来源\"的余额(中转站/官方 Key 那一侧;`force` 跳过 TTL)。 */\n /** 换会话时把账本清成 unknown(别让新会话顶着上一个会话的读数)。 */\n resetForSession(): void\n loadProviderBalance(force?: boolean): Promise<HudProviderLedger | null>\n openTokenPanel(provider: string, ref: string | null): void\n closeTokenPanel(): void\n saveProviderToken(token: string): Promise<boolean>\n showTip(\n key: string,\n text: string,\n rect: { left: number; top: number; right: number } | null,\n ): void\n hideTip(key?: string): void\n tipHandlers(key: string, contentOf: () => TipContent): Record<string, unknown>\n closeDiff(): void\n closeDetail(): void\n setDiffSide(side: string): void\n toggleDiffWrap(): void\n setZoom(value: number | null): void\n openCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown>\n openCommitDiff(file: HudCommitFile): Promise<unknown>\n toggleCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown> | void\n closeCommit(): void\n stepFile(delta: number, open: boolean): HudChange | null\n openCursor(): HudChange | null\n stepHunk(delta: number): void\n toggleHunk(index: number | undefined): void\n toggleOnlySession(): void\n toggleOnlyLatest(): void\n refreshRows(): Promise<unknown>\n loadTools(): Promise<unknown>\n toggleReviewed(file: string): void\n clearReviewed(): void\n}\n\n/** 取词函数(框架把 `t` 交给座位;取不到会回落成键本身)。 */\nexport type Translate = (key: string, params?: Record<string, string>) => string\n\n/** 诊断探针(`createProbe()` 的返回)。 */\nexport interface Probe {\n log(reason: string, detail?: Record<string, unknown>): void\n}\n\n/* --------------------------------------------------------------- 常量 */\n\nexport var CHANNEL = '/dsh-hud-rpc'\nexport var NS = 'dshHud'\n\nexport var UI_KEY = 'dsh-hud.ui.v1'\n/**\n * 插件自诊断的留存键(最近 24 行):会话归因每一步的事实都往这里留一行。\n *\n * 为什么落在这里:客户端插件的异常与\"服务找不到\"都不会进宿主日志,界面出问题时往往只有\n * 有/没有两种观察;把每一步的事实留一行在 localStorage,用户不用开 DevTools 也能把它捞出来,\n * 排查完可以随手删掉。\n */\nexport var DEBUG_KEY = 'dsh-hud.debug'\n\n/** 轮询间隔:面板与胶囊共用,页面不可见时跳过。 */\nexport var POLL_MS = 8000\n/**\n * 侧栏行内 chip 的轮询节奏:比面板慢一档。\n *\n * 那些 chip 是\"扫一眼\"的信息(哪个会话脏了),不值得跟面板一样频繁;宿主侧还按目录\n * 缓存 + 去重,多会话共用一个目录只跑一次 git。\n */\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\nexport var ROWS_POLL_MS = 30000\n/**\n * 工具活动的节拍。比面板那 8s 密得多:宿主这一端是**内存读**(不碰 git、不跑子进程),\n * 密一点才能让\"正在跑什么\"真的看到;代价用\"内容没变就不 set\"抵掉。\n */\nexport var TOOLS_POLL_MS = 3000\n/** 胶囊上最多铺几个技能名(其余收成 `+k`,完整清单在悬停里)。 */\nexport var SKILLS_SHOWN = 2\n/**\n * 账号余额的轮询节拍:**比别的读数都慢**。\n *\n * 这一枚走的不是本地 git,而是宿主替我们去平台查账(一次真实网络请求),\n * 而且余额是按分钟变的,密了纯属浪费。页面隐藏时照例跳过,点击胶囊可强制刷新。\n */\nexport var BALANCE_POLL_MS = 300000\n/** 同一次请求的 TTL:轮询与点击之间不重复打平台(失败时用更短的间隔重试,见切片)。 */\nexport var BALANCE_TTL_MS = 240000\n/** 失败后的重问间隔:账目查不到多半是网络/登录态,隔一小会儿再试就够。 */\nexport var BALANCE_RETRY_MS = 30000\n/* ------------------------------------------------- 提供方余额(按模型来源) */\n\n/**\n * 当前会话**当前模型所属提供方**的余额。\n *\n * 与 `HudBalance` 的区别不只是字段:`HudBalance` 是 DeepSeek 平台账号那一个账本,\n * 这个是\"这次会话真正在用的那个提供方\"的账本 —— 可能是官方 API Key,也可能是用户自己\n * 填的第三方中转站。两个账本的钱不能混着显示(那正是\"胶囊数字看着像 bug\"的来源)。\n */\nexport interface HudProviderBalance {\n /** 提供方 id(`ur` / `muyuan` / `deepseek-official` …)。 */\n provider: string\n kind: 'official' | 'relay'\n /** 站点自己的记账币种;不做汇率换算。 */\n currency: string | null\n /** 剩余额度;站点把额度写成\"无限\"占位值时是 `null`(算不出来,别编)。 */\n remaining: string | null\n /**\n * **整个账号**的累计已用(站点 `/api/user/self` 的 `used_quota`)。\n *\n * 与 `usedKey` 并存而不是二选一:站点上两个数能同时拿到、也能差出一截\n * (实测 ur:账号 $526.66 / 本 Key $479.44)。\n */\n usedAccount: string | null\n /** **这枚 API Key** 的累计已用(站点 `/dashboard/billing/usage` 的 `total_usage`)。 */\n usedKey: string | null\n /** 账号累计请求数(只有账号那一路给得出)。 */\n requests?: number | null\n /** 站点把额度写成占位值 → 剩余不可信,只能展示已用。 */\n placeholderLimit: boolean\n wallets: HudBalanceWallet[]\n bonus: HudBalanceWallet[]\n /** 取数时刻(本地毫秒)。 */\n at: number\n}\n\n/**\n * 这一枚胶囊该看哪个账本。\n *\n * - `account`:会话用的就是 DeepSeek 平台账号 → 走原来的 `HudBalance` 那一路;\n * - `provider`:会话用的是别的提供方,且读到了它的余额;\n * - `unavailable`:知道是哪个提供方,但读不到(不画胶囊,与\"读不到就不画\"同一条规矩);\n * - `unknown`:会话/投影还没就绪(首帧常态),先按账号那一路画,别让胶囊闪没。\n */\n/** 「后台凭据设置面板」的状态:开没开、给哪个提供方、存到了哪一步。 */\nexport interface TokenPanelState {\n open: boolean\n /** 面板正在为哪个提供方配令牌。 */\n provider: string | null\n /** 那个提供方的凭据引用名(宿主给的;面板只拿它显示\"存到哪了\")。 */\n ref: string | null\n /** 这个提供方到底能不能配(宿主不认识它时面板不弹)。 */\n supported: boolean\n saving: boolean\n /** 上一次保存失败的原因键(`balance.tokenError.*`)。 */\n error: string | null\n /** 保存成功的时刻(好让面板自己收起)。 */\n}\n\n/**\n * 「这个提供方的后台凭据配了没」。\n *\n * 只用来决定点胶囊干什么:没配 → 弹设置面板;配了 → 刷新一次读数。\n * 令牌本身永远不出宿主,这里也就没有它的位置。\n */\nexport interface HudProviderToken {\n ref: string\n configured: boolean\n}\n\nexport interface HudProviderLedger {\n mode: 'account' | 'provider' | 'unavailable' | 'unknown'\n balance: HudProviderBalance | null\n /**\n * 知道是哪个提供方(但可能读不到它的余额)时的 id。\n *\n * 为什么要留着:`unavailable` 时胶囊要画 `ur —`,而不是干脆消失 ——\n * 消失会让人分不清\"这个站没有余额接口\"和\"插件坏了\"(这正是用户报过的那一幕)。\n */\n provider?: string | null\n /** 读不到的原因(`unsupported` / `no-key` / `network` …);只用于悬停里那句话。 */\n reason?: string | null\n /** 这个提供方的后台凭据状态(决定点胶囊是\"弹面板\"还是\"刷新\")。 */\n token?: HudProviderToken | null\n}\n\n/**\n * 低余额提醒的阈值(默认 10)。\n *\n * 为什么是常量而不是设置项:DSH 的客户端插件条目拿不到 Cordis config\n * (`client-modules` 建条目时只传 `{ name: id }`),这里也就没有可读的配置面;\n * 而为了一个数字去加一整页设置并不划算。需要改的人用 `BALANCE_LOW_KEY` 那个\n * localStorage 键覆盖(README 里写了)。\n */\nexport var BALANCE_LOW_DEFAULT = 10\n/** 「当前会话模型来源」余额的轮询节拍:真实请求由 store 的 TTL 兜着,这里只决定问的频率。 */\nexport var PROVIDER_POLL_MS = 30000\n/**\n * 「还没问出来」时的重问间隔。\n *\n * 重启/刚开会话的那一下,宿主往往还没读到模型选择投影 —— 第一次问会落空。\n * 用 30 秒节拍兜的话胶囊要空半分钟(用户报的就是这个);1 秒问一次,\n * 正常情况一两秒内就补上了。真的出错时也不会打风暴:store 的失败退避是\n * 1s→2s→4s→…→30s,这里的轮询只是\"看一眼\",多半被 TTL 直接挡回。\n */\nexport var PROVIDER_RETRY_TICK_MS = 1000\n\n/** 覆盖低余额阈值的 localStorage 键(值是十进制数,比如 `50`)。 */\nexport var BALANCE_LOW_KEY = 'dsh-hud.balanceLow'\n/** 面板默认宽度与“停靠在输入框上方”的锚点高度。 */\nexport var PANEL_WIDTH = 380\nexport var COMPOSER_OFFSET = 168\n\nexport var VIEWPORT_MARGIN = 8\n\n/** 超过这个位移才算拖动,避免把头部上的普通点击当成拖动。 */\nexport var DRAG_THRESHOLD_PX = 3\n/** 面板与胶囊所在 dock 行之间的间距。 */\nexport var PANEL_DOCK_GAP = 8\n/**\n * 中间那一栏(打开提交明细时:这次提交的元信息 + 改动清单)的宽度。\n * 它夹在左栏与差异栏之间,是固定的——右边缘那个抓握仍只调整差异栏。\n */\nexport var COMMIT_LIST_WIDTH = 240\n/** 提交明细栏的最小宽度(它装的是文件路径,不能太窄;默认 240 只是起点,可拖)。 */\nexport var COMMIT_MIN_WIDTH = 180\n\n/** diff 模式的面板:更宽,且用固定高度(避免 bottom 锚定下\"内容变高就跳位\")。 */\n/** 右栏(差异)默认宽度、下限,以及面板右边缘抓握的宽度。 */\nexport var DIFF_DEFAULT_WIDTH = 470\nexport var DIFF_MIN_WIDTH = 240\n\nexport var RESIZE_GRIP_WIDTH = 6\n\nexport var DIFF_PANEL_HEIGHT_VH = 0.72\n\n/** 改动类型的固定展示顺序,与宿主半的 counts 键一致。 */\nexport var COUNT_ORDER: HudCountKey[] = [\n 'modified',\n 'added',\n 'deleted',\n 'renamed',\n 'copied',\n 'typeChanged',\n 'untracked',\n 'conflicted',\n]\n\n/** 每类改动对应的主题色令牌(都带回退色,暗亮色主题都能看)。 */\nexport var COUNT_COLOR: Record<HudCountKey, string> = {\n modified: 'var(--dsw-alias-state-warn-primary, #d9a63c)',\n added: 'var(--dsw-alias-state-success-primary, #3fb950)',\n deleted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n // 与 codex-hud 的 `statEntries` 一一对应:R→cyan、C→brightBlue(我们的品牌蓝就是这个位)、\n // T→magenta、?→灰。M/A/D/! 本来就一致(yellow/green/red/critical)。\n // `?` 用 `label-secondary` 而不是 `label-dimmed`:后者是**反相的表面色**(浅 #e1e5ee、深 #43454a),\n // 当文字压在面板底色上只有 1.26:1 / 1.64:1 —— 看不见(issue:未跟踪计数读不出来)。\n // secondary 仍是中性灰、比彩色状态弱一档,但浅 5.8:1 / 深 10.4:1 都读得清。\n renamed: 'var(--dsh-hud-cyan, #22d3ee)',\n copied: 'var(--dsw-alias-brand-primary, #4b8bf5)',\n typeChanged: 'var(--dsh-hud-magenta, #d946ef)',\n untracked: 'var(--dsw-alias-label-secondary, #8a8f98)',\n conflicted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n}\n\n/**\n * 每个改动类型在胶囊里的简写符,沿用 codex-hud / 上游 statusline 的 git 标记约定:\n * M 已修改、A 已暂存新增、D 已删除、R 重命名、C 复制、T 类型改变、? 未跟踪、! 冲突。\n */\nexport var COUNT_MARK: Record<HudCountKey, string> = {\n modified: 'M',\n added: 'A',\n deleted: 'D',\n renamed: 'R',\n copied: 'C',\n typeChanged: 'T',\n untracked: '?',\n conflicted: '!',\n}\n\n/** 胶囊里分支名的显示上限;超出就中段省略,别把简写符挤出可见区。 */\nexport var PILL_BRANCH_MAX = 28\n/** 胶囊里工作区名最多几个字符(它在分支前面,不该抢分支的位置)。 */\nexport var PILL_WORKSPACE_MAX = 20\n\n/**\n * 差异行的底色:半透明,压在面板表面色之上,因此亮/暗主题都可读\n * (文本与行号仍走主题令牌)。\n */\nexport var DIFF_ADD_BG = 'rgba(63,185,80,0.12)'\nexport var DIFF_DEL_BG = 'rgba(240,106,106,0.13)'\n\nexport var DIFF_GUTTER_COLOR = 'var(--dsw-alias-label-secondary, #8a8f98)'\n\n/** prompt 历史的条数上限(与宿主半的 `PROMPT_HISTORY_MAX` 同一个数)。 */\nexport var PROMPT_HISTORY_MAX = 100\n\n/**\n * 时长格式化:**与 codex-hud 同一套口径**。\n *\n * 不足一分钟给 `<1m`;然后按 `Xd Yh Zm` 只列非零项(`45m` / `2h 14m` / `3d 2h`)。\n * 取的是 floor —— 计时器宁可比真实少一点,也不要\"明明刚过 1 分钟就写 2m\"。\n */\nexport function formatDuration(ms: unknown) {\n var safe = typeof ms === 'number' && isFinite(ms) ? Math.max(0, ms) : 0\n if (safe < 60000) return '<1m'\n var minutes = Math.floor(safe / 60000)\n var days = Math.floor(minutes / 1440)\n var hours = Math.floor((minutes % 1440) / 60)\n var rest = minutes % 60\n var parts = []\n if (days > 0) parts.push(String(days) + 'd')\n if (hours > 0) parts.push(String(hours) + 'h')\n if (rest > 0) parts.push(String(rest) + 'm')\n return parts.join(' ')\n}\n\n/** 固定缩放档位:与内置文档预览的菜单一致(25% / 50% / 100% / 150% / 200%)。 */\nexport var ZOOM_STEPS = [0.25, 0.5, 1, 1.5, 2]\n\n/** 落盘/读回的缩放值:只认档位里的数,其他一律当\"适应宽度\"。 */\nexport function readZoom(ui: UiPrefs | null | undefined): number | null {\n return ui && typeof ui.zoom === 'number' && ZOOM_STEPS.indexOf(ui.zoom) >= 0 ? ui.zoom : null\n}\n\n/**\n * 走一档缩放。\n *\n * `null`(适应宽度)出发:`+` 从 100% 开始、`−` 从 50% 开始 —— 和\"适应宽度\"的直觉一致,\n * 不用先经过 25% 才知道按了有反应。\n */\nexport function stepZoom(current: number | null | undefined, direction: number): number {\n if (current === null || current === undefined) return direction > 0 ? 1 : 0.5\n var index = ZOOM_STEPS.indexOf(current)\n if (index < 0) return direction > 0 ? 1 : 0.5\n var next = index + (direction > 0 ? 1 : -1)\n if (next < 0 || next >= ZOOM_STEPS.length) return current\n return ZOOM_STEPS[next]\n}\n\nexport function readUi(): UiPrefs {\n try {\n var raw = window.localStorage.getItem(UI_KEY)\n if (!raw) return {}\n // localStorage 是不可信输入(旧版本写的、手改过的都有可能):先当 unknown,收窄后再当 UiPrefs。\n var parsed: unknown = JSON.parse(raw)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as UiPrefs) : {}\n } catch (error) {\n return {}\n }\n}\n\nexport function writeUi(next: UiPrefs): void {\n try {\n window.localStorage.setItem(UI_KEY, JSON.stringify(next))\n } catch (error) {\n // 隐私模式等写入失败只影响“记住位置/展开状态”,不影响功能。\n }\n}\n\n/** 「审阅进度」的存档键:按\"工作区 + 文件\"记下审过的那一版签名。 */\nexport var REVIEWED_KEY = 'dsh-hud.reviewed.v1'\n/**\n * 存档键的分隔符:NUL。\n *\n * 用 `String.fromCharCode(0)` 而不是 `\"\\u0000\"` 字面量:路径里不可能有 NUL,而转义写法\n * 在源码与测试里极易被多转一层(写成字面量反斜杠),排查起来极其费劲。\n */\nexport var REVIEW_KEY_SEP = String.fromCharCode(0)\n\n/** 存档键:`工作区 NUL 文件`(同一个文件名在别的仓库里是另一码事)。 */\nexport function reviewKeyOf(cwd: string | null | undefined, file: string): string {\n return String(cwd || '') + REVIEW_KEY_SEP + file\n}\n\n/** 存档上限:超出丢最久没动的那些(不然用久了这个键会一直长)。 */\nexport var REVIEWED_MAX = 200\n\n/** 读取\"我审过哪些版本\":坏数据一律当空表(存档不该把插件带崩)。 */\nexport function readReviewed(): ReviewedMap {\n try {\n var raw = window.localStorage.getItem(REVIEWED_KEY)\n if (!raw) return {}\n // 与 readUi 同理:localStorage 里的 JSON 是不可信输入,逐步收窄而不是直接当目标类型用。\n var parsed: unknown = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n var source = parsed as Record<string, unknown>\n var out: ReviewedMap = {}\n for (var key in source) {\n var item = source[key]\n if (!Object.prototype.hasOwnProperty.call(source, key) || !item || typeof item !== 'object')\n continue\n // 同一系统边界:条目也是不可信 JSON,先收成表再逐个字段判断。\n var entry = item as Record<string, unknown>\n if (typeof entry.signature !== 'string') continue\n out[key] = { signature: entry.signature, at: typeof entry.at === 'number' ? entry.at : 0 }\n }\n return out\n } catch (error) {\n return {}\n }\n}\n\n/** 写回\"我审过哪些版本\":超上限就按最后触达时间丢最旧的。 */\nexport function writeReviewed(next: ReviewedMap): ReviewedMap {\n try {\n var keys = Object.keys(next)\n if (keys.length > REVIEWED_MAX) {\n keys.sort(function (left: string, right: string) {\n return (next[left].at || 0) - (next[right].at || 0)\n })\n var trimmed: ReviewedMap = {}\n var keep = keys.slice(keys.length - REVIEWED_MAX)\n for (var index = 0; index < keep.length; index += 1) trimmed[keep[index]] = next[keep[index]]\n next = trimmed\n }\n window.localStorage.setItem(REVIEWED_KEY, JSON.stringify(next))\n return next\n } catch (error) {\n // 写不进去只意味着\"这次不记住\",不影响功能。\n return next\n }\n}\n\n/* --------------------------------------------------------------- store */\n\n/**\n * 插件自诊断的现场记录:只写控制台,不碰界面、不留全局。\n *\n * 客户端插件的异常和\"服务找不到\"都不会进宿主日志,所以控制台是排查时唯一的通道。\n * 同一个 reason + 同样的事实只报一次(轮询每 8s 会重试),一行 JSON 便于复制。\n */\nexport function createProbe(): Probe {\n var seen: Record<string, true> = {}\n return {\n /** 记一行现场:一行进控制台,同时留一份在 localStorage(都按 reason+事实去重)。 */\n log: function (reason: string, detail?: Record<string, unknown>) {\n var facts = detail === undefined ? {} : detail\n var line = reason + ' ' + JSON.stringify(facts)\n if (seen[line] === true) return\n seen[line] = true\n console.warn('[dsh-hud] ' + line)\n try {\n var history = window.localStorage.getItem(DEBUG_KEY)\n var parsed: unknown = history === null ? [] : JSON.parse(history)\n var list: Array<{ at: number; line: string }> = Array.isArray(parsed) ? parsed : []\n list.push({ at: Date.now(), line: line })\n window.localStorage.setItem(DEBUG_KEY, JSON.stringify(list.slice(-24)))\n } catch (error) {\n // 隐私模式等写不进去只影响这行诊断,不影响功能。\n }\n },\n }\n}\n\n/**\n * 面板左栏那份工作区清单 —— **渲染与键盘导航共用这一份**。\n *\n * 过滤必须发生在一个地方:键盘选中用的是行下标,如果渲染时过滤了而导航没过滤,\n * ↑/↓ 就会落在看不见的行上。\n */\nexport function visibleChangeRows(state: HudState): HudChange[] {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var onlySession = state.onlySession === true\n var onlyLatest = state.onlyLatest === true\n if (!onlySession && !onlyLatest) return changes\n var view = sessionChangeView(state)\n // 归因还没到手时这些开关不该悄悄把清单清空:那时候它们还没有意义。\n if (view === null) return changes\n // 别名:`view` 被闭包捕获后 TS 不再保留\"非 null\"的收窄,这里先取出两份表。\n var mineOf = view.mine\n var possibleOf = view.possible\n var rows = changes\n if (onlySession) {\n // \"可能相关\"(命令旁听)也算本会话碰过的:过滤器的承诺是\"只看本会话动过的\"。\n rows = rows.filter(function (change) {\n return mineOf[change.file] !== undefined || possibleOf[change.file] !== undefined\n })\n }\n var latestTurn = view.latestTurn\n if (onlyLatest && latestTurn !== null) {\n // \"这一轮改了什么\":只留最近一轮确定动过的(\"可能相关\"没有可靠的回合归属,不收)。\n rows = rows.filter(function (change) {\n var record = mineOf[change.file]\n return record !== undefined && record.lastTurn === latestTurn\n })\n }\n return rows\n}\n\n/** 进程内唯一一份探针:两个座位共用,去重也一起算。 */\nexport var probe: Probe = createProbe()\n\n/**\n * 两个 sha 说的是不是同一次提交。\n *\n * 最近提交那份清单里是 **短 sha**,而提交明细里是宿主半解析回来的**完整 sha**,\n * 直接 `===` 永远配不上;按前缀对一下(git 自己认 rev 也是这个语义)。\n */\nexport function sameCommit(left: unknown, right: unknown): boolean {\n if (typeof left !== 'string' || typeof right !== 'string' || left === '' || right === '')\n return false\n return left === right || right.indexOf(left) === 0 || left.indexOf(right) === 0\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/** 图片请求的缓存键:把请求的每个字段都带上,免得两侧共用一个键。 */\nexport function imagePairKey(request: ImageRequest): string {\n return [\n request.cwd || '',\n request.path || '',\n request.origPath || '',\n request.side || '',\n request.sha || '',\n ].join('\\u0000')\n}\n\n/** 字节数:十进制 KB/MB,和文件管理器里看到的一致。 */\nexport function formatBytes(bytes: unknown): string {\n if (typeof bytes !== 'number' || !isFinite(bytes) || bytes < 0) return '?'\n if (bytes < 1000) return String(bytes) + ' B'\n if (bytes < 1000 * 1000) return (bytes / 1000).toFixed(1) + ' KB'\n return (bytes / 1000 / 1000).toFixed(2) + ' MB'\n}\n\n/** 图片类文件的扩展名。这里只做粗筛 —— 真正\"是不是图\"由宿主按文件头判断。 */\nexport var IMAGE_FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'avif']\n\n/* ------------------------------------------------------- 悬浮面板 */\n\n/**\n * 当前右栏里有哪几栏 —— 样式、夹位置、渲染三处必须用同一份判断,抽出来免得漂移。\n *\n * @returns `{ diff, commit }`:`diff` = 有工作区/提交差异,`commit` = 打开着提交明细。\n */\nexport function panelLayout(state: HudState): PanelLayout {\n return {\n diff: state.diff !== null && typeof state.diff === 'object',\n commit: state.commit !== null && typeof state.commit === 'object',\n }\n}\n\n/**\n * 面板宽度:单栏紧凑;打开右栏就按\"左栏 + [提交明细栏] + 抓握 + 差异栏\"算。\n *\n * 右栏(差异)宽度由面板右边缘的抓握拖动调整并记忆;中间那栏是固定宽度。\n * 视口不够宽时只把弹性的一栏(差异)压小——左栏与提交明细栏是有下限的固定栏。\n *\n * @param layout - `panelLayout()` 的结果。\n * @param diffWidth - 差异栏的目标宽度。\n */\nexport function panelWidthFor(\n layout: PanelLayout,\n diffWidth?: number | null,\n commitWidth?: number | null,\n): number {\n var hasCommit = layout.commit === true\n var hasDiff = layout.diff === true\n if (!hasCommit && !hasDiff) return PANEL_WIDTH\n // **逐栏钳到自己的下限**:只对总宽加一个 floor 是不够的 —— 存档里写着 40px 时,\n // 总宽仍然会按 40 算(floor 只保证\"不小于最小总和\",但差额会被算到别的栏头上)。\n var middle = hasCommit\n ? Math.max(COMMIT_MIN_WIDTH, typeof commitWidth === 'number' ? commitWidth : COMMIT_LIST_WIDTH)\n : 0\n var right = hasDiff\n ? Math.max(DIFF_MIN_WIDTH, typeof diffWidth === 'number' ? diffWidth : DIFF_DEFAULT_WIDTH)\n : 0\n // 两条抓握:面板右边缘那条(调差异栏;只有提交栏时调提交栏)—— 它是绝对定位、**不占宽**,\n // 但宽度里要预留 6px 让位,否则它压在内容上;栏间那条是真实的一格,占宽。\n var outerGrip = RESIZE_GRIP_WIDTH\n var innerGrip = hasCommit && hasDiff ? RESIZE_GRIP_WIDTH : 0\n var available = window.innerWidth - 2 * VIEWPORT_MARGIN\n return Math.min(PANEL_WIDTH + middle + innerGrip + right + outerGrip, available)\n}\n\n/** 把位置夹回视口内,避免拖到屏幕外再也点不到。 */\nexport function clampPos(left: number, top: number, width?: number | null): PanelPos {\n var panelWidth = typeof width === 'number' ? width : PANEL_WIDTH\n var maxLeft = Math.max(VIEWPORT_MARGIN, window.innerWidth - panelWidth - VIEWPORT_MARGIN)\n var maxTop = Math.max(VIEWPORT_MARGIN, window.innerHeight - 48)\n return {\n left: Math.min(Math.max(VIEWPORT_MARGIN, left), maxLeft),\n top: Math.min(Math.max(VIEWPORT_MARGIN, top), maxTop),\n }\n}\n\n/**\n * 量出胶囊所在 dock 行的视口顶部坐标。\n *\n * 这里用**自己的** `data-dsh-hud` 标记,不碰产品的 DOM 选择器。dsh-git 把\n * \"输入框堆叠高 168px\"写成了常量并注明那只是一次测量;直接量真实位置可以同时\n * 适配 hero(空白会话,输入框居中)与 active 两种布局。\n *\n * @returns 视口坐标的顶部像素,量不到时返回 null。\n */\n/**\n * 面板当前该有多宽:从 store **现读**(模式切换会改宽度,别用可能过期的渲染闭包)。\n *\n * 原来它是组件里的 `panelWidthOf(store)`,被\"视口夹取\"和\"拖动\"两处用;提成顶层\n * 纯函数后两边共用同一份判断。\n */\nexport function panelWidthOf(store: HudStore): number {\n var snapshot = store.getSnapshot()\n return panelWidthFor(panelLayout(snapshot), snapshot.diffWidth, snapshot.commitWidth)\n}\n\n/** 自有样式表的稳定 tag id:`data-plugin-css` 用它做幂等插入。 */\nexport var CSS_TAG_ID = 'dsh-hud/styles.css'\n\n/* --------------------------------------------------- 会话归因(对账) */\n\n/**\n * 把\"本会话改过哪些文件\"(宿主半按 fs 事件记的)与 git 现状对起来。\n *\n * - `mine`:本会话改过、且工作区里还有差异(`status.changes` 里能找到)\n * - `gone`:本会话改过、但工作区里已经没有差异(被改回去了 / 被别的会话或你自己覆盖了)\n * - `others`:工作区里有差异、但不是本会话动的\n *\n * 名字能直接对上:宿主报的是 cwd 之内的 posix 相对路径,`status.changes[].file` 也是。\n * 还拿不到归因(老宿主 / 请求还没回来)时返回 `null` —— 界面这时**不打任何标记**,\n * 而不是把所有改动都标成\"别人的\"。\n */\nexport function sessionChangeView(state: HudState): SessionChangeView | null {\n // 快照可能来自老宿主/旧存档:这里保留\"缺字段就当没有\"的老行为,不让界面崩。\n var slice: SessionChangesState | null | undefined = state.sessionChanges\n if (slice === null || slice === undefined) return null\n var files = slice.files\n if (files === null || files === undefined) return null\n var possibleAll: Record<string, SessionChangeCandidate> | null =\n slice.possible === undefined ? null : slice.possible\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var mine: Record<string, SessionChange> = {}\n var possible: Record<string, SessionChangeCandidate> = {}\n var gone: string[] = []\n var index\n var change\n // 先认\"确定改过\"的:一档优先于一档,同一个文件不会被标两次。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (Object.prototype.hasOwnProperty.call(files, change.file))\n mine[change.file] = files[change.file]\n }\n // 再看\"可能相关\":只标没有被确定认领的那些(命令旁听来的,置信度低一档)。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (mine[change.file] !== undefined) continue\n if (possibleAll !== null && Object.prototype.hasOwnProperty.call(possibleAll, change.file)) {\n possible[change.file] = possibleAll[change.file]\n }\n }\n for (var file in files) {\n if (Object.prototype.hasOwnProperty.call(files, file) && mine[file] === undefined)\n gone.push(file)\n }\n gone.sort()\n var commands: SessionCommand[] = Array.isArray(slice.commands) ? slice.commands : []\n var turns =\n state.turns !== undefined && state.turns !== null && state.turns.available === true\n ? state.turns\n : null\n var latestTurn = turns === null ? null : turns.latest\n var byTurn: Record<number, number> = {}\n var latestCount = 0\n for (var mineFile in mine) {\n if (!Object.prototype.hasOwnProperty.call(mine, mineFile)) continue\n var turn = typeof mine[mineFile].lastTurn === 'number' ? mine[mineFile].lastTurn : null\n if (turn === null) continue\n byTurn[turn] = (byTurn[turn] === undefined ? 0 : byTurn[turn]) + 1\n if (latestTurn !== null && turn === latestTurn) latestCount += 1\n }\n return {\n mine: mine,\n possible: possible,\n /** 第几轮 → prompt 预览(来自 `turnOutline` 投影;没有就是空表)。 */\n turnPrompts: turns === null ? {} : turns.prompts,\n latestTurn: latestTurn,\n latestCount: latestCount,\n byTurn: byTurn,\n mineCount: Object.keys(mine).length,\n possibleCount: Object.keys(possible).length,\n /** 跑过的命令里\"看着会写文件\"的条数(含失败的,界面自己再分)。 */\n commandCount: commands.length,\n writingCommands: commands.filter(function (item) {\n return item && item.readOnly !== true\n }).length,\n commands: commands,\n gone: gone,\n /** 本会话记录过的文件总数(宿主那边的集合大小)。 */\n tracked: Object.keys(files).length,\n since: slice.since === undefined ? null : slice.since,\n dropped: slice.dropped === undefined ? 0 : slice.dropped,\n possibleDropped: slice.possibleDropped === undefined ? 0 : slice.possibleDropped,\n others: changes.length - Object.keys(mine).length,\n }\n}\n\nexport function reviewView(state: HudState): ReviewView {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var signatures: ReviewSignatures =\n state.review && typeof state.review === 'object' ? state.review : {}\n var reviewed = state.reviewed && typeof state.reviewed === 'object' ? state.reviewed : {}\n var prefix = reviewKeyOf(state.cwd, '')\n var marks: Record<string, ReviewMark> = {}\n var known: Record<string, true> = {}\n var done = 0\n var total = 0\n var staleCount = 0\n for (var index = 0; index < changes.length; index += 1) {\n var file = changes[index].file\n var signature = typeof signatures[file] === 'string' ? signatures[file] : null\n if (signature === null) continue\n known[file] = true\n total += 1\n var mark = reviewed[prefix + file]\n if (mark === undefined) continue\n if (mark.signature === signature) {\n marks[file] = 'fresh'\n done += 1\n } else {\n marks[file] = 'stale'\n staleCount += 1\n }\n }\n return { marks: marks, known: known, done: done, total: total, staleCount: staleCount }\n}\n","/**\n * 展示用的格式化:分支名、工作区名、相对时间、计数简写。\n *\n * 都是纯函数(输入数据、输出字符串),所以可以直接单测,也让组件里不再夹带这类判断。\n */\n\nimport type {\n CountShorthand,\n HudCountKey,\n HudSessionOverviewRow,\n HudStatusCounts,\n RowSummary,\n Translate,\n} from './shared.js'\nimport {\n BALANCE_LOW_KEY,\n COUNT_MARK,\n COUNT_ORDER,\n PILL_BRANCH_MAX,\n PILL_WORKSPACE_MAX,\n} from './shared.js'\n/** 从中间省略:保留头尾(路径/分支名两头的区分度都比中间高)。 */\nexport function middleEllipsis(name: string, max: number): string {\n if (name.length <= max) return name\n var head = Math.ceil((max - 1) / 2)\n var tail = max - 1 - head\n return name.slice(0, head) + '…' + name.slice(name.length - tail)\n}\n\n/**\n * 工作区名 = 会话 cwd 的目录名(侧栏给工作区起的名字就是它)。\n *\n * 取的是 **cwd** 而不是仓库顶层:会话开在子目录时,用户认的是那个子目录;\n * 完整路径在悬停与面板里,胶囊上只要一个能认出来的短名字。\n */\nexport function workspaceNameOf(cwd: string | null | undefined): string | null {\n if (typeof cwd !== 'string' || cwd.length === 0) return null\n var trimmed = cwd.replace(/[/\\\\]+$/, '')\n if (trimmed.length === 0) return null\n var index = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n var name = index === -1 ? trimmed : trimmed.slice(index + 1)\n if (name.length === 0) return null\n return middleEllipsis(name, PILL_WORKSPACE_MAX)\n}\n\n/** 分支名过长时中段省略,保留头尾信息量。 */\nexport function shortBranch(name: string): string {\n return middleEllipsis(name, PILL_BRANCH_MAX)\n}\n\n/**\n * 改动计数 → 简写符列表 `[{ key, mark, value }]`(只列非零项,顺序固定)。\n *\n * 胶囊与侧栏行内 chip 共用这一份:两处各写一套算法一定会漂。\n */\nexport function countShorthands(counts: HudStatusCounts | null | undefined): CountShorthand[] {\n var out: CountShorthand[] = []\n var source: Partial<HudStatusCounts> = counts || {}\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = source[key] || 0\n if (value <= 0) continue\n out.push({ key: key, mark: COUNT_MARK[key], value: value })\n }\n return out\n}\n\nexport function branchLabel(\n status: { branch?: string | null; oid?: string | null } | null | undefined,\n t: Translate,\n): string {\n if (!status) return '—'\n if (status.branch) return status.branch\n if (status.oid) return t('panel.detached') + ' ' + status.oid\n return t('panel.detached')\n}\n\nexport function timeLabel(at: number): string {\n var date = new Date(at)\n var pad = function (value: number) {\n return value < 10 ? '0' + String(value) : String(value)\n }\n return pad(date.getHours()) + ':' + pad(date.getMinutes()) + ':' + pad(date.getSeconds())\n}\n\n/* --------------------------------------------------------- 胶囊(dock) */\n\n/**\n * 会话作用域的座位:框架给出 `sessionId`,`useSessions` 快照给出该会话的 `cwd`,\n * 因此它是唯一能完成绑定的座位。\n */\n/**\n * 侧栏每条会话行上的状态 chip(座位 `sidebar.workspaces.session.row.action`)。\n *\n * 一行只有 32px 高、悬停才露出来,所以只放**最该一眼看到的两件事**:这个工作区脏不脏\n * (git 简写符)与**这个会话自己改过几个文件**(`●n`,只我们有这个信息)。干净且本会话\n * 没改过就整枚不画 —— 不占位置、也不制造噪音。完整信息在悬停里。\n */\n/**\n * 一枚侧栏状态的\"事实摘要\":三种呈现(常亮标记 / 悬停动条 / 卡片详情)共用这一份计算。\n *\n * 同一份数据要在三个地方以三种宽度呈现,各写一套一定会漂 —— 所以抽成一处。\n */\nexport function rowSummaryOf(row: HudSessionOverviewRow | null | undefined): RowSummary | null {\n if (row === undefined || row === null || row.pending === true) return null\n var mineCount = typeof row.mine === 'number' ? row.mine : 0\n var possibleCount = typeof row.possible === 'number' ? row.possible : 0\n var shorthands: CountShorthand[] = row.repo === true ? countShorthands(row.counts) : []\n var changed = 0\n for (var index = 0; index < shorthands.length; index += 1) changed += shorthands[index].value\n return {\n row: row,\n mine: mineCount,\n possibleNow: typeof row.possibleNow === 'number' ? row.possibleNow : 0,\n possible: possibleCount,\n shorthands: shorthands,\n changed: changed,\n dirty: row.repo === true && row.dirty === true,\n hasContent: changed > 0 || mineCount > 0 || possibleCount > 0 || row.error !== null,\n }\n}\n\n/** 相对时间;超过 30 天直接给提交自己的日期。 */\nexport function commitWhen(\n commit: { committedAt?: number | null; date?: string | null },\n t: Translate,\n): string {\n if (typeof commit.committedAt !== 'number') return commit.date || ''\n return fullWhen(commit.committedAt)\n}\n\n/**\n * 时刻一律给完整年月日 + 时分(本地时区):`2026-10-16 14:18`。\n *\n * 两级简化都被否掉了:先是从\"2 小时前\"改成具体时间(相对时间在对账场景不够用),\n * 再是\"今天只给 `14:18`\"也被否 —— 用户要**年月日都在**。所以不再分档,一视同仁。\n * 只到分钟:提交/标签不需要秒,列一排在面板里也塞得下。\n */\nexport function fullWhen(at: number): string {\n var then = new Date(at)\n return (\n String(then.getFullYear()) +\n '-' +\n pad2(then.getMonth() + 1) +\n '-' +\n pad2(then.getDate()) +\n ' ' +\n pad2(then.getHours()) +\n ':' +\n pad2(then.getMinutes())\n )\n}\n\nexport function pad2(value: number): string {\n return value < 10 ? '0' + String(value) : String(value)\n}\n\n/** porcelain-v2 的展示字母 → counts 键(用于配色)。 */\nexport function statusKey(letter: string): HudCountKey {\n if (letter === 'M') return 'modified'\n if (letter === 'A') return 'added'\n if (letter === 'D') return 'deleted'\n if (letter === 'R') return 'renamed'\n if (letter === 'C') return 'copied'\n if (letter === 'T') return 'typeChanged'\n if (letter === '?') return 'untracked'\n if (letter === '!') return 'conflicted'\n return 'typeChanged'\n}\n\n/** 宿主半的 `details.code` → 本座位的本地化文案(store 只存机器码)。 */\nexport function errorText(code: string | null | undefined, t: Translate): string {\n if (code === 'timeout') return t('err.timeout')\n if (code === 'no-cwd') return t('err.no-cwd')\n if (code === 'git-missing') return t('err.git-missing')\n if (code === 'invalid-cwd') return t('err.invalid-cwd')\n if (code === 'transport') return t('err.transport')\n if (code === 'invalid-path') return t('err.invalid-path')\n // 提交可能在两次请求之间被 rebase / amend 换掉:给一句能操作的提示,而不是\"git 执行失败\"。\n if (code === 'unknown-commit') return t('commit.unknown')\n // 宿主半是启动时加载的:只刷新页面时,新端点还不存在,这个提示能省下一轮排查。\n if (code === 'unknown-endpoint') return t('err.stale-host')\n return t('err.git-failed')\n}\n\n/* ------------------------------------------------------------- 账号余额 */\n\n/**\n * 这个金额真的大于 0 吗(不引 big.js:只看字符串里有没有非零数字、且不是负数)。\n *\n * 平台会给 0 余额的钱包(赠送额度用完了也还在列里),DSH 自己的账号卡片就是\n * 这么过滤的(`new Big(wallet.balance).gt(0)`)—— 两边判定要一致,否则会出现\n * \"这里有 ¥0.00 的赠送余额\"这种没人想看的一行。\n *\n * @param amount - 平台给的十进制字符串。\n * @returns 严格大于 0 为 `true`。\n */\nexport function isPositiveAmount(amount: string): boolean {\n if (typeof amount !== 'string') return false\n var raw = amount.trim()\n if (raw.charAt(0) === '-') return false\n return /[1-9]/.test(raw)\n}\n\n/** 币种 → 符号。认不出的币种原样接在后面(`123.45 XYZ`),不猜符号。 */\nexport function currencySymbol(currency: string): string | null {\n if (currency === 'CNY') return '\\u00a5'\n if (currency === 'USD') return '$'\n return null\n}\n\n/**\n * 十进制字符串 → 千分位整数部分,**不经过浮点**。\n *\n * 余额是账目数字:`Number('1234567.89')` 在极大值上会丢精度,所以整数部分只做字符串分组,\n * 小数部分原样保留。符号与小数位由调用方决定(两者在本插件里是两条独立规则)。\n */\nfunction groupDigits(integer: string): string {\n var negative = integer.charAt(0) === '-'\n var digits = negative ? integer.slice(1) : integer\n var out = ''\n for (var index = 0; index < digits.length; index += 1) {\n var rest = digits.length - index\n out += digits.charAt(index)\n if (rest > 1 && rest % 3 === 1) out += ','\n }\n return negative ? '-' + out : out\n}\n\n/** 一个金额的字符串分解(不经过浮点:账目数字转成 double 会丢精度)。 */\nexport interface ParsedAmount {\n negative: boolean\n /** 整数部分,已去掉前导 0(`007` → `7`),至少一位。 */\n integer: string\n /** 小数部分,至少一位(`5` → `0`);科学计数法已展开。 */\n fraction: string\n}\n\n/**\n * 把十进制字符串拆成符号 / 整数 / 小数三块。\n *\n * 允许的写法与平台一致:`123`、`123.45`、`.5`、`5.`、科学计数法。\n * 不接受空串、`+` 号以外的其它前缀、以及 `1,234`(千分位是我们**输出**的形状,不是输入)。\n *\n * @param amount - 十进制字符串。\n * @returns 分解结果;非法输入回 `null`。\n */\nexport function parseAmount(amount: string): ParsedAmount | null {\n if (typeof amount !== 'string') return null\n var raw = amount.trim()\n if (!/^-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(raw)) return null\n var negative = raw.charAt(0) === '-'\n var body = negative ? raw.slice(1) : raw\n // 科学计数法只在真的出现时才展开(平台实际不会给,但别在这里崩)。\n if (/[eE]/.test(body)) {\n var expanded = Number(raw)\n if (!Number.isFinite(expanded)) return null\n negative = expanded < 0\n body = String(Math.abs(expanded))\n }\n var dot = body.indexOf('.')\n var integerPart = dot === -1 ? body : body.slice(0, dot)\n var fractionPart = dot === -1 ? '' : body.slice(dot + 1)\n // `.5` / `5.` 这类写法先补齐成 `0.5` / `5.0`;整数部分的前导 0 去掉(`007.5` → `7.5`)。\n if (integerPart.length === 0) integerPart = '0'\n integerPart = integerPart.replace(/^0+(?=\\d)/, '')\n if (fractionPart.length === 0) fractionPart = '0'\n return { negative: negative, integer: integerPart, fraction: fractionPart }\n}\n\n/** 参与加减的最大小数位(Decimal 的 8 位对账目足够,再长也没人会看)。 */\nvar AMOUNT_SCALE_MAX = 8\n\n/**\n * 把两个金额对齐到同一标度,转成 BigInt(值为 `真实值 × 10^scale`)。\n *\n * @param left - 第一个十进制字符串。\n * @param right - 第二个十进制字符串。\n * @returns 两个 BigInt 与它们共用的标度;任一非法回 `null`。\n */\nfunction scaledPair(\n left: string,\n right: string,\n): { left: bigint; right: bigint; scale: number } | null {\n var a = parseAmount(left)\n var b = parseAmount(right)\n if (a === null || b === null) return null\n var scale = Math.min(AMOUNT_SCALE_MAX, Math.max(a.fraction.length, b.fraction.length))\n var toScaled = function (value: ParsedAmount): bigint {\n var digits = (value.fraction + '0'.repeat(scale)).slice(0, scale)\n var magnitude = BigInt(value.integer + digits)\n return value.negative ? -magnitude : magnitude\n }\n return { left: toScaled(a), right: toScaled(b), scale: scale }\n}\n\n/** 标度整数 → 十进制字符串(`-30` + scale 2 → `-0.30`)。 */\nfunction formatScaled(value: bigint, scale: number): string {\n var negative = value < BigInt(0)\n var digits = (negative ? -value : value).toString()\n if (scale === 0) return (negative ? '-' : '') + digits\n var padded = digits.padStart(scale + 1, '0')\n var head = padded.slice(0, padded.length - scale)\n var tail = padded.slice(padded.length - scale)\n return (negative ? '-' : '') + head + '.' + tail\n}\n\n/**\n * 两个金额相减(`left - right`),精确到小数末位。\n *\n * 为什么不用 `Number`:余额是账目数字,`8.6 - 8.3` 在 double 里是 `0.2999999999999998`,\n * 两次刷新差一分钱就能看出误差。这里按十进制对齐后做整数减法,结果直接可用于展示。\n *\n * @param left - 被减数(十进制字符串)。\n * @param right - 减数。\n * @returns 差值(十进制字符串,可能带 `-`,小数位到 max(两侧位数));任一非法回 `null`。\n */\nexport function subtractAmounts(left: string, right: string): string | null {\n var pair = scaledPair(left, right)\n if (pair === null) return null\n return formatScaled(pair.left - pair.right, pair.scale)\n}\n\n/**\n * `amount` 是否严格小于 `limit`(同样走十进制对齐,不走浮点)。\n *\n * @param amount - 待比较的金额。\n * @param limit - 阈值(十进制字符串)。\n * @returns 严格小于为 `true`;任一非法回 `false`(拿不准就不报警)。\n */\nexport function isBelowAmount(amount: string, limit: string): boolean {\n var pair = scaledPair(amount, limit)\n if (pair === null) return false\n return pair.left < pair.right\n}\n\n/**\n * 读取低余额阈值(`dsh-hud.balanceLow`,没有就用默认 10)。\n *\n * 键值是十进制数;解析不出正数就退回默认值 —— 一个写坏了的存储项不该让提醒消失。\n *\n * @param fallback - 默认阈值(`BALANCE_LOW_DEFAULT`)。\n * @returns 阈值字符串(交给 `isBelowAmount`)。\n */\nexport function lowThresholdOf(fallback: number): string {\n try {\n var raw = globalThis.localStorage ? globalThis.localStorage.getItem(BALANCE_LOW_KEY) : null\n if (typeof raw === 'string' && raw.trim() !== '') {\n var value = Number(raw.trim())\n if (Number.isFinite(value) && value > 0) return String(value)\n }\n } catch (error) {\n // 读不到存储(隐私模式 / 假 DOM)就用默认值,不影响其它任何东西。\n }\n return String(fallback)\n}\n\n/**\n * 变化量的展示文本:`+¥0.30` / `-¥0.30`(零值回 `null`,调用方据此不画那一行)。\n *\n * @param amount - `subtractAmounts` 的输出。\n * @param currency - 币种。\n * @returns 带正负号的文本;零值或金额非法回 `null`。\n */\nexport function formatDelta(amount: string, currency: string): string | null {\n var parsed = parseAmount(amount)\n if (parsed === null) return null\n var abs = parsed.negative ? amount.slice(1) : amount\n var text = formatBalance(abs, currency)\n if (text === null) return null\n // 零值没有\"变化\",不画(`-0.00` 这种更不该出现)。\n if (/^0*(\\.0*)?$/.test(abs.trim())) return null\n return (parsed.negative ? '-' : '+') + text\n}\n\n/**\n * 把一个钱包余额格式化成界面文本。\n *\n * 规则照 DSH 自己的账号卡片(`formatBalance`,`dsh-client-ui-settings-account`):\n * 0 → `¥0.00`;不足一分 → `<¥0.01`;负的小额进位成 `-¥0.01`;其余**向零截断**到两位并加千分位。\n * 两边读同一份余额就该显示同一个数,所以这里刻意复刻而不是\"差不多\"。\n *\n * 认不出的币种没有符号可用:直接给 `123.45 XYZ` 这样带代码的文本,不做符号猜测。\n *\n * @param amount - 平台给的十进制字符串(可能带负号;非法输入按 0 处理)。\n * @param currency - `CNY` / `USD`,其它值按代码显示。\n * @returns 可展示文本;`amount` 非法时回 `null`(宁可不画,也不画错数字)。\n */\nexport function formatBalance(amount: string, currency: string): string | null {\n var parsed = parseAmount(amount)\n if (parsed === null) return null\n var symbol = currencySymbol(currency)\n var suffix = symbol === null ? ' ' + currency : ''\n var negative = parsed.negative\n var integerPart = parsed.integer\n var fractionPart = parsed.fraction\n\n var sign = negative ? '-' : ''\n /** 按币种符号规则拼一段文本。 */\n var render = function (value: string): string {\n return symbol === null ? sign + value + suffix : sign + symbol + value\n }\n var integerIsZero = /^0*$/.test(integerPart)\n var fractionHasDigit = /[1-9]/.test(fractionPart)\n\n // 零:`¥0.00`。判零只看有没有非 0 数字,避免 `0.000` 这类写法被当成\"不足一分\"。\n if (integerIsZero && !fractionHasDigit) return render('0.00')\n\n // 不足一分(且不是零):正数说 `<¥0.01`,负数没有\"小于\"的直觉,直接进位成 `-¥0.01`。\n // `0.0` 后面全 0、前两位小数是 00,就等价于 0 < |v| < 0.01。\n if (integerIsZero && (fractionPart + '00').slice(0, 2) === '00') {\n if (negative) return render('0.01')\n return '<' + render('0.01')\n }\n\n // 常规:整数部分千分位,小数**向零截断**到两位(不足补 0)。\n var cents = (fractionPart + '00').slice(0, 2)\n return render(groupDigits(integerPart) + '.' + cents)\n}\n","/**\n * 余额胶囊:dock 行里那枚读数。\n *\n * 它画**当前会话真正在用的那个账本**:会话用的是 DeepSeek 平台账号,就画账号余额;\n * 换成了第三方中转站 / 官方 API Key,就画那个提供方的余额(见 `store/provider.ts`)。\n * 两个账本的钱不混着显示 —— 混着显示就是\"胶囊数字看着像 bug\"的来源。\n *\n * 两个动作:**左键刷新**(不必等 5 分钟那条节拍)、**右键打开平台用量页**\n * (累计消耗 / 本月消耗这些账目只在平台上,DSH 的账号接口只回钱包余额)。\n *\n * 它与其它几枚同住一条注册 —— dock 是竖列,独立注册会另起一整行(工具胶囊当初就撞过这个)。\n *\n * 余额是**账号级**事实:与会话、工作区都无关,所以这里不读 `sessionId`,也不因\n * \"当前会话换了\"而变化。没有账号能力 / 没登录 / 一个可展示的钱包都没有时**不画**,\n * 而不是画一个 `¥0.00` —— 那会把\"读不到\"说成\"没钱\"。\n */\n\nimport React from 'react'\n\nimport {\n formatBalance,\n formatDelta,\n fullWhen,\n isBelowAmount,\n isPositiveAmount,\n lowThresholdOf,\n subtractAmounts,\n} from '../format.js'\nimport type {\n HudBalance,\n HudBalanceWallet,\n HudProviderBalance,\n HudProviderLedger,\n HudStore,\n TipLine,\n Translate,\n} from '../shared.js'\nimport { BALANCE_LOW_DEFAULT, h, probe } from '../shared.js'\n\n/** 余额胶囊的选项。 */\nexport interface BalancePillOptions {\n t: Translate\n store: HudStore\n}\n\n/**\n * 挑出要摆在胶囊上的那个钱包。\n *\n * 平台一般只给一个充值钱包;真给了多个,取**第一个**(顺序由平台决定),其余全在\n * 悬停里 —— 胶囊上塞两个币种会挤掉它\"扫一眼\"的价值。\n *\n * @param state - 快照(只读 `balance`)。\n * @returns 主钱包,或 `null`(没有可展示的钱包)。\n */\nexport function primaryWallet(state: { balance?: HudBalance | null }): HudBalanceWallet | null {\n var balance = state.balance\n if (!balance || !Array.isArray(balance.wallets)) return null\n for (var index = 0; index < balance.wallets.length; index += 1) {\n var wallet = balance.wallets[index]\n if (!wallet || typeof wallet.balance !== 'string') continue\n if (typeof wallet.currency !== 'string') continue\n return wallet\n }\n return null\n}\n\n/**\n * 赠送钱包里**真正有钱**的那些。\n *\n * 平台会把用完的赠送额度继续挂在 `bonusWallets` 里(余额 `0`),过滤规则与 DSH 自己的\n * 账号卡片一致(`isPositiveAmount`,等价于它的 `new Big(…).gt(0)`)—— store 那侧已经过滤\n * 过一次,这里再兜一次:组件不该假设上游一定干净。\n *\n * @param balance - 余额快照。\n * @returns 余额大于 0 的赠送钱包。\n */\nexport function visibleBonus(balance: HudBalance): HudBalanceWallet[] {\n var bonus = Array.isArray(balance.bonus) ? balance.bonus : []\n var out: HudBalanceWallet[] = []\n for (var index = 0; index < bonus.length; index += 1) {\n var wallet = bonus[index]\n if (!wallet || typeof wallet.balance !== 'string') continue\n if (!isPositiveAmount(wallet.balance)) continue\n out.push(wallet)\n }\n return out\n}\n\n/**\n * 某个钱包**较上一次成功刷新**变化了多少。\n *\n * 只在我们自己采样到的两个时刻之间比较(不是平台账单的\"今日消耗\"):所以文案写的是\n * \"较上次刷新\",而不是\"今天花了多少\"。上一次读数与这次币种对不上、或数值没变,都回\n * `null`(那时没有可展示的变化量)。\n *\n * @param balance - 余额快照(含 `prev`)。\n * @param wallet - 当前主钱包。\n * @returns `{ delta, at }`;没有变化或没有前一份读数时回 `null`。\n */\nexport function deltaOf(\n balance: HudBalance,\n wallet: HudBalanceWallet,\n): { delta: string; at: number } | null {\n var prev = balance.prev\n if (!prev || !Array.isArray(prev.wallets)) return null\n for (var index = 0; index < prev.wallets.length; index += 1) {\n var older = prev.wallets[index]\n if (!older || older.currency !== wallet.currency) continue\n if (typeof older.balance !== 'string') return null\n var diff = subtractAmounts(wallet.balance, older.balance)\n if (diff === null) return null\n var text = formatDelta(diff, wallet.currency)\n if (text === null) return null\n return { delta: text, at: prev.at }\n }\n return null\n}\n\n/**\n * 这个钱包是否低于低余额阈值。\n *\n * 阈值默认 `BALANCE_LOW_DEFAULT`,可被 `dsh-hud.balanceLow` 覆盖(见 README)。\n * 拿不准(金额非法、阈值读坏)时**不报警**:误报比漏报更烦人。\n *\n * @param wallet - 钱包。\n * @returns 严格低于阈值为 `true`。\n */\nexport function isLowWallet(wallet: HudBalanceWallet): boolean {\n return isBelowAmount(wallet.balance, lowThresholdOf(BALANCE_LOW_DEFAULT))\n}\n\n/** 提供方账本里可展示的主数字(剩余优先;只有已用时退回已用)。 */\nexport interface ProviderFigure {\n /** 主数字的十进制字符串。 */\n amount: string\n /** 币种(站点口径)。 */\n currency: string | null\n /** 主数字是\"剩余\"还是\"已用\"(决定胶囊要不要前缀、要不要低余额判断)。 */\n kind: 'remaining' | 'used'\n}\n\n/**\n * 从提供方余额里挑出胶囊该画的那个数。\n *\n * 顺序是有讲究的:**剩余优先**。中转站把额度写成\"无限\"占位值时 `remaining` 是 `null`\n * (站点自己也不知道剩多少),这时只能画已用 —— 一个能核对的数字,胜过编一个天文数字。\n *\n * @param balance - 提供方余额快照。\n * @returns 主数字;一个能画的都没有时回 `null`(那就不画胶囊)。\n */\nexport function providerFigureOf(balance: HudProviderBalance): ProviderFigure | null {\n if (typeof balance.remaining === 'string') {\n return { amount: balance.remaining, currency: balance.currency, kind: 'remaining' }\n }\n // 只剩\"已用\"可画时优先用**本 Key**那个数:胶囊空间只够一个,而它跟\"这个会话\n // 花了多少\"对得更近;账号累计照样在悬停里列着(见 `providerBalanceTitle`)。\n if (typeof balance.usedKey === 'string') {\n return { amount: balance.usedKey, currency: balance.currency, kind: 'used' }\n }\n if (typeof balance.usedAccount === 'string') {\n return { amount: balance.usedAccount, currency: balance.currency, kind: 'used' }\n }\n // 只有钱包列表(官方 `/user/balance` 那种):取第一个。\n var wallets = Array.isArray(balance.wallets) ? balance.wallets : []\n for (var index = 0; index < wallets.length; index += 1) {\n var wallet = wallets[index]\n if (wallet && typeof wallet.balance === 'string') {\n return { amount: wallet.balance, currency: wallet.currency, kind: 'remaining' }\n }\n }\n return null\n}\n\n/**\n * 金额文本:币种未知时不要凭空造一个符号。\n *\n * @param amount - 十进制字符串。\n * @param currency - 币种(可能为 `null`)。\n * @returns 展示文本;金额非法回 `null`。\n */\nexport function amountText(amount: string, currency: string | null): string | null {\n var text = formatBalance(amount, currency === null ? '' : currency)\n if (text === null) return null\n return currency === null ? text.trim() : text\n}\n\n/** `amountText` 的\"可能没有\"版本:不是十进制字符串就回 `null`。 */\nfunction amountTextOf(amount: string | null | undefined, currency: string | null): string | null {\n if (typeof amount !== 'string') return null\n return amountText(amount, currency)\n}\n\n/** i18n 模板里用来定位\"标签/数值\"分界点的哨兵(不可能出现在译文里)。 */\nconst TIP_SPLIT = '\\u0000'\n\n/**\n * 把 `'余额 {amount}'` 这样的模板拆成「标签 + 数值」两段。\n *\n * 为什么要拆:用户要「数字亮、标签灰」,而一整段文本在 DOM 里只有一个文本节点。\n * 做法是把参数换成一个哨兵再渲染,按哨兵位置切开 —— 这样**标签部分始终来自词典**,\n * 中英文都自动跟着走,不需要为每种语言另立一个\"只有标签\"的 key。\n *\n * @param t - 取词。\n * @param key - 词典里那条带 `{param}` 的模板。\n * @param param - 模板里的参数名。\n * @param value - 已经格式化好的数值文本。\n * @returns 逐行结构里的一行。\n */\nfunction labelledLine(\n t: Translate,\n key: string,\n param: string,\n value: string,\n tone: TipLine['tone'] = 'primary',\n): TipLine {\n var probe: Record<string, string> = {}\n probe[param] = TIP_SPLIT\n var rendered = t(key, probe)\n var at = rendered.indexOf(TIP_SPLIT)\n if (at < 0) return { text: t(key, probe) }\n return { label: rendered.slice(0, at).trim(), value: value, tone: tone }\n}\n\n/**\n * 提供方余额的悬停明细。\n *\n * 只放读数:提供方 id、余额、账号已用、请求数、本 Key 已用、更新时间。\n * 不再解释\"为什么没有剩余额度\"—— 没有那一行本身就是答案。\n *\n * @param balance - 提供方余额快照。\n * @param t - 取词。\n * @returns 逐行结构(标签灰、数字亮)。\n */\nexport function providerBalanceTitle(balance: HudProviderBalance, t: Translate): TipLine[] {\n // 站名当抬头:一整行次要色,不和下面的读数抢注意力。\n var lines: TipLine[] = [{ text: balance.provider, tone: 'muted' }]\n var figure = providerFigureOf(balance)\n if (figure !== null && figure.kind === 'remaining') {\n var remainingText = amountText(figure.amount, figure.currency)\n if (remainingText !== null)\n // 绿 = 还剩多少(\"可用额度\"的通行读法)。\n lines.push(labelledLine(t, 'balance.providerRemaining', 'amount', remainingText, 'success'))\n }\n // 官方 Key 会带赠送额度(胶囊上那个 `+` 就是它),明细里要说清是多少 ——\n // 不然 `+` 就是个没人知道什么意思的符号。\n var bonus = Array.isArray(balance.bonus) ? balance.bonus : []\n var parts: string[] = []\n for (var index = 0; index < bonus.length; index += 1) {\n if (!isPositiveAmount(bonus[index].balance)) continue\n var bonusText = amountText(bonus[index].balance, bonus[index].currency)\n if (bonusText !== null) parts.push(bonusText)\n }\n if (parts.length > 0) {\n lines.push({ label: t('balance.bonus'), value: parts.join(' · '), tone: 'muted' })\n }\n // 两个\"已用\"是两个账本上的数(ur:账号 $637.56 / 本 Key $536.22),\n // 所以各占一行、各带自己的标签 —— 不合并成一个数,也不互相顶替。\n // 顺序:余额 → 请求数 → 账号已用 → 本 Key 已用。\n // 请求数紧跟在余额下面(用户要的):它是同一屏里唯一一个\"次数\"而不是钱,\n // 夹在两个金额中间容易被读成第三个金额。\n if (typeof balance.requests === 'number' && balance.requests > 0) {\n // 站点后台「API 请求」那个数;千分位让四位数一眼能读。\n // 品牌蓝:这是次数、不是金额,用中性强调色把它和两行钱区分开。\n lines.push(\n labelledLine(\n t,\n 'balance.providerRequests',\n 'count',\n balance.requests.toLocaleString('en-US'),\n 'accent',\n ),\n )\n }\n var accountUsed = amountTextOf(balance.usedAccount, balance.currency)\n if (accountUsed !== null) {\n // 琥珀 = 已经花掉多少。它是**分类色**(消耗),不是在报警。\n lines.push(labelledLine(t, 'balance.providerUsedAccount', 'amount', accountUsed, 'warn'))\n }\n var keyUsed = amountTextOf(balance.usedKey, balance.currency)\n if (keyUsed !== null) {\n lines.push(labelledLine(t, 'balance.providerUsedKey', 'amount', keyUsed, 'warn'))\n }\n // 时间不是读数,整行次要色。\n lines.push({ text: t('balance.updated', { time: fullWhen(balance.at) }), tone: 'muted' })\n return lines\n}\n\n/**\n * 点胶囊该干什么。\n *\n * 分两种情况,因为\"点了没反应\"是最难查的那种问题:\n * - 这个提供方的**后台凭据还没配**(`token.configured === false`)→ 直接弹设置面板。\n * 这才是用户此刻真正要做的事;让他自己去翻配置文件就是把人挡在门外。\n * - 配了(或宿主没告诉我们状态)→ 刷新一次读数(跳过宿主 60 秒缓存)。\n *\n * @param ledger - 当前账本结论(`token` 在里面)。\n * @param store - 共享 store。\n */\nfunction onPillClick(ledger: HudProviderLedger, store: HudStore): void {\n var token = ledger.token\n if (token !== null && token !== undefined && token.configured === false) {\n store.openTokenPanel(\n typeof ledger.provider === 'string' && ledger.provider !== '' ? ledger.provider : '',\n token.ref,\n )\n return\n }\n void store.loadProviderBalance(true)\n}\n\n/** 平台用量页地址(没有就回 `null`:更旧的宿主不给这个链接)。 */\nexport function usageUrlOf(balance: HudBalance): string | null {\n var url = balance.usageUrl\n return typeof url === 'string' && url !== '' ? url : null\n}\n\n/**\n * 在新标签页打开一个外链。\n *\n * 为什么要 try/catch:`window.open` 在假 DOM 与某些沙箱里不存在,而\"打不开链接\"\n * 不该把整枚胶囊的点击路径打崩(它是只读读数,不是导航控件)。\n *\n * @param url - 目标地址。\n * @returns 是否成功发起打开。\n */\nexport function openExternal(url: string): boolean {\n try {\n var opened = (globalThis as { open?: (target: string, name?: string) => unknown }).open\n if (typeof opened === 'function') {\n opened.call(globalThis, url, '_blank')\n return true\n }\n } catch (error) {\n // 落到下面的 false:调用方只知道\"没打开\",不需要知道原因。\n }\n return false\n}\n\n/**\n * 悬停明细:每个钱包一行、赠送余额、变化量、低余额提醒、更新时间,以及失败时那句\"可能已过期\"。\n *\n * 刻意**不写操作说明**(\"点一下刷新\" / \"右键开用量页\"):气泡是读数,不是帮助文档;\n * 两句话就把本来五行的明细撑成七行,还挤掉了真正会变的那几个数。\n *\n * @param balance - 余额快照。\n * @param t - 取词。\n * @returns 多行文本(气泡按 `pre-wrap` 原样显示)。\n */\nexport function balanceTitle(balance: HudBalance, t: Translate): TipLine[] {\n // 抬头(「DeepSeek 账号余额」)用次要色:它是标题,不是读数。\n var lines: TipLine[] = [{ text: t('balance.title'), tone: 'muted' }]\n var wallets = Array.isArray(balance.wallets) ? balance.wallets : []\n for (var index = 0; index < wallets.length; index += 1) {\n var text = formatBalance(wallets[index].balance, wallets[index].currency)\n if (text === null) continue\n // 钱包金额是这一屏的主角:绿色(\"还剩多少\"),不带标签所以整行染色。\n lines.push({\n text: (index === 0 ? text : text + ' (' + wallets[index].currency + ')') as string,\n tone: 'success',\n })\n }\n // 赠送钱包里 0 余额的那些不必露脸(平台会把用完的额度继续挂在列表里)。\n var bonus = visibleBonus(balance)\n if (bonus.length > 0) {\n var parts: string[] = []\n for (var k = 0; k < bonus.length; k += 1) {\n var bonusText = formatBalance(bonus[k].balance, bonus[k].currency)\n if (bonusText !== null) parts.push(bonusText)\n }\n if (parts.length > 0) {\n lines.push({ label: t('balance.bonus'), value: parts.join(' · '), tone: 'muted' })\n }\n }\n // 变化量放在金额之后、时间之前:读的人先看\"现在多少\",再看\"比上次多了/少了多少\"。\n var wallet = primaryWallet({ balance: balance })\n if (wallet !== null) {\n var move = deltaOf(balance, wallet)\n // 变化量是给\"顺手看一眼\"的信息,不是主读数:整行次要色。\n if (move !== null) {\n lines.push({\n text: t('balance.delta', { delta: move.delta, time: fullWhen(move.at) }),\n tone: 'muted',\n })\n }\n // 低余额不在这里写字:`⚠` + 警示色已经在胶囊上说了这件事,气泡不再复述一遍。\n }\n lines.push({ text: t('balance.updated', { time: fullWhen(balance.at) }), tone: 'muted' })\n // 失败时 `at` 还停在上一次成功的时刻:这句把\"这个数是旧的\"说清楚。\n if (balance.failed === true) {\n lines.push({ text: t('balance.stale', { time: fullWhen(balance.at) }), tone: 'muted' })\n }\n return lines\n}\n\n/**\n * 画那枚余额胶囊。\n * @param state - store 快照(只读 `balance`)。\n * @param options - 取词与 store(悬停接线、刷新都经它)。\n * @returns 胶囊节点;没有可展示的余额时 `null`。\n */\nexport function balancePillNode(\n state: { balance?: HudBalance | null; provider?: HudProviderLedger | null },\n options: BalancePillOptions,\n): React.ReactElement | null {\n var t = options.t\n var ledger = state.provider\n\n // ── 会话用的是别的提供方,并且读到了它的余额 ────────────────────────────────\n if (ledger && ledger.mode === 'provider' && ledger.balance !== null) {\n // 右键仍要去平台用量页:那份链接挂在**账号**快照上(提供方那一侧没有),\n // 所以这里把账号快照里的链接单独取出来传下去。\n var accountUsage =\n state.balance && typeof state.balance === 'object' ? usageUrlOf(state.balance) : null\n return providerPillNode(ledger, ledger.balance, accountUsage, options)\n }\n // 知道是哪个提供方但读不到:**绝不退回平台账号的余额**(那是另一个账本的钱)。\n // 但也别干脆消失 —— 消失分不清\"这个站没有余额接口\"和\"插件坏了\"(用户正是这么误判的)。\n // 折中:画成 `ur --`,悬停里写一句为什么。\n if (ledger && ledger.mode === 'unavailable') {\n if (typeof ledger.provider === 'string' && ledger.provider !== '') {\n return unknownProviderPillNode(ledger, options)\n }\n // 连是哪个站都还不知道(新会话的投影还没就绪 / 宿主答不上来):\n // 画 `--`,**不要**拿平台账号的余额顶上 —— 用户明确要求过这一条。\n return placeholderPillNode(ledger, options)\n }\n\n // **明确**处于\"还没问出来\"(`unknown`):画 `--`。\n // 早先这里落到下面的账号那一路,于是新会话会先闪一个官方账号余额 ——\n // 那个数字未必是这次会话要花的钱,等于骗人。\n //\n // 注意与\"根本没有 provider 字段\"区分开:后者是更旧的快照(或 store 还没挂上),\n // 那时没有\"本次会话要用谁的钱\"这个概念,照旧走账号那一路(向后兼容)。\n if (ledger !== undefined && ledger !== null && ledger.mode === 'unknown') {\n return placeholderPillNode(ledger, options)\n }\n\n // ── 平台账号那一路(`account`):宿主明确说\"这个提供方的账本就是平台账号\"────────\n var snapshot = state.balance\n if (!snapshot || typeof snapshot !== 'object') return null\n // 收窄进 local const:下面的悬停闭包里还要用它(`var` 在回调里 TS 不再保留收窄)。\n const balance = snapshot\n var wallet = primaryWallet({ balance: balance })\n if (wallet === null) return null\n var text = formatBalance(wallet.balance, wallet.currency)\n if (text === null) return null\n\n // 只画 `formatBalance` 给的那一串:**不要再额外加一个符号** ——\n // 它自己已经带 `¥` / `$`,早先这里又画了一个字形,屏幕上就成了 `¥ ¥8.60`。\n var low = isLowWallet(wallet)\n var children: React.ReactNode[] = []\n // 低余额:在最前面加一个 `⚠`(不是替换数字 —— 用户还是要知道具体剩多少)。\n if (low) children.push(h('span', { key: 'low', className: 'dshh-balanceWarn' }, '\\u26a0'))\n children.push(\n h(\n 'span',\n { key: 'value', className: 'dshh-balanceValue' + (low ? ' dshh-balanceLow' : '') },\n text,\n ),\n )\n // 有赠送余额就缀一个 `+`:不占地方,但\"还有一笔\"这件事不会被藏起来(明细在悬停里)。\n if (visibleBonus(balance).length > 0) {\n children.push(h('span', { key: 'bonus', className: 'dshh-balanceBonus' }, '+'))\n }\n // 最近一次刷新失败:数字还是上一次的,用 `?` 标出\"可能已过期\"。\n if (balance.failed === true) {\n children.push(h('span', { key: 'stale', className: 'dshh-balanceStale' }, '?'))\n }\n\n probe.log('balance-pill', {\n ledger: 'account',\n currency: wallet.currency,\n failed: balance.failed === true,\n bonus: Array.isArray(balance.bonus) ? balance.bonus.length : 0,\n low: low,\n delta: deltaOf(balance, wallet) === null ? null : 'yes',\n })\n\n return h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill dshh-balance',\n 'data-dsh-hud': 'balance-pill',\n key: 'balance',\n 'aria-label': t('balance.title'),\n onClick: function () {\n options.store.loadBalance(true)\n },\n // 右键 = 去看账目明细。累计消耗 / 本月消耗在平台用量页,DSH 没有代理那份数据,\n // 所以这里只做\"一步跳过去\",并在悬停里写清楚(不然没人会想到去右键)。\n onContextMenu: function (event: {\n preventDefault?: () => void\n stopPropagation?: () => void\n }) {\n var url = usageUrlOf(balance)\n if (url === null) return\n if (event && typeof event.preventDefault === 'function') event.preventDefault()\n if (event && typeof event.stopPropagation === 'function') event.stopPropagation()\n openExternal(url)\n },\n },\n options.store.tipHandlers('balance-pill', function () {\n return balanceTitle(balance, t)\n }),\n ),\n children,\n )\n}\n\n/**\n * 画\"还没有读数\"的占位胶囊:`--`。\n *\n * 为什么不是干脆不画:不画就分不清\"还在查\"和\"插件没装好\"(用户当年正是这么误判的)。\n * 为什么不用平台账号的余额顶上:那是**另一个账本**的钱;新会话还不知道要用谁的钱,\n * 拿官方账号余额去填,等于报一个和本次会话无关的数。\n *\n * 点击照样重取一次 —— 用户点它,通常就是\"怎么还没出来\"。\n *\n * @param ledger - 当前账本结论(可能是 `null`:连结论都还没有)。\n * @param options - 取词与 store。\n * @returns 胶囊节点。\n */\nfunction placeholderPillNode(\n ledger: HudProviderLedger | null,\n options: BalancePillOptions,\n): React.ReactElement {\n var t = options.t\n probe.log('balance-pill', { ledger: 'placeholder', reason: ledger?.reason ?? null })\n var text = t('balance.providerUnknown')\n return h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill dshh-balance dshh-balanceUnknown',\n 'data-dsh-hud': 'balance-pill',\n key: 'balance',\n 'aria-label': text,\n onClick: function () {\n void options.store.loadProviderBalance(true)\n },\n },\n options.store.tipHandlers('balance-pill', function () {\n return text\n }),\n ),\n [h('span', { key: 'value', className: 'dshh-balanceValue' }, text)],\n )\n}\n\n/**\n * 画\"知道是哪个提供方、但读不到它的余额\"的胶囊:`ur --`。\n *\n * 为什么值得画:`—` 是一个**读数**(\"这个账本的数读不到\"),而空白只是一个疑点。\n * 用户看到空白的第一反应是\"插件坏了\",于是去翻日志、重装、重启 —— 那才是真正的成本。\n *\n * @param ledger - 账本结论(含提供方与原因)。\n * @param options - 取词与 store。\n * @returns 胶囊节点。\n */\nfunction unknownProviderPillNode(\n ledger: HudProviderLedger,\n options: BalancePillOptions,\n): React.ReactElement {\n var t = options.t\n var provider = typeof ledger.provider === 'string' ? ledger.provider : '?'\n var reason =\n typeof ledger.reason === 'string' && ledger.reason !== ''\n ? t('balance.reason.' + ledger.reason)\n : ''\n\n probe.log('balance-pill', { ledger: 'unavailable', provider: provider, reason: ledger.reason })\n\n return h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill dshh-balance dshh-balanceUnknown',\n 'data-dsh-hud': 'balance-pill',\n key: 'balance',\n 'aria-label': provider,\n onClick: function () {\n // `ur --` 往往正是\"没配后台令牌\"的样子,所以这里也要能弹面板。\n onPillClick(ledger, options.store)\n },\n },\n options.store.tipHandlers('balance-pill', function () {\n return reason === '' ? provider : provider + '\\n' + reason\n }),\n ),\n [\n h('span', { key: 'provider', className: 'dshh-balanceProvider' }, provider),\n h('span', { key: 'value', className: 'dshh-balanceValue' }, t('balance.providerUnknown')),\n ],\n )\n}\n\n/**\n * 画\"当前提供方\"的余额胶囊(中转站 / 官方 API Key)。\n *\n * 与账号那一路的两处不同:\n * - 没有赠送标记 `+` 那一套区分含义(中转站只报一个数),但官方 Key 带 `bonus`,照旧缀起来;\n * - 只有\"已用\"可画时(站点把额度写成无限占位值)在数字前加一个词,否则读的人会把它当成余额。\n *\n * @param balance - 提供方余额快照。\n * @param options - 取词与 store。\n * @returns 胶囊节点;没有可画的数字时 `null`。\n */\nfunction providerPillNode(\n ledger: HudProviderLedger,\n balance: HudProviderBalance,\n usageUrl: string | null,\n options: BalancePillOptions,\n): React.ReactElement | null {\n var t = options.t\n var figure = providerFigureOf(balance)\n if (figure === null) return null\n var text = amountText(figure.amount, figure.currency)\n if (text === null) return null\n\n // 低余额只在\"这是剩余额度\"时才有意义:拿\"已用\"去比阈值是错的。\n var low =\n figure.kind === 'remaining' && isBelowAmount(figure.amount, lowThresholdOf(BALANCE_LOW_DEFAULT))\n\n var children: React.ReactNode[] = []\n if (low) children.push(h('span', { key: 'low', className: 'dshh-balanceWarn' }, '\\u26a0'))\n // 只有\"已用\"可画时先标一个词:不然 `$442.33` 会被读成余额。\n if (figure.kind === 'used') {\n children.push(\n h('span', { key: 'label', className: 'dshh-balanceUsed' }, t('balance.usedShort')),\n )\n }\n children.push(\n h(\n 'span',\n { key: 'value', className: 'dshh-balanceValue' + (low ? ' dshh-balanceLow' : '') },\n text,\n ),\n )\n if (balance.bonus.length > 0) {\n children.push(h('span', { key: 'bonus', className: 'dshh-balanceBonus' }, '+'))\n }\n\n probe.log('balance-pill', {\n ledger: 'provider',\n provider: balance.provider,\n kind: balance.kind,\n figure: figure.kind,\n placeholder: balance.placeholderLimit,\n low: low,\n })\n\n return h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill dshh-balance',\n 'data-dsh-hud': 'balance-pill',\n key: 'balance',\n 'aria-label': balance.provider,\n onClick: function () {\n onPillClick(ledger, options.store)\n },\n // 右键仍然去平台用量页 —— 那是\"按时间维度看消耗\"的地方;没有链接就什么都不做。\n onContextMenu: function (event: {\n preventDefault?: () => void\n stopPropagation?: () => void\n }) {\n var url = usageUrl\n if (url === null) return\n if (event && typeof event.preventDefault === 'function') event.preventDefault()\n if (event && typeof event.stopPropagation === 'function') event.stopPropagation()\n openExternal(url)\n },\n },\n options.store.tipHandlers('balance-pill', function () {\n return providerBalanceTitle(balance, t)\n }),\n ),\n children,\n )\n}\n","/**\n * 订阅共享 store 的经典写法(不依赖种子里的 `useSyncExternalStore` 构建)。\n */\n\nimport React from 'react'\n\nimport type { HudState, HudStore } from '../shared.js'\n\n/* ---------------------------------------------------------- 订阅 hook */\n\n/** 订阅共享 store 的经典写法(不依赖种子里的 useSyncExternalStore 构建)。 */\nexport function useStore(store: HudStore): HudState {\n var pair = React.useState(function () {\n return store.getSnapshot()\n })\n React.useEffect(\n function () {\n return store.subscribe(function () {\n pair[1](store.getSnapshot())\n })\n },\n [store],\n )\n return pair[0]\n}\n","/**\n * 输入框上方那一行里的胶囊:分支摘要 / 非 git 仓库,点击开关面板;\n * 含两枚工具胶囊、技能清单与悬停气泡的锚点。\n */\n\nimport React from 'react'\n\nimport { balancePillNode } from './balance-pill.js'\nimport { branchLabel, countShorthands, fullWhen, shortBranch, workspaceNameOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudState,\n HudStatusCounts,\n HudStore,\n HudToolActive,\n TipLine,\n ToolsState,\n Translate,\n} from '../shared.js'\nimport {\n COUNT_COLOR,\n COUNT_ORDER,\n SKILLS_SHOWN,\n emphasize,\n formatDuration,\n h,\n probe,\n sessionChangeView,\n} from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\nexport interface SessionSummaryLike {\n cwd?: string | null\n /** 这个会话被哪些视图保留着;`mainView` 为 0 时说明它在内嵌的子会话里。 */\n retainedBy?: { mainView?: number | null } | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\nexport interface SessionsSnapshotLike {\n byId?: Record<string, SessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\nexport type UseSessions = <T>(selector: (snapshot: SessionsSnapshotLike) => T) => T\n\n/** `HudDockPill` 的 props:store + 框架注入的会话作用域信息。 */\nexport interface HudDockPillProps {\n t?: Translate\n /** 当前会话(座位按会话作用域注入)。 */\n sessionId?: string | null\n useSessions: UseSessions\n store: HudStore\n /** 测试/调试用:固定\"现在\",省得等本地计时器。 */\n now?: number\n}\n\n/** 工具胶囊的选项。 */\nexport interface ToolPillOptions {\n t: Translate\n store: HudStore\n now: number\n}\n\n/** 计时胶囊的选项。 */\nexport interface TimerPillOptions {\n t: Translate\n store: HudStore\n sessionId: string | null | undefined\n now: number\n}\n\nexport function HudDockPill(props: HudDockPillProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var store = props.store\n\n // useSessions 是选择器 hook:选择器参数是必需的,裸调用会以\n // \"selector is not a function\" 崩掉。\n var sessions = useSessions(function (snapshot) {\n return snapshot\n })\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n // 右栏里的嵌入式会话(子会话 chat)自带会话绑定;只有主视图座位可以驱动共享\n // 面板,否则一个子会话会把它下面的仓库从用户正在看的会话下抽走。\n // 快照不带这个计数(更旧的宿主)时按主视图放行,胶囊不会因此消失。\n var mainView = !summary || !summary.retainedBy ? true : (summary.retainedBy.mainView || 0) > 0\n\n var state = useStore(store)\n\n /**\n * 工具胶囊的\"实时秒数\"滴答。\n *\n * **必须在组件体里、无条件、固定顺序地调用**:早先放在普通函数 `toolPillNode` 里、还带\n * `if (now === undefined)` 的条件,就是条件钩子 —— React 会抛错,导致整行 dock 渲染失败,\n * 两枚胶囊一起消失(用户实测\"胶囊都不显示了\")。回调里再判断要不要开定时器就没这问题。\n */\n var tick = React.useState(Date.now())\n var tickSet = tick[1]\n var toolsTickKey =\n state.tools && state.tools.active !== null && state.tools.active !== undefined\n ? String(state.tools.active.callId)\n : null\n // 会话计时 + 输入框 ↑ 召回的数据(两个 loader 各自带 TTL,这里按最密的节奏问就行)。\n React.useEffect(\n function () {\n if (typeof sessionId !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`var` 变量在回调里 TS 不再保留)。\n const id = sessionId\n var load = function () {\n if (store.loadSessionTime !== undefined) store.loadSessionTime(id)\n if (store.loadPromptHistory !== undefined) store.loadPromptHistory(id)\n }\n // 换了会话就换了账本:余额胶囊要跟着会话的模型来源走(中转站 / 官方 Key / 平台账号)。\n if (store.loadProviderBalance !== undefined) store.loadProviderBalance()\n load()\n var poll = window.setInterval(load, 3000)\n return function () {\n window.clearInterval(poll)\n }\n },\n [sessionId],\n )\n\n // 挂钟每 4 秒跳一次:计时胶囊的显示粒度是分钟,但跨分钟不用等下一次取数。\n React.useEffect(function () {\n var clock = window.setInterval(function () {\n tickSet(Date.now())\n }, 4000)\n return function () {\n window.clearInterval(clock)\n }\n }, [])\n\n React.useEffect(\n function () {\n if (toolsTickKey === null) return undefined\n var timer = window.setInterval(function () {\n tickSet(Date.now())\n }, 500)\n return function () {\n window.clearInterval(timer)\n }\n },\n [toolsTickKey],\n )\n\n React.useEffect(\n function () {\n if (!mainView) return\n store.bindSession(sessionId, cwd)\n },\n [sessionId, cwd, mainView, store],\n )\n\n if (!cwd || !mainView || state.sessionId !== sessionId) return null\n\n // 会话归因的胶囊段:只有\"确实改过东西\"时才露头(没数据 / 0 个都不显示)。\n var sessionView = sessionChangeView(state)\n var sessionSegment =\n sessionView !== null && sessionView.mineCount > 0\n ? ' │ ●' + String(sessionView.mineCount)\n : null\n\n var children: React.ReactNode[] = []\n // 工作区名放在最前、且与分支用 `·` 隔开:写成 `git:(dsh-hud/main*)` 会被读成\"分支叫 dsh-hud/main\"。\n // 三种\"非仓库\"状态也带上它 —— 工作区跟 git 没关系,没仓库时它反而更有用。\n var workspace = workspaceNameOf(cwd)\n if (workspace !== null) {\n children.push(h('span', { key: 'workspace', className: 'dshh-workspace' }, workspace))\n children.push(h('span', { key: 'workspaceSep', className: 'dshh-workspaceSep' }, ' · '))\n }\n if (state.phase === 'error') {\n children.push(\n h('span', { key: 'gitWraperr', className: 'dshh-gitWrap' }, 'git:(' + t('pill.error') + ')'),\n )\n } else if (state.phase === 'loading' || state.phase === 'idle') {\n children.push(\n h(\n 'span',\n { key: 'gitWrapload', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.loading') + ')',\n ),\n )\n } else if (!state.status || !state.status.repo) {\n children.push(\n h(\n 'span',\n { key: 'gitWrapnorepo', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.notRepo') + ')',\n ),\n )\n } else {\n var status = state.status\n // `git:(branch*)` —— codex-hud 的 gitSegment 就是这么拼的,配色也照它:\n // `git:(` 与 `)` 品红(colors.git)、分支名与 ↑↓ 青(colors.gitBranch),\n // 而 `*` 在括号**里面**、与分支名同色。\n children.push(h('span', { key: 'gitOpen', className: 'dshh-gitWrap' }, 'git:('))\n children.push(\n h(\n 'span',\n { key: 'branch', className: 'dshh-branch' },\n shortBranch(branchLabel(status, t)) + (status.dirty ? '*' : ''),\n ),\n )\n children.push(h('span', { key: 'gitClose', className: 'dshh-gitWrap' }, ')'))\n if (status.ahead > 0)\n children.push(\n h('span', { key: 'ahead', className: 'dshh-ahead' }, ' ↑' + String(status.ahead)),\n )\n if (status.behind > 0)\n children.push(\n h('span', { key: 'behind', className: 'dshh-ahead' }, ' ↓' + String(status.behind)),\n )\n\n // 简写符:只列非零项,按固定顺序,各自上色(M 黄 / A 绿 / D 红 / ? 灰 …)。\n // 与侧栏行内 chip 共用 `countShorthands` —— 两处各写一套算法一定会漂。\n var shorthands = countShorthands(status.counts)\n for (var index = 0; index < shorthands.length; index += 1) {\n children.push(' ')\n children.push(\n h(\n 'span',\n {\n key: shorthands[index].key,\n style: { color: COUNT_COLOR[shorthands[index].key] },\n },\n shorthands[index].mark + String(shorthands[index].value),\n ),\n )\n }\n }\n\n if (sessionSegment !== null) children.push(sessionSegment)\n\n // 悬停给出完整信息:总数型的托盘里放不下细节,这里补齐。\n // 读数(数字)要高亮,但路径、分支名、上游名这些不该染 —— 所以逐行决定怎么切段。\n var titleLines: TipLine[] = [{ text: t('pill.title'), tone: 'muted' }]\n if (sessionView !== null) {\n titleLines.push({\n segments: emphasize(t('session.pill', { n: String(sessionView.mineCount) }), [\n String(sessionView.mineCount),\n ]),\n })\n }\n // cwd 是路径不是读数:整行次要色(染路径等于满行是色)。\n if (cwd) titleLines.push({ text: cwd, tone: 'muted' })\n if (state.status && state.status.repo) {\n var tip = branchLabel(state.status, t)\n if (state.status.upstream) tip += ' · ' + t('panel.upstream') + ' ' + state.status.upstream\n titleLines.push({ text: tip })\n var labels: string[] = []\n var countReadings: string[] = []\n var counts2: Partial<HudStatusCounts> = state.status.counts || {}\n for (var i2 = 0; i2 < COUNT_ORDER.length; i2 += 1) {\n var k2 = COUNT_ORDER[i2]\n var v2 = counts2[k2] || 0\n if (v2 > 0) {\n labels.push(t('count.' + k2) + ' ' + String(v2))\n countReadings.push(String(v2))\n }\n }\n if (labels.length > 0) {\n // 领先/落后那两个数也要高亮,所以和改动计数一起交给 `emphasize`。\n var aheadBehind = aheadBehindReadings(state.status)\n titleLines.push({\n segments: emphasize(labels.join(' · '), countReadings.concat(aheadBehind)),\n })\n }\n }\n\n // 外层容器复刻框架自己的 dock 行几何(见 queue 公告):仅靠胶囊自身对齐,\n // 胶囊会飘到整栏的最左边,而输入卡片是居中的。\n return h(\n 'div',\n { className: 'dshh-dock', 'data-dsh-hud': 'dock' },\n h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill',\n 'data-dsh-hud': 'pill',\n 'aria-expanded': state.panelOpen ? 'true' : 'false',\n style: { color: state.panelOpen ? 'var(--dsw-alias-brand-primary, #4b8bf5)' : undefined },\n onClick: function () {\n store.togglePanel()\n },\n },\n store.tipHandlers('pill', function () {\n return titleLines\n }),\n ),\n children,\n ),\n // 工具活动那一枚并排在后面 —— dock 是竖列,独立注册会另起一整行(用户实测\"换行了\")。\n toolPillNode(state, { t: t, store: store, now: props.now === undefined ? tick[0] : props.now }),\n // 余额在计时之前:余额是会变的\"钱\",计时是一直在涨的时长 —— 让读数靠前、节拍感靠后。\n // 读不到时 `balancePillNode` 自己回 null(不占位)。\n balancePillNode(state, { t: t, store: store }),\n // 计时胶囊放在**最后**(几枚同在一条注册里 → 同一行;独立注册会另起一整行)。\n timerPillNode(state, {\n t: t,\n store: store,\n sessionId: sessionId,\n now: props.now === undefined ? tick[0] : props.now,\n }),\n )\n}\n\nexport function toolPillNode(state: HudState, options: ToolPillOptions): React.ReactElement | null {\n var t = options.t\n var tools = state.tools\n if (!tools || (tools.active === null && tools.total === 0)) return null\n var now = options.now\n\n var children: React.ReactNode[] = []\n if (tools.active !== null) {\n // 配色对齐 codex-hud 的 activity-lines:`◐` 黄 + 工具名青 + 目标/耗时用默认正文色。\n var elapsed = toolElapsedLabel(tools.active, now)\n children.push(\n h('span', { key: 'glyph', className: 'dshh-toolsGlyph dshh-toolsGlyphRun' }, '\\u25d0'),\n )\n children.push(\n h(\n 'span',\n { key: 'name', className: 'dshh-toolsName' },\n ' ' + String(tools.active.name === undefined ? '?' : tools.active.name),\n ),\n )\n // 带上参数摘要(codex-hud 就是 `◐ exec_command: pnpm test`)—— 技能调用因此能看出是哪个技能。\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0) {\n children.push(': ' + tools.active.summary)\n }\n if (elapsed !== null) children.push(' ' + elapsed)\n } else {\n // 空闲时把**每个工具各自几次**铺出来(codex-hud 那条 `Tools:` 就是这么给的):\n // `✓ bash ×16 │ ✓ apply_patch ×6 │ ✗ write_stdin ×3`。名字与数字贴在一起,\n // 就不会再有\"×54 是总数还是这个工具的次数\"那种误读。\n //\n // 最多铺 3 个(按次数排序,宿主已经排好),其余收成 `+k` —— 悬停里有完整清单与最近几条。\n // `skill` 工具本身对用户没信息量(`skill ×2` 说明不了是哪个技能)—— codex-hud 也是直接列技能名。\n // 所以把它从计数里剔掉,改由下面那段技能清单承担;总次数不受影响。\n var toolCounts = tools.counts.filter(function (item) {\n return item.name !== 'skill'\n })\n var shown = toolCounts.slice(0, 3)\n for (var i = 0; i < shown.length; i += 1) {\n var item = shown[i]\n var bad = item.ok === false\n if (i > 0) children.push(' \\u2502 ')\n children.push(\n h(\n 'span',\n {\n key: 'glyph' + String(i),\n className: 'dshh-toolsGlyph' + (bad ? ' dshh-toolsGlyphBad' : ' dshh-toolsGlyphOk'),\n },\n bad ? '\\u2717' : '\\u2713',\n ),\n )\n children.push(' ' + String(item.name) + ' \\u00d7' + String(item.count))\n }\n // 技能清单:名字直接铺出来(\"会话用了哪些 skill\"要能一眼看到,不只是次数)。\n // 标签用字面的 `skills:`(不翻译)—— 与 `git:(` 同一套:它是约定写法,不是描述。\n var skills = tools.skills || []\n if (skills.length > 0) {\n if (children.length > 0) children.push(' \\u2502 ')\n children.push(t('tools.skills') + ' ')\n for (var k = 0; k < skills.length && k < SKILLS_SHOWN; k += 1) {\n if (k > 0) children.push(' \\u00b7 ')\n children.push(\n h(\n 'span',\n {\n key: 'skill' + String(k),\n className: skills[k].ok === false ? 'dshh-toolsGlyphBad' : 'dshh-skillName',\n },\n String(skills[k].name) + ' \\u00d7' + String(skills[k].count),\n ),\n )\n }\n if (skills.length > SKILLS_SHOWN) children.push(' +' + String(skills.length - SKILLS_SHOWN))\n if (tools.skillsDropped > 0) children.push(' +' + String(tools.skillsDropped))\n }\n if (toolCounts.length > shown.length)\n children.push(' +' + String(toolCounts.length - shown.length))\n }\n\n // 渲染现场:画没画、画的是哪一档(与 row-chip/row-token 同一套排查手段)。\n probe.log('tools-pill', {\n active: tools.active === null ? null : String(tools.active.name),\n total: tools.total,\n })\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-toolsPill',\n 'data-dsh-hud': 'tools-pill',\n key: 'tools',\n },\n options.store.tipHandlers('tools-pill', function () {\n return toolsTitle(tools, t)\n }),\n ),\n children,\n )\n}\n\nexport function timerPillNode(\n state: HudState,\n options: TimerPillOptions,\n): React.ReactElement | null {\n var t = options.t\n var sessionId = options.sessionId\n var now = options.now\n var snapshot = state.sessionTime\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId === sessionId\n ? snapshot.value\n : null\n // 会话不在(还没恢复进存储、已归档、子会话已回收)、宿主服务缺失:不画这枚胶囊,也不占位。\n if (value === null || value.available !== true) return null\n var createdAt = value.createdAt\n if (typeof createdAt !== 'number') return null\n\n var age = Math.max(0, now - createdAt)\n // 整句话不变(用户没要求改排版),只把里面的**读数**挑出来高亮:\n // 创建时间留灰(时间戳染上就等于满屏是色,重点反而没了),时长/轮/步上色。\n var readings: string[] = []\n var hints: string[] = [t('timer.title'), t('timer.created', { time: fullWhen(createdAt) })]\n if (typeof value.llmMs === 'number' && typeof value.toolMs === 'number') {\n var llmText = formatDuration(value.llmMs)\n var toolText = formatDuration(value.toolMs)\n readings.push(llmText, toolText)\n hints.push(t('timer.work', { llm: llmText, tool: toolText }))\n }\n if (typeof value.turns === 'number' && typeof value.steps === 'number') {\n readings.push(String(value.turns), String(value.steps))\n hints.push(t('timer.turns', { turns: String(value.turns), steps: String(value.steps) }))\n }\n probe.log('timer-pill', { age: Math.round(age / 60000) })\n var tipKey = 'session-timer'\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-timer',\n 'data-dsh-hud': 'session-timer',\n key: 'timer',\n },\n options.store.tipHandlers(tipKey, function () {\n // 一句一行、用 `·` 连起来(原样式),只把数字染成强调色。\n return [{ segments: emphasize(hints.join(' · '), readings) }]\n }),\n ),\n [\n h('span', { key: 'glyph', className: 'dshh-timerGlyph' }, '⏱'),\n h('span', { key: 'value', className: 'dshh-timerValue' }, formatDuration(age)),\n ],\n )\n}\n\nexport function toolsTitle(tools: ToolsState, t: Translate): TipLine[] {\n // 每一行都是\"一句白话里夹着读数\":句式不动,只把**数字**挑出来高亮。\n var readings: string[] = []\n function countUnit(): void {\n readings.push(String(tools.total))\n }\n var lines: TipLine[] = []\n if (tools.active !== null) {\n lines.push({ text: t('tools.now', { name: String(tools.active.name) }), tone: 'muted' })\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0)\n lines.push({ text: tools.active.summary, tone: 'muted' })\n }\n var totalText = t('tools.total', { n: String(tools.total) })\n countUnit()\n lines.push({ segments: emphasize(totalText, readings) })\n if (tools.counts.length > 0) {\n var byTool = t('tools.byTool') + ' '\n lines.push({\n segments: emphasize(\n byTool +\n tools.counts\n .map(function (item) {\n return String(item.name) + ' \\u00d7' + String(item.count)\n })\n .join(' \\u00b7 '),\n tools.counts.map(function (item) {\n return '\\u00d7' + String(item.count)\n }),\n ),\n })\n }\n if (tools.skills && tools.skills.length > 0) {\n lines.push({\n segments: emphasize(\n t('tools.skills') +\n ' ' +\n tools.skills\n .map(function (item) {\n return (\n String(item.name) +\n ' \\u00d7' +\n String(item.count) +\n (item.ok === false ? ' (\\u2717)' : '')\n )\n })\n .join(' \\u00b7 '),\n tools.skills.map(function (item) {\n return '\\u00d7' + String(item.count)\n }),\n ),\n })\n }\n var recent = tools.recent.slice(0, 3)\n if (recent.length > 0) {\n lines.push({ text: t('tools.recent'), tone: 'muted' })\n var recentReadings: string[] = []\n for (var at = 0; at < recent.length; at += 1) {\n var entry = recent[at]\n var mark = entry.ok === false ? '\\u2717' : '\\u2713'\n var ms = typeof entry.ms === 'number' ? ' ' + (entry.ms / 1000).toFixed(1) + 's' : ''\n if (ms !== '') recentReadings.push(ms.trim())\n var summary =\n typeof entry.summary === 'string' && entry.summary.length > 0\n ? ' \\u00b7 ' + entry.summary\n : ''\n lines.push({\n segments: emphasize(mark + ' ' + String(entry.name) + ms + summary, recentReadings),\n })\n }\n }\n return lines\n}\n\n/**\n * `↑2 ↓1` 这类领先/落后标记里的数字(给悬停高亮用)。\n *\n * 直接扫字符串而不是重算一遍 git 语义:这里要的只是\"已经画出去的那几个数\"。\n */\nfunction aheadBehindReadings(status: { ahead?: number; behind?: number }): string[] {\n var out: string[] = []\n if (typeof status.ahead === 'number' && status.ahead > 0) out.push(String(status.ahead))\n if (typeof status.behind === 'number' && status.behind > 0) out.push(String(status.behind))\n return out\n}\n\nexport function toolElapsedLabel(active: HudToolActive | null, now: number): string | null {\n if (active === null || typeof active !== 'object') return null\n if (typeof active.startedAt !== 'number') return null\n var seconds = Math.max(0, (now - active.startedAt) / 1000)\n return seconds < 10 ? seconds.toFixed(1) + 's' : String(Math.round(seconds)) + 's'\n}\n","/**\n * 「本会话改过哪些文件」那一行的展示:标题、审阅标记、回合标签。\n *\n * 数据来自 store 的会话归因,这里只负责把事实排成一行字。\n */\n\nimport type React from 'react'\n\nimport { timeLabel } from '../format.js'\nimport type {\n ReviewMark,\n ReviewView,\n SessionChangeView,\n SessionCommand,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `SessionChangesLine` 的 props:归因视图 + 审阅进度 + 三枚开关。 */\nexport interface SessionChangesLineProps {\n t: Translate\n /** 会话归因视图;`null` = 这份数据还没到手(这一行只剩开关)。 */\n view: SessionChangeView | null\n /** 「只看本会话改的」开关状态。 */\n onlySession?: boolean\n /** 「只看最近一轮」开关状态。 */\n onlyLatest?: boolean\n /** 审阅进度;`null` / 缺省 = 这次没要签名。 */\n review?: ReviewView | null\n onToggle: () => void\n onToggleLatest: () => void\n onClearReviewed: () => void\n}\n\n/**\n * 改动概览下面那一行:本会话改了几个文件 + 「只看本会话改的」开关。\n *\n * 归因数据没到手(`view === null`)时整行不渲染 —— 不显示一个\"全部都是别人的\"的假象。\n */\nexport function SessionChangesLine(props: SessionChangesLineProps): React.ReactElement {\n var t = props.t\n var view = props.view\n var review = props.review || null\n // 这一行**总是渲染**:它带着三枚开关(只看本会话改的 / 侧栏档位 / 只看最近一轮),\n // 归因或审阅数据缺失时只是没有计数可报 —— 控件不能因为\"数据还没到\"就凭空消失。\n var notes: string[] = []\n if (view !== null) {\n notes.push(t('session.line', { n: String(view.mineCount) }))\n // 回合信息拿得到时,\"最近一轮改了几个\"是最常问的那个问题,直接写在行上。\n if (view.latestTurn !== null && view.latestCount > 0) {\n notes.push(\n t('session.latestTurn', { n: String(view.latestCount), turn: String(view.latestTurn + 1) }),\n )\n }\n if (view.possibleCount > 0) notes.push(t('session.possible', { n: String(view.possibleCount) }))\n }\n if (view !== null) {\n if (view.gone.length > 0) notes.push(t('session.gone', { n: String(view.gone.length) }))\n if (view.dropped > 0) notes.push(t('session.dropped', { n: String(view.dropped) }))\n if (view.possibleDropped > 0)\n notes.push(t('session.possibleDropped', { n: String(view.possibleDropped) }))\n }\n if (props.review && props.review.total > 0) {\n notes.push(\n t('review.count', { n: String(props.review.done), total: String(props.review.total) }),\n )\n }\n if (props.review && props.review.staleCount > 0)\n notes.push(t('review.staleCount', { n: String(props.review.staleCount) }))\n return h(\n 'div',\n { className: 'dshh-session', 'data-dsh-hud': 'session' },\n // 没有任何可报的计数时,文字位置留空(开关照旧在右边)。\n notes.length === 0\n ? null\n : h(\n 'span',\n { className: 'dshh-sessionText', title: sessionLineTitle(view, t) },\n notes.join(' · '),\n ),\n h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlySession === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'only',\n 'aria-pressed': props.onlySession === true ? 'true' : 'false',\n title: t('session.onlyTitle'),\n onClick: props.onToggle,\n },\n props.onlySession === true ? '◉ ' : '○ ',\n t('session.only'),\n ),\n // 已经把某些文件标成\"已审\"时,给一个\"清空\"的出口(只清当前工作区)。\n props.review && props.review.done > 0\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-sessionToggle',\n 'data-session-review': 'clear',\n title: t('review.clear'),\n onClick: props.onClearReviewed,\n },\n t('review.clearShort'),\n )\n : null,\n // 第二枚开关只在\"这一轮的归属拿得到\"时出现:拿不到就整枚不画,别给一个按不动的按钮。\n view === null || view.latestTurn === null\n ? null\n : h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlyLatest === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'latest',\n 'aria-pressed': props.onlyLatest === true ? 'true' : 'false',\n title: turnLabel(view, view.latestTurn, t) + '\\n' + t('session.onlyLatestTitle'),\n onClick: props.onToggleLatest,\n },\n props.onlyLatest === true ? '◉ ' : '○ ',\n t('session.onlyLatest'),\n ),\n )\n}\n\n/** 一个文件属于哪一档:`mine` 确定改过 / `possible` 命令旁听 / `other` 与本会话无关。 */\nexport function sessionMarkOf(\n view: SessionChangeView,\n file: string,\n): 'mine' | 'possible' | 'other' {\n if (view.mine !== undefined && view.mine[file] !== undefined) return 'mine'\n if (view.possible !== undefined && view.possible[file] !== undefined) return 'possible'\n return 'other'\n}\n\nexport function sessionTitle(view: SessionChangeView, file: string, t: Translate): string {\n var record = view.mine !== undefined ? view.mine[file] : undefined\n if (record === undefined) {\n var guess = view.possible !== undefined ? view.possible[file] : undefined\n if (guess === undefined) return t('session.other')\n // 低一档的置信度:说清是\"从命令里看出来的\",并把那条命令贴出来。\n return t('session.possibleTitle', { reason: String(guess.reason || '').slice(0, 80) })\n }\n var parts: string[] = []\n if (record.writes > 0) parts.push(t('session.writes', { n: String(record.writes) }))\n if (record.edits > 0) parts.push(t('session.edits', { n: String(record.edits) }))\n var when = typeof record.lastAt === 'number' ? timeLabel(record.lastAt) : ''\n var line =\n t('session.mine') +\n (parts.length === 0 ? '' : '(' + parts.join('、') + ')') +\n (when ? ' ' + when : '')\n // 回合是\"这一轮改了什么\"的锚点:有就一并说清是哪一轮、那一轮的请求是什么。\n var turn = typeof record.lastTurn === 'number' ? record.lastTurn : null\n if (turn !== null) line += '\\n' + turnLabel(view, turn, t)\n return line\n}\n\n/** 行尾那个标记的悬停说明:三种状态各说清下一步。 */\nexport function reviewTitle(state: ReviewMark | undefined, t: Translate): string {\n if (state === 'fresh') return t('review.unmark')\n if (state === 'stale') return t('review.stale')\n return t('review.mark')\n}\n\n/**\n * 那一行的悬停说明:自哪一刻起记录 + 跑过哪些命令。\n *\n * \"可能相关\"是从命令里看出来的,用户要能自己判断 —— 所以把命令原文贴出来,而不是只报一个数。\n */\nexport function sessionLineTitle(view: SessionChangeView | null, t: Translate): string | undefined {\n // 没有归因数据(那一行只剩审阅进度)时没有\"自 … 起记录\"也没有命令可列。\n if (view === null) return undefined\n var lines: string[] = []\n if (view.since !== null) lines.push(t('session.since', { time: timeLabel(view.since) }))\n var commands = (view.commands || []).slice(-5).reverse()\n if (commands.length > 0) {\n lines.push(t('session.commands', { n: String(commands.length) }))\n for (var index = 0; index < commands.length; index += 1) {\n var item: Partial<SessionCommand> = commands[index] || {}\n lines.push(\n '· ' +\n String(item.command || '') +\n (item.failed === true ? ' ' + t('session.commandFailed') : ''),\n )\n }\n if (view.writingCommands > commands.length) {\n lines.push(t('session.commandsMore', { n: String(view.writingCommands - commands.length) }))\n }\n }\n return lines.length === 0 ? undefined : lines.join('\\n')\n}\n\n/**\n * 清单里那一行的悬停说明:这行是不是本会话改的。\n *\n * `mine` 里有这条就把写入/编辑次数与时间区间说清楚;没有就直说\"不是这个会话改的\"\n * (可能是你自己、编辑器、或另一个会话)。\n */\n/**\n * 一轮的说明:`第 3 轮:<那一轮的请求预览>`。\n *\n * 回合号从宿主的 `turnBoundary` 投影来,请求预览从 `turnOutline` 投影来 —— 拿不到 prompt\n * 时只说\"第 N 轮\",不编内容。\n */\nexport function turnLabel(view: SessionChangeView, turn: number, t: Translate): string {\n var prompt =\n view.turnPrompts !== undefined && view.turnPrompts !== null ? view.turnPrompts[turn] : undefined\n if (typeof prompt !== 'string' || prompt.length === 0)\n return t('session.turn', { n: String(turn + 1) })\n return t('session.turnWithPrompt', { n: String(turn + 1), prompt: prompt })\n}\n","/**\n * 小段 DOM 判定与定位:焦点在哪、点在不在面板里、差异行怎么滚进视野。\n *\n * 数量少但被 hooks 与组件共用,所以单独一层,避免它们互相 import。\n */\n\n/**\n * 本层用到的 DOM 子集(结构类型)。\n *\n * 为什么不用 `Element` / `instanceof Element`:测试里的假 DOM 是普通对象(没有 Element 类),\n * 用 instanceof 会把那些用例全判成\"没有 DOM\"。这里只关心下面几个字段,按结构收窄即可;\n * 外部传进来的值(事件目标、查询结果)都从 `unknown` 收一次 —— 这就是系统边界。\n */\ninterface DomNodeLike {\n getAttribute?: (name: string) => string | null\n parentNode?: DomNodeLike | null\n tagName?: string\n isContentEditable?: boolean\n className?: string\n scrollTop?: number\n offsetTop?: number\n offsetHeight?: number\n clientHeight?: number\n getBoundingClientRect?: () => { top: number }\n}\n\n/** 把未知值收窄成\"结构上像个 DOM 节点\";不是对象就给 null。 */\nfunction asDomNode(value: unknown): DomNodeLike | null {\n return value !== null && typeof value === 'object' ? (value as DomNodeLike) : null\n}\n\n/** 取当前焦点节点;没有 document / activeElement 时给 null。 */\nexport function activeElement(): Element | null {\n if (typeof document === 'undefined') return null\n return document.activeElement || null\n}\n\n/** 事件目标是否落在面板里(走自己的 `data-dsh-hud=\"panel\"` 标记,不碰产品 DOM)。 */\nexport function isInsidePanel(node: unknown): boolean {\n return isInsideMarked(node, 'panel')\n}\n\n/** 事件目标是否落在某个 `data-dsh-hud` 标记的子树里(自己的标记,不碰产品的选择器)。 */\nexport function isInsideMarked(node: unknown, mark: string): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 64) {\n var attribute =\n typeof current.getAttribute === 'function' ? current.getAttribute('data-dsh-hud') : null\n if (attribute === mark) return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/**\n * 事件目标是不是\"正在输入\"的地方。\n *\n * 产品的输入框是 contenteditable,所以不能只看 tagName;两种都认。\n */\nexport function isTypingTarget(node: unknown): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 8) {\n var tag = typeof current.tagName === 'string' ? current.tagName.toLowerCase() : ''\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true\n if (current.isContentEditable === true) return true\n var editable =\n typeof current.getAttribute === 'function' ? current.getAttribute('contenteditable') : null\n if (editable === '' || editable === 'true' || editable === 'plaintext-only') return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/** 用自己写在节点上的标记找节点;找不到或不支持就是 null。 */\nexport function findMarked(selector: string): Element | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n return document.querySelector(selector)\n}\n\n/**\n * 把一个带自己标记的节点滚进它所在滚动容器的视野(最小滚动量)。\n *\n * 全走能力判断:假 DOM(测试)里没有 offsetTop / clientHeight,这个函数直接\n * no-op,测试断言的是状态与结构,不是像素。不用 `scrollIntoView`:那会连带\n * 滚动祖先容器(面板是浮层,滚了产品的会话列表就不好了)。\n */\nexport function revealRow(node: unknown, containerClassName: string): void {\n var start = asDomNode(node)\n if (!start || typeof start.parentNode === 'undefined') return\n var container = asDomNode(start.parentNode)\n var depth = 0\n while (container && depth < 32) {\n if (String(container.className || '').indexOf(containerClassName) >= 0) break\n container = asDomNode(container.parentNode)\n depth += 1\n }\n if (!container || typeof container.scrollTop !== 'number') return\n var top = start.offsetTop\n var height = start.offsetHeight\n var view = container.clientHeight\n if (typeof top !== 'number' || typeof height !== 'number' || typeof view !== 'number') return\n if (top < container.scrollTop) container.scrollTop = top\n else if (top + height > container.scrollTop + view) container.scrollTop = top + height - view\n}\n\n/* ------------------------------------------------------- 悬浮面板 */\n\nexport function measureDockTop(): number | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n var node = document.querySelector('[data-dsh-hud=\"dock\"]')\n if (!node || typeof node.getBoundingClientRect !== 'function') return null\n var rect = node.getBoundingClientRect()\n return typeof rect.top === 'number' ? rect.top : null\n}\n","/**\n * 改动清单(面板左栏):分组、计数、每行的状态标记,以及只显示本会话/最近一轮的过滤。\n */\n\nimport { statusKey } from '../format.js'\nimport React from 'react'\n\nimport { reviewTitle, sessionMarkOf, sessionTitle } from './session-changes.js'\nimport { revealRow } from '../dom.js'\nimport type { HudChange, ReviewView, SessionChangeView, Translate } from '../shared.js'\nimport { COUNT_COLOR, h } from '../shared.js'\n\n/** `ChangeList` 的 props:清单本体 + 会话归因/审阅两列标记 + 交互回调。 */\nexport interface ChangeListProps {\n /** 要渲染的清单:工作区那份是 `HudChange`,提交明细那份是 `HudCommitFile`(结构兼容)。 */\n changes?: HudChange[] | null\n /** 正在看的那份差异对应的文件(`null` = 没打开差异)。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 两份清单同时在屏幕上时要区分\"哪一份\"(滚进视野时按它找行)。 */\n listId?: string\n /** 会话归因视图;`null` = 这份数据还没到手(整列不画)。 */\n sessionView?: SessionChangeView | null\n /** 审阅进度;`null` = 这次没要签名(胶囊那一路的轮询就是这样)。 */\n review?: ReviewView | null\n /** 清单被截断时的那句脚注。 */\n truncated?: boolean\n /** 自定义标题(提交明细那栏用);不给自己拼一个带计数的。 */\n title?: string\n t: Translate\n /** 点一行:打开该文件的差异视图。 */\n onOpen: (change: HudChange) => void\n /** 点行尾审阅标记:切换\"这一版审过了\"(不画审阅列的那份清单可以不给)。 */\n onToggleReviewed?: (file: string) => void\n}\n\nexport function ChangeList(props: ChangeListProps): React.ReactElement | null {\n var changes = props.changes || []\n var activeFile = props.activeFile\n var cursorFile = props.cursor || null\n // 提交明细打开时,屏幕上会同时有两份清单(左栏工作区 + 右栏这次提交),\n // 所以行标记必须带上是哪一份,否则\"把选中行滚进视野\"会滚错栏。\n var listId = props.listId || 'changes'\n\n // 键盘选中(或正在看)的那行可能被滚出视野:把它带回来。\n React.useEffect(\n function () {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return\n var wanted = cursorFile || activeFile\n if (!wanted) return\n for (var index = 0; index < changes.length; index += 1) {\n if (changes[index].file !== wanted) continue\n var node = document.querySelector(\n '[data-dsh-hud=\"row\"][data-row-list=\"' +\n listId +\n '\"][data-row-index=\"' +\n String(index) +\n '\"]',\n )\n revealRow(node, 'dshh-list')\n return\n }\n },\n [cursorFile, activeFile, changes.length, listId],\n )\n\n if (changes.length === 0) return null\n\n // 会话归因标记:只在这份数据到手时才有(`sessionView` 为 null 就整列不画)。\n var sessionView = props.sessionView === undefined ? null : props.sessionView\n // 审阅进度:`null` 表示这次没要签名(胶囊那一路的轮询就是这样),整列不画。\n var review = props.review === undefined ? null : props.review\n\n var rows = changes.map(function (change, index) {\n // 整行可点:打开该文件的差异视图(只读,不改动任何东西)。\n // \"正在看的那行\"和\"键盘选中的那行\"可能是两行(选了还没打开),因此两种标记分开。\n return h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-row' +\n (activeFile && change.file === activeFile ? ' dshh-rowActive' : '') +\n (cursorFile && change.file === cursorFile ? ' dshh-rowCursor' : ''),\n key: String(index) + ':' + change.path,\n 'data-dsh-hud': 'row',\n 'data-row-list': listId,\n 'data-row-index': String(index),\n title:\n change.path +\n (sessionView === null ? '' : '\\n' + sessionTitle(sessionView, change.file, props.t)) +\n '\\n' +\n props.t('panel.clickForDiff') +\n '\\n' +\n props.t('panel.rowKeys'),\n onClick: function () {\n props.onOpen(change)\n },\n },\n sessionView === null\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-marker' +\n (sessionMarkOf(sessionView, change.file) === 'mine'\n ? ''\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? ' dshh-markerPossible'\n : ' dshh-markerOther'),\n 'data-session-mark': sessionMarkOf(sessionView, change.file),\n 'aria-hidden': 'true',\n },\n sessionMarkOf(sessionView, change.file) === 'mine'\n ? '●'\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? '◐'\n : '○',\n ),\n h(\n 'span',\n {\n className: 'dshh-status',\n style: {\n color:\n COUNT_COLOR[statusKey(change.status)] || 'var(--dsw-alias-label-secondary, #8a8f98)',\n },\n },\n change.status,\n ),\n h('span', { className: 'dshh-path' }, change.path),\n review === null || review.known === undefined || review.known[change.file] !== true\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-review' +\n (review.marks[change.file] === 'fresh' ? ' dshh-reviewOn' : '') +\n (review.marks[change.file] === 'stale' ? ' dshh-reviewStale' : ''),\n 'data-review':\n review.marks[change.file] === undefined ? 'none' : review.marks[change.file],\n title: reviewTitle(review.marks[change.file], props.t),\n onClick: function (event: React.MouseEvent<HTMLSpanElement>) {\n // 行本身是按钮(点开差异):这里必须拦住,否则标记一下就顺手打开了差异。\n event.stopPropagation()\n event.preventDefault()\n // 只有画了审阅列的清单才会渲染这枚标记,所以回调不给时这里也不会被走到。\n if (props.onToggleReviewed) props.onToggleReviewed(change.file)\n },\n },\n review.marks[change.file] === undefined ? '○' : '✓',\n ),\n )\n })\n\n var footer = props.truncated\n ? h('div', { className: 'dshh-note' }, props.t('panel.truncated'))\n : null\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h(\n 'div',\n { className: 'dshh-sectionTitle' },\n props.title || props.t('panel.changes') + ' · ' + String(changes.length),\n ),\n h('div', { className: 'dshh-list' }, rows),\n footer,\n )\n}\n","/**\n * 最近提交清单(面板中间那一栏):一条一行,短 sha + 标题 + HEAD 徽标 + 时刻。\n */\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudLogEntry, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `CommitList` 的 props:最近提交清单 + 打开回调 + 高亮的那一条。 */\nexport interface CommitListProps {\n /** 最近提交;`null` / 空表时整段不画。 */\n commits?: HudLogEntry[] | null\n t: Translate\n /** 提交明细当前打开的那条(短 sha 与完整 sha 前缀匹配)。 */\n activeSha?: string | null\n /** 点一行:打开/收起这次提交的明细。 */\n onOpen?: ((commit: HudLogEntry) => void) | null\n}\n\n/**\n * 最近提交:一行一条,sha + 标题 + HEAD 徽标 + 相对时间;整行可点看这次提交改了什么。\n * 打开的那条会高亮(再点一次收起)——提交明细在右栏,左栏这块主面板不动。\n */\nexport function CommitList(props: CommitListProps) {\n var commits = props.commits || []\n if (commits.length === 0) return null\n var t = props.t\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.commits')),\n h(\n 'div',\n { className: 'dshh-commits' },\n commits.map(function (commit, index) {\n var refs = commit.refs || []\n var isHead = refs.some(function (ref) {\n return ref.indexOf('HEAD') === 0\n })\n var when =\n typeof commit.committedAt === 'number' ? fullWhen(commit.committedAt) : commit.date || ''\n var tip = [t('commit.click'), '', commit.subject, commit.author + ' · ' + when]\n .concat(refs)\n .join('\\n')\n // 整行是按钮:点了在左栏换成这次提交的改动清单(只读)。\n var open = sameCommit(commit.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n key: commit.sha + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(commit)\n },\n },\n h('span', { className: 'dshh-csha' }, commit.sha),\n h('span', { className: 'dshh-csubject' }, commit.subject || '—'),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(commit, t)),\n )\n }),\n ),\n )\n}\n","/**\n * 提交明细(右栏):某次提交的元信息与它改过的文件。\n */\n\nimport { errorText } from '../format.js'\nimport { ChangeList } from './change-list.js'\nimport type { HudCommitFile, HudCommitState, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `CommitPane` 的 props:这次提交的完整明细 + 打开某个文件差异的回调。 */\nexport interface CommitPaneProps {\n t: Translate\n /** 提交明细的状态(`state.commit`);只在它非空时才渲染本栏。 */\n commit: HudCommitState\n /** 打开清单里一条(= 提交里的某个文件)的差异。 */\n onOpenFile: (file: HudCommitFile) => void\n /** 正在看的那份差异对应的文件。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 本栏宽度(右边缘抓握可拖)。 */\n width?: number | null\n}\n\n/**\n * 一次提交的那一栏(**中间**栏,固定宽度):元信息 + 这次提交的改动清单。\n *\n * 差异在它右边那一栏(DiffView)。打开提交不改动左栏——左栏始终是那块主面板,\n * 只是\"最近提交\"里对应的一行高亮起来。清单复用工作区那套 ChangeList(同一个\n * 行样式、同一套键盘移动)。\n */\nexport function CommitPane(props: CommitPaneProps) {\n var t = props.t\n var commit = props.commit\n var style = { width: props.width }\n\n var head\n if (commit.phase === 'loading') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, t('commit.loading')),\n )\n } else if (commit.phase === 'error') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, errorText(commit.errorCode, t)),\n )\n } else {\n var lines = [commit.author + ' · ' + commit.date]\n if (commit.refs && commit.refs.length > 0) lines.push(commit.refs.join(', '))\n if (commit.merge) lines.push(t('commit.merge'))\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h(\n 'div',\n { className: 'dshh-commitTitle' },\n h('span', { className: 'dshh-csha' }, commit.shortSha),\n h(\n 'span',\n { className: 'dshh-commitSubject', title: commit.subject },\n commit.subject || '—',\n ),\n ),\n h('div', { className: 'dshh-commitMeta' }, lines.join(' · ')),\n )\n }\n\n var files = commit.files || []\n var list =\n commit.phase !== 'ready'\n ? null\n : files.length === 0\n ? h(\n 'div',\n { className: 'dshh-commitFiles' },\n h('div', { className: 'dshh-note' }, t('commit.empty')),\n )\n : h(\n 'div',\n { className: 'dshh-commitFiles' },\n h(ChangeList, {\n changes: files,\n truncated: commit.truncated,\n t: t,\n title: t('commit.files') + ' · ' + String(files.length),\n onOpen: props.onOpenFile,\n activeFile: props.activeFile,\n cursor: props.cursor,\n listId: 'commit',\n }),\n )\n\n return h('div', { className: 'dshh-commitpane', style: style }, head, list)\n}\n","/**\n * 概览那一行的计数小片:按「改动 > 新增 > 未跟踪」排列,颜色与胶囊上的简写一致。\n */\n\nimport type React from 'react'\n\nimport type { HudStatusCounts, Translate } from '../shared.js'\nimport { COUNT_COLOR, COUNT_ORDER, h } from '../shared.js'\n\n/** `CountChips` 的 props:八类改动计数 + 取词函数。 */\nexport interface CountChipsProps {\n /** git status 的分组计数;缺字段按 0 处理。 */\n counts?: HudStatusCounts | null\n t: Translate\n}\n\n/* ------------------------------------------------------------- 小组件 */\n\nexport function CountChips(props: CountChipsProps): React.ReactElement {\n var counts: Partial<HudStatusCounts> = props.counts || {}\n var children: React.ReactElement[] = []\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = counts[key] || 0\n if (value <= 0) continue\n children.push(\n h(\n 'span',\n {\n key: key,\n className: 'dshh-chip',\n style: { color: COUNT_COLOR[key], borderColor: 'currentColor' },\n title: props.t('count.' + key) + ' ' + String(value),\n },\n props.t('count.' + key) + ' ' + String(value),\n ),\n )\n }\n if (children.length === 0) {\n return h('span', { className: 'dshh-clean' }, props.t('panel.clean'))\n }\n return h('div', { className: 'dshh-chips' }, children)\n}\n","/**\n * 图片改动的前后对比:两侧缩略、尺寸与字节数、可缩放,认不出的二进制退回一句话说明。\n */\n\nimport React from 'react'\n\nimport { errorText } from '../format.js'\nimport type {\n HudDiffState,\n HudStore,\n ImagePayload,\n ImageRequest,\n ImageSide,\n Translate,\n} from '../shared.js'\nimport { IMAGE_FILE_EXTENSIONS, formatBytes, h, imagePairKey, stepZoom } from '../shared.js'\n\n/**\n * `image` 端点在 store 缓存里的条目(store 的 image 片写进 `Map` 的那个形状)。\n *\n * 它同时被 image-diff 与本目录的 turn-images 消费;先放在这里(等 shared 收尾后视需要上提)。\n */\nexport interface ImagePairEntry {\n phase: string\n value?: ImagePayload\n code?: string | null\n}\n\n/** 图片请求失败/成功的一侧标签对。 */\nexport interface ImageLabels {\n before: string\n after: string\n}\n\n/** 图片并排/切换时的一格。 */\ninterface ImageCell {\n key: string\n label: string\n side: ImageSide\n}\n\n/** `ImageDiff` 的 props:差异状态(二进制改动才有它)+ store + 工作区(发请求要用)。 */\nexport interface ImageDiffProps {\n t: Translate\n store: HudStore\n diff: HudDiffState\n /** 会话工作区;没有它就没法发 `image` 请求(文本 diff 也用不到)。 */\n cwd?: string | null\n}\n\n/**\n * 二进制(图片)改动的预览:并排两张图,或按需在两张之间切换。\n *\n * 只在文本 diff 报 `binary` 时挂上来。字节来自宿主的 `image` 端点(只读),\n * 这里只负责画:单侧画不出来就画那一侧的原因,两侧都不行才退回原来那句话。\n */\nexport function ImageDiff(props: ImageDiffProps) {\n var t = props.t\n var store = props.store\n var diff = props.diff\n var request = imageRequest(diff, props.cwd)\n var key = request === null ? null : imagePairKey(request)\n // 先同步看缓存:已经取过的图第一帧就画出来,不闪\"读取图片…\"。\n var viewRef = React.useState<ImagePairEntry | null>(function () {\n return request !== null && store.cachedImagePair\n ? asPairEntry(store.cachedImagePair(request))\n : null\n })\n var modeRef = React.useState<'pair' | 'flip'>('pair')\n var flipRef = React.useState<'after' | 'before'>('after')\n\n React.useEffect(\n function () {\n if (request === null || key === null) return undefined\n var cancelled = false\n var cached = store.cachedImagePair ? asPairEntry(store.cachedImagePair(request)) : null\n if (cached !== null) {\n viewRef[1](cached)\n return undefined\n }\n viewRef[1](null)\n store.loadImagePair(request).then(function (entry) {\n if (!cancelled) viewRef[1](asPairEntry(entry))\n })\n return function () {\n cancelled = true\n }\n },\n [key],\n )\n\n if (key === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('diff.binary'))\n\n var view = viewRef[0]\n if (view === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('image.loading'))\n if (view.phase === 'error')\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, errorText(view.code, t))\n\n var value: Partial<ImagePayload> = view.value || {}\n var before: ImageSide = value.before || { kind: 'missing' }\n var after: ImageSide = value.after || { kind: 'missing' }\n if (before.kind !== 'image' && after.kind !== 'image') {\n return h(\n 'div',\n { className: 'dshh-empty', 'data-dsh-hud': 'image' },\n imageFallbackText(t, before, after),\n )\n }\n\n var labels = imageLabels(diff, t)\n var mode = modeRef[0]\n var flip = flipRef[0]\n // 缩放住在 store 里(和\"自动换行\"一样记住):`null` = 适应宽度,数字 = 固定档位。\n var zoom = diff.zoom === undefined ? null : diff.zoom\n var cells: ImageCell[] =\n mode === 'pair'\n ? [\n { key: 'before', label: labels.before, side: before },\n { key: 'after', label: labels.after, side: after },\n ]\n : [\n {\n key: flip,\n label: flip === 'after' ? labels.after : labels.before,\n side: flip === 'after' ? after : before,\n },\n ]\n\n function cell(entry: ImageCell): React.ReactElement {\n var side = entry.side\n var body =\n side.kind === 'image'\n ? h('img', {\n className: 'dshh-img',\n alt: entry.label,\n // 缩放:适应宽度交给 CSS(`max-width:100%`);固定档位按**固有像素**算宽度,\n // 因为宿主已经把宽高一起给我们了 —— 不用等图片加载就能算对。\n style:\n zoom === null || typeof side.width !== 'number'\n ? undefined\n : { width: Math.round(side.width * zoom) + 'px', maxWidth: 'none' },\n src: 'data:' + side.mime + ';base64,' + String(side.base64 || ''),\n })\n : h('div', { className: 'dshh-imgNote' }, sideReasonText(side, t))\n return h(\n 'div',\n { className: 'dshh-imgCell', key: entry.key },\n h('div', { className: 'dshh-imgLabel' }, entry.label),\n body,\n )\n }\n\n function tab(id: 'pair' | 'flip', label: string): React.ReactElement {\n var active = mode === id\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n onClick: function () {\n modeRef[1](id)\n },\n },\n label,\n )\n }\n\n // 尺寸变了才是这张图最值得一眼看到的信息(改图最常见的就是换尺寸)。\n var dimsChanged =\n before.kind === 'image' &&\n after.kind === 'image' &&\n typeof before.width === 'number' &&\n typeof after.width === 'number' &&\n (before.width !== after.width || before.height !== after.height)\n ? h(\n 'span',\n { className: 'dshh-imgDims' },\n t('image.dimsChanged', {\n from: t('image.dims', { w: String(before.width), h: String(before.height) }),\n to: t('image.dims', { w: String(after.width), h: String(after.height) }),\n }),\n )\n : null\n var bytes =\n before.kind === 'image' && after.kind === 'image'\n ? h(\n 'span',\n { className: 'dshh-imgBytes' },\n formatBytes(before.bytes) + ' → ' + formatBytes(after.bytes),\n )\n : null\n\n // 缩放按钮:内置那个文档预览把缩放藏在\"预览区底部 72px 的悬停感应带\"里\n // (CSS 默认 opacity:0,还得 Ctrl+滚轮),这里给一排看得见、点的着的。\n var zoomControls = h('span', { className: 'dshh-imgZoom', 'data-dsh-hud': 'image-zoom' }, [\n h(\n 'button',\n {\n key: 'out',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomOut'),\n 'data-dsh-hud': 'image-zoom-out',\n onClick: function () {\n store.setZoom(stepZoom(zoom, -1))\n },\n },\n '−',\n ),\n h(\n 'button',\n {\n key: 'value',\n type: 'button',\n className: 'dshh-imgZoomBtn dshh-imgZoomValue',\n title: t('image.fitWidth'),\n 'data-dsh-hud': 'image-zoom-value',\n onClick: function () {\n store.setZoom(null)\n },\n },\n zoom === null\n ? t('image.fitWidth')\n : t('image.zoomValue', { percent: String(Math.round(zoom * 100)) }),\n ),\n h(\n 'button',\n {\n key: 'in',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomIn'),\n 'data-dsh-hud': 'image-zoom-in',\n onClick: function () {\n store.setZoom(stepZoom(zoom, 1))\n },\n },\n '+',\n ),\n ])\n\n return h(\n 'div',\n { className: 'dshh-imgWrap', 'data-dsh-hud': 'image' },\n h(\n 'div',\n { className: 'dshh-imgMeta' },\n tab('pair', t('image.pair')),\n mode === 'flip'\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('image.flip'),\n 'data-dsh-hud': 'image-flip',\n onClick: function () {\n flipRef[1](flip === 'after' ? 'before' : 'after')\n },\n },\n t('image.flip'),\n )\n : null,\n zoomControls,\n dimsChanged,\n bytes,\n ),\n h(\n 'div',\n {\n className:\n 'dshh-imgPair' +\n (mode === 'flip' ? ' dshh-imgPairSingle' : '') +\n (zoom === null ? '' : ' dshh-imgPairZoomed'),\n },\n cells.map(cell),\n ),\n )\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/**\n * 由 diff 状态推出 `image` 请求;推不出(没有 cwd / 文件)返回 null。\n *\n * 页签语义与文本 diff 完全一致:未暂存看「索引 → 工作区」,已暂存看「HEAD → 索引」,\n * 提交明细按 sha 取,所以两个视图里\"未暂存 / 已暂存\"指的是同一件事。\n */\nexport function imageRequest(\n diff: HudDiffState | null | undefined,\n cwd: string | null | undefined,\n): ImageRequest | null {\n if (!diff || !diff.file || !cwd) return null\n var request: ImageRequest = { cwd: cwd, path: diff.file }\n if (diff.origPath) request.origPath = diff.origPath\n if (diff.source === 'commit' && diff.sha) request.sha = diff.sha\n else request.side = diff.side === 'index' ? 'index' : 'worktree'\n return request\n}\n\n/** 两侧的标签跟着\"看的到底是哪两条记录\"走。 */\nexport function imageLabels(diff: HudDiffState, t: Translate): ImageLabels {\n if (diff.source === 'commit')\n return { before: t('image.beforeCommit'), after: t('image.afterCommit') }\n if (diff.side === 'index') return { before: t('image.beforeIndex'), after: t('image.afterIndex') }\n return { before: t('image.beforeWorktree'), after: t('image.afterWorktree') }\n}\n\n/** 一侧画不出来时的原因文案(每一侧各有各的原因)。 */\nexport function sideReasonText(side: ImageSide, t: Translate): string {\n if (side.kind === 'too-large') {\n return t('image.tooLarge', {\n limit: String(Math.round((typeof side.limit === 'number' ? side.limit : 0) / 1024 / 1024)),\n size: formatBytes(side.bytes),\n })\n }\n if (side.kind === 'not-image') return t('image.notImage', { size: formatBytes(side.bytes) })\n return t('image.missing')\n}\n\n/**\n * 两张都画不出来时说什么:能说清原因就说原因,否则退回原来那句话。\n * \"太大\"优先于\"不是图\" —— 前者才是用户会想处理的那件事。\n */\nexport function imageFallbackText(t: Translate, before: ImageSide, after: ImageSide): string {\n var sides = [before, after]\n var tooLarge = sides.filter(function (side) {\n return side.kind === 'too-large'\n })[0]\n if (tooLarge !== undefined) return sideReasonText(tooLarge, t)\n var notImage = sides.filter(function (side) {\n return side.kind === 'not-image'\n })[0]\n if (notImage !== undefined) return sideReasonText(notImage, t)\n return t('diff.binary')\n}\n\n/* ----------------------------------------- 轮尾:本轮改过的图片(一条门) */\n\n/** 按扩展名粗筛\"这看起来是张图\"。 */\nexport function looksLikeImage(file: unknown): boolean {\n if (typeof file !== 'string') return false\n var dot = file.lastIndexOf('.')\n if (dot < 0 || dot === file.length - 1) return false\n return IMAGE_FILE_EXTENSIONS.indexOf(file.slice(dot + 1).toLowerCase()) >= 0\n}\n\n/**\n * store 边界:图片缓存条目由 store 的 image 片构造(形状见 `store/image.ts`),\n * 到这里只做一次\"是不是对象\"的收窄;坏值当没有缓存(会再发一次请求,行为不变)。\n */\nfunction asPairEntry(value: unknown): ImagePairEntry | null {\n return value !== null && value !== undefined && typeof value === 'object'\n ? (value as ImagePairEntry)\n : null\n}\n","/**\n * 差异视图:两侧页签、逐行渲染、折叠 hunk、复制为文本。\n */\n\nimport React from 'react'\n\nimport { ImageDiff } from './image-diff.js'\nimport { findMarked, revealRow } from '../dom.js'\nimport { errorText } from '../format.js'\nimport type {\n DiffPayload,\n DiffRow as DiffRowShape,\n DiffSide,\n HudDiffState,\n HudStore,\n Translate,\n WorktreeDiffPayload,\n} from '../shared.js'\nimport { DIFF_ADD_BG, DIFF_DEL_BG, DIFF_GUTTER_COLOR, h } from '../shared.js'\n\n/** 差异视图的两侧页签(提交差异没有这个概念,不会画)。 */\nexport type DiffTabSide = 'worktree' | 'index'\n\n/** `DiffView` 的 props:当前差异状态 + 打开它的 store + 工作区(图片预览要按它发请求)。 */\nexport interface DiffViewProps {\n t: Translate\n diff: HudDiffState\n store: HudStore\n cwd?: string | null\n}\n\n/** `DiffRow` 的 props:一行解析好的差异 + 它的 hunk 归属与折叠状态。 */\nexport interface DiffRowProps {\n row: DiffRowShape\n /** 自动换行(store 里记住的开关)。 */\n wrap?: boolean\n /** 这一行属于第几个 hunk(头之前的行是 `null`)。 */\n hunk?: number | null\n /** hunk 头是否\"当前正在看\"的那一段。 */\n active?: boolean\n /** hunk 是否被折叠。 */\n collapsed?: boolean\n /** hunk 头的悬停说明(\"点一下折叠\")。 */\n hint?: string\n /** 点 hunk 头:折叠/展开这一段。 */\n onToggle?: (() => void) | null\n}\n\n/** `visibleDiffRows()` 的输出项:要渲染的行 + 它属于哪个 hunk。 */\nexport interface VisibleDiffEntry {\n row: DiffRowShape\n /** hunk 序号;hunk 头之前的行是 `null`。 */\n hunk: number | null\n}\n\nexport function DiffView(props: DiffViewProps) {\n var t = props.t\n var diff = props.diff\n var store = props.store\n // 图片预览要按工作区发请求;文本 diff 用不到它。\n var cwd = props.cwd\n\n var copiedRef = React.useState(false)\n var copied = copiedRef[0]\n var setCopied = copiedRef[1]\n // 复制的\"已复制\"提示要 1.5s 后收回;定时器句柄得留一份好清掉。\n var timerRef = React.useState<number | null>(null)\n\n var isCommit = diff.source === 'commit'\n // 提交差异没有\"侧\"这个概念,但只要不是提交,页签就得知道自己亮的是哪个。\n var side: DiffTabSide = diff.side === 'index' ? 'index' : 'worktree'\n var data = diffPayload(diff)\n var untracked = isCommit ? false : payloadUntracked(diff.data)\n var hunk = typeof diff.hunk === 'number' ? diff.hunk : 0\n var collapsed = diff.collapsed || []\n var rows = data && data.rows ? data.rows : []\n\n // 换了文件 / 侧 / 提交:回到顶部。否则会停在上一份 diff 的滚动位置\n // (React 复用同一个容器节点,scrollTop 会留下来)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (host && typeof host.scrollTop === 'number') host.scrollTop = 0\n },\n [diff.file, diff.sha, diff.side],\n )\n\n // 键盘移动 hunk:把当前 hunk 滚进视野(已经看得见就一点都不动)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (!host || typeof host.querySelector !== 'function') return\n revealRow(\n host.querySelector('[data-dsh-hud=\"hunk\"][data-hunk-index=\"' + String(hunk) + '\"]'),\n 'dshh-diff',\n )\n },\n [hunk, diff.file, diff.sha, diff.side, collapsed.length],\n )\n\n function onCopy(): void {\n if (!data) return\n var clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined\n if (!clipboard || typeof clipboard.writeText !== 'function') return\n Promise.resolve(clipboard.writeText(rowsToText(data.rows || [])))\n .then(function () {\n setCopied(true)\n if (timerRef[0]) window.clearTimeout(timerRef[0])\n timerRef[1](\n window.setTimeout(function () {\n setCopied(false)\n }, 1500),\n )\n })\n .catch(function () {\n // 剪贴板被拒绝(无权限/非安全上下文)时不谎报成功。\n })\n }\n\n function tab(kind: DiffTabSide, label: string, title: string): React.ReactElement {\n var active = side === kind\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n title: title,\n onClick: function () {\n store.setDiffSide(kind)\n },\n },\n label,\n )\n }\n\n var counts =\n data && data.binary !== true\n ? h(\n 'span',\n { className: 'dshh-difcounts' },\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-success-primary, #3fb950)' } },\n '+' + String(data.additions),\n ),\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-error-primary, #f06a6a)' } },\n '-' + String(data.deletions),\n ),\n )\n : null\n\n var body: React.ReactElement\n if (diff.phase === 'loading') {\n body = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else if (diff.phase === 'error') {\n body = h('div', { className: 'dshh-empty' }, errorText(diff.errorCode, t))\n } else if (!data) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else if (data.binary === true) {\n // 二进制里最常被改的就是图:能认出来就画出来,认不出才退回那句话。\n body = h(ImageDiff, { t: t, store: store, diff: diff, cwd: cwd })\n } else if (rows.length === 0) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else {\n var visible = visibleDiffRows(rows, collapsed)\n body = h(\n 'div',\n { className: 'dshh-diff', 'data-dsh-hud': 'diff' },\n visible.map(function (entry, index) {\n // hunk 序号只在 hunk 头及其后面的行上有;头之前的行是 null。\n var entryHunk = entry.hunk === null ? -1 : entry.hunk\n var isHunk = entry.row.kind === 'hunk'\n return h(DiffRow, {\n key: String(index) + ':' + entry.row.kind,\n row: entry.row,\n wrap: diff.wrap === true,\n hunk: entry.hunk,\n active: isHunk && entry.hunk === hunk && collapsed.indexOf(entryHunk) < 0,\n collapsed: isHunk && collapsed.indexOf(entryHunk) >= 0,\n hint: isHunk ? t('diff.hunkToggle') : undefined,\n onToggle: isHunk\n ? function () {\n store.toggleHunk(entry.hunk === null ? undefined : entry.hunk)\n }\n : undefined,\n })\n }),\n data.truncated === true ? h('div', { className: 'dshh-note' }, t('diff.truncated')) : null,\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-diffview' },\n h(\n 'div',\n { className: 'dshh-toolbar' },\n h('span', { className: 'dshh-difpath', title: diff.path }, diff.path),\n counts,\n h('span', { className: 'dshh-spacer' }),\n // 提交差异只有一个\"侧\"(相对父提交),因此不给 未暂存/已暂存 页签\n // (短 sha 与标题在右栏的明细头里,这里不重复)。\n untracked ? h('span', { className: 'dshh-kv' }, t('diff.untrackedHint')) : null,\n isCommit ? null : tab('worktree', t('diff.worktree'), t('diff.worktree')),\n isCommit || untracked ? null : tab('index', t('diff.index'), t('diff.index')),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (diff.wrap === true ? ' dshh-tabActive' : ''),\n 'aria-pressed': diff.wrap === true ? 'true' : 'false',\n title: t('diff.wrap'),\n onClick: function () {\n store.toggleDiffWrap()\n },\n },\n t('diff.wrap'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('diff.copy'),\n onClick: onCopy,\n },\n copied ? t('diff.copied') : t('diff.copy'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n // 提交明细里这个 ✕ 收的是整块明细(差异是明细的一段),文案跟着改。\n title: isCommit ? t('commit.close') : t('diff.close'),\n 'aria-label': isCommit ? t('commit.close') : t('diff.close'),\n onClick: function () {\n store.closeDetail()\n },\n },\n '✕',\n ),\n ),\n body,\n )\n}\n\n/**\n * 一行差异:两个行号槽 + 文本。\n *\n * hunk 头是**可点**的(折叠 / 展开这一段),因此它第一个行号槽放的是折叠标记,\n * 不是行号——那个位置本来就空着,放标记不会让文本错位。\n */\nexport function DiffRow(props: DiffRowProps): React.ReactElement {\n var row = props.row\n var style =\n row.kind === 'add'\n ? { background: DIFF_ADD_BG }\n : row.kind === 'del'\n ? { background: DIFF_DEL_BG }\n : null\n var gutterStyle = { color: DIFF_GUTTER_COLOR }\n var text = row.text === '' ? '\\u00a0' : row.text\n\n if (row.kind === 'hunk') {\n return h(\n 'div',\n {\n className: 'dshh-difrow dshh-difrow-hunk' + (props.active ? ' dshh-difrow-hunkActive' : ''),\n 'data-dsh-hud': 'hunk',\n 'data-hunk-index':\n props.hunk === null || props.hunk === undefined ? undefined : String(props.hunk),\n title: props.hint,\n onClick: props.onToggle,\n },\n h('span', { className: 'dshh-difno', style: gutterStyle }, ''),\n h(\n 'span',\n { className: 'dshh-difno dshh-hunkMark', style: gutterStyle },\n props.collapsed ? '▸' : '▾',\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-difrow dshh-difrow-' + row.kind, style: style },\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.oldLine === null ? '' : String(row.oldLine),\n ),\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.newLine === null ? '' : String(row.newLine),\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n}\n\n/**\n * 把 rows 整理成\"要渲染的行 + 它属于哪个 hunk\"。\n *\n * hunk 段从 hunk 头开始,到下一个 hunk 头(或结尾)结束;折叠某个 hunk 就是\n * 把该段里除头以外的行丢掉。hunk 头之前那些行(`diff --git`、`---`、`+++`)\n * 永远显示——它们是\"这是哪个文件\"的上下文。\n *\n * @returns `[{ row, hunk }]`,`hunk` 是 hunk 序号(头之前的行是 null,收起时是负数)。\n */\nexport function visibleDiffRows(rows: DiffRowShape[], collapsed: number[]): VisibleDiffEntry[] {\n var folded = collapsed || []\n var out: VisibleDiffEntry[] = []\n var ordinal = -1\n for (var index = 0; index < rows.length; index += 1) {\n var row = rows[index]\n if (row.kind === 'hunk') {\n ordinal += 1\n out.push({ row: row, hunk: ordinal })\n continue\n }\n if (ordinal >= 0 && folded.indexOf(ordinal) >= 0) continue\n out.push({ row: row, hunk: ordinal >= 0 ? ordinal : null })\n }\n return out\n}\n\n/** 把 rows 还原成 unified diff 文本(\"复制差异\"用)。 */\nexport function rowsToText(rows: DiffRowShape[]): string {\n return rows\n .map(function (row) {\n if (row.kind === 'add') return '+' + row.text\n if (row.kind === 'del') return '-' + row.text\n if (row.kind === 'ctx') return ' ' + row.text\n return row.text\n })\n .join('\\n')\n}\n\n/** 工作区差异的载荷形状:两侧都在。提交差异是单侧,没有这个形状。 */\nfunction isWorktreePayload(payload: DiffPayload): payload is WorktreeDiffPayload {\n return 'worktree' in payload && 'index' in payload\n}\n\n/** 工作区差异载荷里的 `untracked`;单侧载荷(提交差异)没有这个字段。 */\nfunction payloadUntracked(payload: DiffPayload | null): boolean {\n return payload !== null && isWorktreePayload(payload) ? payload.untracked === true : false\n}\n\n/** 当前这一侧的 rows 载体:提交差异只有一侧,工作区差异要按 side 取。 */\nexport function diffPayload(diff: HudDiffState | null | undefined): DiffSide | null {\n if (!diff || !diff.data) return null\n // 域边界:来源是 commit 时宿主只回单侧载荷(`DiffSide`),类型上 `data` 是联合,这里收窄一次。\n if (diff.source === 'commit') return diff.data as DiffSide\n // 工作区差异的 `data` 一定是两侧载荷;收窄成单侧只是给类型看的,取的是同一份 rows。\n if (!isWorktreePayload(diff.data)) return null\n return diff.data[diff.side === 'index' ? 'index' : 'worktree'] || null\n}\n","/**\n * 标签列表(面板底部):只在有标签时渲染,点了复制名字。\n */\n\nimport type React from 'react'\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudTag, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `TagList` 的 props:标签清单 + HEAD 比对 + 打开回调。 */\nexport interface TagListProps {\n /** `null` = 还没取到(整段不画);`[]` = 取回了、确实一个都没有(画一行说明)。 */\n tags?: HudTag[] | null\n t: Translate\n /** 当前 HEAD 的 oid(用来给指向 HEAD 的标签标 HEAD 徽标)。 */\n headSha?: string | null\n /** 提交明细当前打开的那条。 */\n activeSha?: string | null\n truncated?: boolean\n onOpen?: ((tag: HudTag) => void) | null\n}\n\n/**\n * 标签列表:一行一个,tag 名 + 它指向的提交 sha + 相对时间;指向当前 HEAD 的标 HEAD。\n * 点一行 = 用右栏的提交明细看这个 tag 指向的那次提交(复用同一条管道,不新增界面)。\n */\nexport function TagList(props: TagListProps): React.ReactElement | null {\n // 原样保留 `null`(\"还没取到\")—— 写成 `props.tags || []` 会把\"没取到\"和\"确实没有\"合并,\n // 于是面板一打开就先闪一行\"还没有标签\"。\n var tags = props.tags\n var t = props.t\n // `null` = 还没取到(先不画,避免闪一下\"还没有标签\");`[]` = 取回了、确实一个都没有 →\n // 这时要**画一行写明**:整段不画的话,用户没法区分\"这个仓库没有 tag\"与\"这个功能没做\"(实测踩过)。\n if (!Array.isArray(tags)) return null\n if (tags.length === 0) {\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h('div', { className: 'dshh-note', 'data-tags-empty': '1' }, t('panel.tagsEmpty')),\n )\n }\n var headSha = props.headSha\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h(\n 'div',\n { className: 'dshh-commits' },\n tags.map(function (tag, index) {\n var isHead =\n typeof headSha === 'string' &&\n headSha.length > 0 &&\n (tag.sha === headSha || headSha.indexOf(tag.sha) === 0)\n var tip = [\n t('tag.click'),\n '',\n tag.subject || '',\n t(tag.annotated ? 'tag.annotated' : 'tag.lightweight') +\n ' · ' +\n tag.sha +\n ' · ' +\n (typeof tag.committedAt === 'number' ? fullWhen(tag.committedAt) : tag.date || ''),\n ].join('\\n')\n var open = sameCommit(tag.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n 'data-tag-row': tag.name,\n key: tag.name + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(tag)\n },\n },\n h('span', { className: 'dshh-ctag' }, tag.name),\n h('span', { className: 'dshh-csha' }, tag.sha),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(tag, t)),\n )\n }),\n ),\n props.truncated === true\n ? h('div', { className: 'dshh-note' }, t('panel.tagsCapped', { n: String(tags.length) }))\n : null,\n )\n}\n","/**\n * 面板的三段行为,各自一个 hook:键盘与\"点进来\"手势、视口变化后夹回位置、量 dock 行位置。\n *\n * 提成 hook 的理由:这些是 `FloatingPanel` 里最费劲的部分,而它们只依赖 `store` 与几个快照字段 ——\n * 组件从此只剩\"hooks 调用 + JSX\"。\n */\n\nimport { activeElement, isInsidePanel, isTypingTarget, measureDockTop } from '../dom.js'\nimport { clampPos, panelWidthOf, visibleChangeRows } from '../shared.js'\nimport type { HudStatus, HudStore, PanelPos } from '../shared.js'\nimport React from 'react'\n\n/**\n * 根作用域的座位:读不到当前会话,只读共享 store。\n * 收起时渲染 `null` —— 这样不会在输入框上留一个挡点击的气泡。\n */\n/**\n * 面板的键盘与\"点进来\"手势:Esc 逐层退(右栏 → 收起面板),其余键只在面板被激活时接管。\n *\n * 提成顶层 hook 的理由:这段是面板里最费劲的一部分,而它只依赖 `store` 与 `panelOpen`\n * (`armed` 是就地可变的对象,按引用传进来,语义与原来完全一致)。\n */\nexport function usePanelKeys(\n store: HudStore,\n panelOpen: boolean,\n armed: { inside: boolean },\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var onKeyDown = function (event: KeyboardEvent) {\n // Esc 任何时候都收(和以前一样:焦点在输入框里也能收到)。\n if (event.key === 'Escape') {\n // 和 dsh-git 一样\"退一层\":右栏(差异 / 提交明细)→ 收起面板。\n var current = store.getSnapshot()\n if (current.diff || current.commit) store.closeDetail()\n else store.openPanel(false)\n return\n }\n\n // 其余按键只在面板被激活时接管:先在面板里点一下(或 Tab 进去)。\n // 本插件是挂在输入框上方的浮层,抢键的代价(打字、翻菜单、滚会话\n // 全被吃掉)远大于多几个快捷键,所以宁可要一次\"点进来\"。\n if (!armed.inside && !isInsidePanel(activeElement())) return\n // 正在输入时一律不管。\n if (isTypingTarget(event.target)) return\n // 带修饰键的组合交给产品与浏览器。\n if (event.metaKey || event.ctrlKey || event.altKey) return\n\n var handled = true\n if (event.key === 'ArrowDown') store.stepFile(1, false)\n else if (event.key === 'ArrowUp') store.stepFile(-1, false)\n else if (event.key === ']') store.stepFile(1, true)\n else if (event.key === '[') store.stepFile(-1, true)\n else if (event.key === 'Enter') store.openCursor()\n else if (event.key === 'n') store.stepHunk(1)\n else if (event.key === 'p') store.stepHunk(-1)\n else if (event.key === ' ' || event.code === 'Space') {\n var openDiff = store.getSnapshot().diff\n store.toggleHunk(openDiff ? openDiff.hunk : 0)\n } else if (event.key === 'r') store.refresh()\n // `v`:把当前聚焦的那一行标成\"这一版我审过了\"(再按一次取消)。\n // 用键盘而不是给行尾那个标记加 tabindex:行本身是按钮,里面再塞一个按钮是无效结构。\n else if (event.key === 'v') {\n var snapshot = store.getSnapshot()\n var rows = visibleChangeRows(snapshot)\n var target =\n snapshot.cursor !== null && snapshot.cursor !== undefined ? snapshot.cursor : null\n if (target === null) target = rows[0] ? rows[0].file : null\n if (target !== null) store.toggleReviewed(target)\n } else handled = false\n\n if (handled) {\n // 认领了的键就别再让浏览器滚动页面,也别让产品再处理一遍。\n event.preventDefault()\n event.stopPropagation()\n }\n }\n\n var onPointerDown = function (event: PointerEvent) {\n armed.inside = isInsidePanel(event.target)\n }\n\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('pointerdown', onPointerDown, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('pointerdown', onPointerDown, true)\n }\n },\n [panelOpen, store],\n )\n}\n\n/** 视口变化后把已记忆的面板位置夹回可见区域。 */\nexport function usePanelClamp(store: HudStore, pos: PanelPos | null): void {\n React.useEffect(\n function () {\n if (!pos) return undefined\n var onResize = function () {\n var next = clampPos(pos.left, pos.top, panelWidthOf(store))\n if (next.left !== pos.left || next.top !== pos.top) store.movePanel(next)\n }\n window.addEventListener('resize', onResize)\n return function () {\n window.removeEventListener('resize', onResize)\n }\n },\n [pos, store],\n )\n}\n\n/** 量 dock 行位置:面板据此贴到胶囊上方(会话/状态变化都会重量一次,所以输入框移位时面板跟着走)。 */\nexport function useDockAnchor(\n store: HudStore,\n panelOpen: boolean,\n sessionId: string | null | undefined,\n status: HudStatus | null,\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var measure = function () {\n store.setAnchorTop(measureDockTop())\n }\n measure()\n window.addEventListener('resize', measure)\n return function () {\n window.removeEventListener('resize', measure)\n }\n },\n [panelOpen, sessionId, status, store],\n )\n}\n","/**\n * 悬浮面板本体:头部、概览、左栏改动清单、中间提交栏、右栏差异/提交明细,以及拖拽与抓握。\n *\n * 三段行为(键盘、视口夹回、dock 锚点)在 `hooks/panel.ts` 里;这里只剩 hooks 调用与 JSX。\n */\n\nimport { reviewView } from '../shared.js'\nimport React from 'react'\n\nimport { ChangeList } from './change-list.js'\nimport { CommitList } from './commit-list.js'\nimport { CommitPane } from './commit-pane.js'\nimport { CountChips } from './count-chips.js'\nimport { DiffView } from './diff-view.js'\nimport { SessionChangesLine } from './session-changes.js'\nimport { TagList } from './tag-list.js'\nimport { branchLabel, errorText, timeLabel } from '../format.js'\nimport { useDockAnchor, usePanelClamp, usePanelKeys } from '../hooks/panel.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudChange, HudLogEntry, HudStore, HudTag, Translate } from '../shared.js'\nimport {\n COMMIT_LIST_WIDTH,\n COMPOSER_OFFSET,\n DIFF_DEFAULT_WIDTH,\n DIFF_PANEL_HEIGHT_VH,\n DRAG_THRESHOLD_PX,\n PANEL_DOCK_GAP,\n PANEL_WIDTH,\n VIEWPORT_MARGIN,\n clampPos,\n h,\n panelLayout,\n panelWidthFor,\n panelWidthOf,\n sessionChangeView,\n visibleChangeRows,\n} from '../shared.js'\n\n/** 头部拖拽的现场(指针 id + 起点 + 按下时指针在面板内的偏移)。 */\ninterface DragState {\n pointerId: number\n startX: number\n startY: number\n offsetX: number\n offsetY: number\n}\n\n/** 抓握拖拽的现场(调哪一栏 + 指针 id + 起点与起始宽度)。 */\ninterface GripState {\n kind: 'diff' | 'commit'\n pointerId: number\n startX: number\n startWidth: number\n}\n\n/** `HudFloatingPanel` 的 props:共享 store(面板状态都在它的快照里)。 */\nexport interface HudFloatingPanelProps {\n t?: Translate\n store: HudStore\n}\n\nexport function HudFloatingPanel(props: HudFloatingPanelProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var store = props.store\n var state = useStore(store)\n\n var dragRef = React.useState<DragState | null>(null)\n var drag = dragRef[0]\n var setDrag = dragRef[1]\n\n var gripRef = React.useState<GripState | null>(null)\n /**\n * \"面板已激活\"标记:在面板里按过指针(或 Tab 进去)才允许接管裸按键。\n *\n * 用可变对象而不是 React state:事件处理里读的必须是最新值,而重建监听\n * 只依赖 `panelOpen`,改这个标记不该触发重渲染。\n * 必须和其它 hook 一起放在下面那个 `!panelOpen → null` 早退之前。\n */\n var armedState = React.useState(function () {\n return { inside: false }\n })\n var armed = armedState[0]\n usePanelKeys(store, state.panelOpen, armed)\n usePanelClamp(store, state.pos)\n useDockAnchor(store, state.panelOpen, state.sessionId, state.status)\n var grip = gripRef[0]\n var setGrip = gripRef[1]\n\n /**\n * 拖动:只在头部按下、并且**不是**按在按钮上时开始;移动走 window 监听,\n * 刻意**不用** `setPointerCapture` —— 一旦在头部捕获指针,`pointerup` 会被\n * 重定向到头部,`click` 便落在按下与松开的公共祖先(头部)上,按钮的\n * `onClick` 永远不触发,面板就「关不掉」了。\n */\n React.useEffect(\n function () {\n if (!drag) return undefined\n // 闭包里用局部常量保住上面的非空收窄(`drag` 是 `var`,TS 在回调里会重新放宽成可空)。\n const activeDrag = drag\n var moved = false\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n if (\n !moved &&\n Math.abs(event.clientX - activeDrag.startX) < DRAG_THRESHOLD_PX &&\n Math.abs(event.clientY - activeDrag.startY) < DRAG_THRESHOLD_PX\n )\n return\n moved = true\n store.movePanel(\n clampPos(\n event.clientX - activeDrag.offsetX,\n event.clientY - activeDrag.offsetY,\n panelWidthOf(store),\n ),\n )\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n setDrag(null)\n // 只有真的拖动过才落盘:头部上的普通点击不该改变位置。\n if (moved) {\n // 拖动过程中 movePanel 已经把位置写进快照,这里收窄只是 strictNullChecks 需要。\n var next = store.getSnapshot().pos\n if (next !== null) store.commitPanel(next)\n }\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [drag, store],\n )\n\n /**\n * 拖动面板**右边缘**调整差异栏宽度(与 dsh-git 的抓握位置一致):\n * 往左拖变宽、往右拖变窄;松手才落盘。\n */\n React.useEffect(\n function () {\n if (!grip) return undefined\n // 同拖动:回调里用局部常量保住非空收窄。\n const activeGrip = grip\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n if (activeGrip.kind === 'commit')\n store.setCommitWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n else store.setDiffWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n setGrip(null)\n store.persistLayout()\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [grip, store],\n )\n\n if (!state.panelOpen) return null\n\n /**\n * 抓握按下。\n *\n * `kind` 决定它调哪一栏:`diff` = 面板右边缘那条(调差异栏);`commit` = 提交栏右边那条\n * (调提交明细栏)。只有提交栏、没有差异栏时,右边缘那条也调提交栏 —— 它是右边界上唯一\n * 还能变宽的东西,不该按下去没反应。\n */\n function onGripPointerDown(\n kind: GripState['kind'],\n event: React.PointerEvent<HTMLDivElement>,\n ): void {\n if (event.button !== 0) return\n var snapshot = store.getSnapshot()\n event.preventDefault()\n var start =\n kind === 'commit'\n ? typeof snapshot.commitWidth === 'number'\n ? snapshot.commitWidth\n : COMMIT_LIST_WIDTH\n : typeof snapshot.diffWidth === 'number'\n ? snapshot.diffWidth\n : DIFF_DEFAULT_WIDTH\n setGrip({ kind: kind, pointerId: event.pointerId, startX: event.clientX, startWidth: start })\n }\n\n function onHeaderPointerDown(event: React.PointerEvent<HTMLDivElement>): void {\n if (event.button !== 0) return\n // DOM 边界:事件的 target 是 EventTarget,`closest` 只存在于 Element 上,按能力判断前先收窄。\n var target = event.target as Element | null\n // 头部里的按钮要保持普通点击,否则拖动会吞掉它们。\n if (target && typeof target.closest === 'function' && target.closest('button')) return\n // DOM 边界:头部的父节点就是面板容器(渲染结构固定),这里按元素取几何。\n var rect = (event.currentTarget.parentNode as HTMLElement).getBoundingClientRect()\n setDrag({\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n offsetX: event.clientX - rect.left,\n offsetY: event.clientY - rect.top,\n })\n }\n\n function onHeaderDoubleClick(): void {\n store.resetPanel()\n }\n\n var layout = panelLayout(state)\n var diffMode = layout.diff\n // 提交明细在场时右栏也在场(明细是中间那一栏,差异是右边那一栏),\n // 而当时 `state.diff` 可能还在路上 —— 因此两种情形都算\"宽面板\"。\n var commitMode = layout.commit\n var wide = diffMode || commitMode\n var dockTop = typeof state.anchorTop === 'number' ? state.anchorTop : null\n // 贴到胶囊所在行的上方;量不到就退回 dsh-git 记录的那次测量(168px)。\n var anchorBottom =\n dockTop === null\n ? COMPOSER_OFFSET\n : Math.max(VIEWPORT_MARGIN, window.innerHeight - dockTop + PANEL_DOCK_GAP)\n\n var panelStyle: React.CSSProperties = {\n position: 'fixed',\n zIndex: 30,\n boxSizing: 'border-box',\n width: panelWidthFor(layout, state.diffWidth, state.commitWidth),\n maxWidth: 'calc(100vw - 16px)',\n display: 'flex',\n flexDirection: 'column',\n border: '1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))',\n borderRadius: 12,\n // 表面用\"抬升面\"令牌:DSH 自己的浮层几乎都用 bg-layer-1,而 bg-overlay\n // 在暗色下是中灰 bluish-700,拿它当面板底色会发灰、与产品观感不一致。\n background: 'var(--dsw-alias-bg-layer-1, #1f2126)',\n boxShadow: 'var(--dsw-shadow-lv2, 0 4px 12px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.12))',\n overflow: 'hidden',\n pointerEvents: 'auto',\n }\n\n if (wide) {\n // 固定高度:面板以 bottom 锚定,若只给 max-height,差异到达时面板会长高并整体跳位。\n var spaceAbove =\n (dockTop === null ? window.innerHeight - anchorBottom : dockTop - PANEL_DOCK_GAP) -\n VIEWPORT_MARGIN\n panelStyle.height = Math.max(\n 240,\n Math.min(Math.round(window.innerHeight * DIFF_PANEL_HEIGHT_VH), spaceAbove),\n )\n } else {\n panelStyle.maxHeight = '62vh'\n }\n\n // 未拖动过:锚在胶囊上方居中;拖动过:显式坐标。\n if (state.pos) {\n panelStyle.left = state.pos.left\n panelStyle.top = state.pos.top\n } else {\n panelStyle.left = '50%'\n panelStyle.bottom = anchorBottom\n panelStyle.transform = 'translateX(-50%)'\n }\n\n // 单栏内容(\"面板本来就长这样\"):两种模式共用同一份,\n // 打开差异时它原封不动地留在左栏,只有右栏是新出现的。\n // \"正在看的那份差异\"与\"键盘选中的那行\"是两件事:前者是打开的文件,\n // 后者可能只是选过还没打开(↑/↓ 移动,Enter 才打开)。两个标记都留着。\n // 左栏那份清单只关心工作区差异:提交明细打开时它不该跟着高亮。\n var openDiff = state.diff\n var openCommit = state.commit\n // 工作区差异(含未暂存/已暂存两页签)与提交差异是两种右栏内容。\n var openFile = openDiff !== null && openDiff.source !== 'commit' ? openDiff.file : null\n var commitOpenFile = openDiff !== null && openDiff.source === 'commit' ? openDiff.file : null\n\n // 会话归因视图与过滤后的清单:渲染期只算一次(标记、过滤、概览那行共用)。\n var sessionView = sessionChangeView(state)\n var sessionRows = visibleChangeRows(state)\n\n var emptyBody: React.ReactElement\n if (state.phase === 'error') {\n emptyBody = h('div', { className: 'dshh-empty' }, errorText(state.errorCode, t))\n } else if (!state.cwd) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.noSession'))\n } else if (state.phase === 'loading' && !state.status) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.notRepo'))\n }\n\n var listBody\n if (state.status && state.status.repo) {\n var status = state.status\n var repoChildren: React.ReactNode[] = [\n h(\n 'div',\n { className: 'dshh-meta' },\n h('span', { className: 'dshh-repo', title: status.repo }, status.repo),\n status.upstream\n ? h('span', { className: 'dshh-kv' }, t('panel.upstream') + ' ' + status.upstream)\n : null,\n status.ahead > 0\n ? h('span', { className: 'dshh-kv' }, '↑ ' + t('panel.ahead') + String(status.ahead))\n : null,\n status.behind > 0\n ? h('span', { className: 'dshh-kv' }, '↓ ' + t('panel.behind') + String(status.behind))\n : null,\n status.oid ? h('span', { className: 'dshh-kv' }, status.oid) : null,\n ),\n h('div', { className: 'dshh-sectionTitle' }, t('panel.summary')),\n h(CountChips, { counts: status.counts, t: t }),\n h(SessionChangesLine, {\n t: t,\n view: sessionView,\n onlySession: state.onlySession === true,\n onToggle: function () {\n store.toggleOnlySession()\n },\n onlyLatest: state.onlyLatest === true,\n onToggleLatest: function () {\n store.toggleOnlyLatest()\n },\n review: reviewView(state),\n onClearReviewed: function () {\n store.clearReviewed()\n },\n }),\n // 顺序:改动概览 → **改动清单** → 最近提交。清单是\"现在要处理的\",\n // 提交是背景信息;清单放前面,一屏不够时先看到的是眼前的事。\n sessionRows.length > 0\n ? h(ChangeList, {\n changes: sessionRows,\n truncated: status.truncated,\n t: t,\n onOpen: function (change: HudChange) {\n store.openDiff(change)\n },\n activeFile: openFile,\n cursor: commitMode ? null : state.cursor,\n listId: 'changes',\n sessionView: sessionView,\n review: reviewView(state),\n onToggleReviewed: function (file: string) {\n store.toggleReviewed(file)\n },\n })\n : state.status.changes.length > 0 && state.onlySession === true && sessionView !== null\n ? // 过滤器开着、本会话又什么都没改:说一句,而不是让清单整段消失。\n h('div', { className: 'dshh-note' }, t('session.empty'))\n : null,\n h(CommitList, {\n commits: state.commits,\n t: t,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 再点一次打开着的那条 = 收起明细(打开提交不改左栏,只高亮它)。\n onOpen: function (commit: HudLogEntry) {\n store.toggleCommit(commit)\n },\n }),\n h(TagList, {\n tags: state.tags,\n truncated: state.tagsTruncated,\n t: t,\n headSha: state.status && state.status.oid ? state.status.oid : null,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 点一个 tag = 看它指向的那次提交(tag 自己不是提交,所以把提交信息填进去)。\n onOpen: function (tag: HudTag) {\n store.toggleCommit({\n sha: tag.sha,\n subject: tag.subject || '',\n author: '',\n date: tag.date || '',\n committedAt: typeof tag.committedAt === 'number' ? tag.committedAt : null,\n refs: [tag.name],\n })\n },\n }),\n ]\n listBody = h('div', { className: 'dshh-body' }, ...repoChildren)\n } else {\n listBody = emptyBody\n }\n\n // 工作台:左栏**始终**是上面那份单栏内容(宽度也与单栏一致);打开提交明细时\n // 中间多一栏(这次提交的元信息 + 改动清单),差异固定在右边那一栏。\n // 左栏一个像素都不动,面板右边缘的抓握仍只调整差异栏——与 dsh-git 的 workbench 同形。\n var body = wide\n ? h(\n 'div',\n { className: 'dshh-workbench' },\n h('div', { className: 'dshh-leftpane', style: { width: PANEL_WIDTH } }, listBody),\n commitMode && openCommit !== null\n ? h(CommitPane, {\n t: t,\n commit: openCommit,\n onOpenFile: function (file) {\n store.openCommitDiff(file)\n },\n activeFile: commitOpenFile,\n cursor: state.cursor,\n width: typeof state.commitWidth === 'number' ? state.commitWidth : COMMIT_LIST_WIDTH,\n })\n : null,\n commitMode && diffMode\n ? h('div', {\n className: 'dshh-grip dshh-gripBetween',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n diffMode && openDiff !== null\n ? h(DiffView, { t: t, diff: openDiff, store: store, cwd: state.cwd })\n : null,\n )\n : listBody\n\n return h(\n 'div',\n { className: 'dshh-panel', 'data-dsh-hud': 'panel', style: panelStyle },\n h(\n 'div',\n {\n className: 'dshh-header',\n title: t('panel.dragHint'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onHeaderPointerDown(event)\n },\n onDoubleClick: onHeaderDoubleClick,\n },\n h('span', { className: 'dshh-title' }, t('panel.title')),\n state.status && state.status.repo\n ? h('span', { className: 'dshh-branch' }, branchLabel(state.status, t))\n : null,\n h('span', { className: 'dshh-spacer' }),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.refresh'),\n 'aria-label': t('panel.refresh'),\n onClick: function () {\n store.refresh()\n },\n },\n '⟳',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.reset'),\n 'aria-label': t('panel.reset'),\n onClick: function () {\n store.resetPanel()\n },\n },\n '⌖',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.close'),\n 'aria-label': t('panel.close'),\n onClick: function () {\n store.openPanel(false)\n },\n },\n '✕',\n ),\n ),\n body,\n diffMode\n ? h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip',\n title: t('diff.resize'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('diff', event)\n },\n })\n : commitMode\n ? // 只有提交栏:右边缘这条改成调它(否则按下去没反应)。\n h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n h(\n 'div',\n { className: 'dshh-footer' },\n h('span', null, t('panel.readonly')),\n h('span', null, state.updatedAt ? t('panel.updated') + timeLabel(state.updatedAt) : ''),\n ),\n )\n}\n","/**\n * 悬停气泡本体:位置由 store 里的 tip 快照给(`shell.overlay` 座位里渲染)。\n */\n\nimport type React from 'react'\n\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore, TipState } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `PillTip` 的 props:共享 store(气泡文案与锚点都在它的快照里)。 */\nexport interface PillTipProps {\n store: HudStore\n}\n\n/**\n * 胶囊悬停提示的气泡。**自己画**,不用浏览器原生 `title`。\n *\n * 由一个独立的 `shell.overlay` 座位渲染、`position:fixed` 定位 —— 这样它不受 dock 那行的\n * 裁切与堆叠上下文限制。位置只用锚点矩形的 `top` 与 `right`:\n * 贴在那枚胶囊**上方**,横向按视口夹住(不量自己的宽度,避免\"先渲染再回填\"的抖动)。\n */\nexport function PillTip(props: PillTipProps): React.ReactElement | null {\n var store = props.store\n var state = useStore(store)\n var tip = state.tip\n\n if (tip === null || tip === undefined) return null\n var innerWidth = typeof window.innerWidth === 'number' ? window.innerWidth : 1200\n var innerHeight = typeof window.innerHeight === 'number' ? window.innerHeight : 800\n var margin = 8\n var left = Math.max(\n margin,\n Math.min(typeof tip.rect.left === 'number' ? tip.rect.left : margin, innerWidth - 428),\n )\n var bottom = Math.max(\n margin,\n innerHeight - (typeof tip.rect.top === 'number' ? tip.rect.top : 0) + 6,\n )\n\n return h(\n 'div',\n {\n className: 'dshh-tip',\n 'data-dsh-hud': 'pill-tip',\n role: 'tooltip',\n style: { left: String(left) + 'px', bottom: String(bottom) + 'px' },\n },\n tipLines(tip),\n )\n}\n\n/**\n * 把 `tip.lines` 画成一行一个元素。\n *\n * 为什么要拆元素:用户要「数字亮、标签灰」。一整段字符串在 DOM 里只有一个文本节点,\n * 一种颜色,做不到;拆成行以后标签与数字各自带类名,颜色由 CSS 决定。\n *\n * 三种行:`label + value`(两段)、整行 `text` 带 `tone`、以及只有 `text` 的普通行。\n */\n/** `tone` → 类名。默认(`primary`)不加类,走 `.dshh-tipValue` 的主文字色。 */\nfunction toneClass(tone: string | undefined): string {\n if (tone === 'muted') return ' dshh-tipMuted'\n if (tone === 'success') return ' dshh-tipSuccess'\n if (tone === 'warn') return ' dshh-tipWarn'\n if (tone === 'accent') return ' dshh-tipAccent'\n return ''\n}\n\nfunction tipLines(tip: TipState): React.ReactNode[] {\n var lines = Array.isArray(tip.lines) ? tip.lines : []\n // 兜底:老快照(或别处直接塞了 text)没有 lines,就照旧画一整段。\n if (lines.length === 0) return [tip.text]\n return lines.map(function (line, index) {\n // 行内分段:一句白话里只把读数染色(计时 / 工具统计这类整句气泡)。\n if (Array.isArray(line.segments)) {\n return h(\n 'div',\n { key: 'line-' + index, className: 'dshh-tipLine' },\n line.segments.map(function (segment, at) {\n var segmentTone =\n segment.tone === undefined ? toneClass(line.tone) : toneClass(segment.tone)\n return h(\n 'span',\n {\n key: 'seg-' + at,\n // 有色的段用内联强调,无色段继承所在行的类(普通文字)。\n className: 'dshh-tipSeg' + segmentTone,\n },\n segment.text,\n )\n }),\n )\n }\n if (typeof line.label === 'string' || typeof line.value === 'string') {\n // 标签恒为次要色;`tone` 只落在数值上(那才是要强调的东西)。\n return h(\n 'div',\n { key: 'line-' + index, className: 'dshh-tipLine' },\n h('span', { key: 'label', className: 'dshh-tipLabel' }, line.label ?? ''),\n h(\n 'span',\n { key: 'value', className: 'dshh-tipValue' + toneClass(line.tone) },\n line.value ?? '',\n ),\n )\n }\n return h(\n 'div',\n { key: 'line-' + index, className: 'dshh-tipLine' + toneClass(line.tone) },\n line.text ?? '',\n )\n })\n}\n","/**\n * 「给这个中转站配一枚后台凭据」的小面板。\n *\n * 为什么要有它:中转站的**账号余额**(后台那个\"当前余额\")挂在 new-api 的\n * `/api/user/self` 上,只认后台凭据(PAT / 登录会话),`sk-` API Key 读不到。\n * 没有这枚凭据,胶囊就只能报\"本 Key 已用\"。\n *\n * 而没有这个面板时,唯一的配法是让用户去手改 `~/.dsh/.credentials.yaml` ——\n * 那是 DSH 的凭据库,手滑一个缩进整份凭据就读不出来了。所以这里做三件事:\n *\n * 1. 只问**一样东西**:令牌。引用名(`UR_ACCESS_TOKEN`)由宿主从设置投影推导,\n * 用户不用知道它叫什么;\n * 2. 粘贴即存:`⌘V` 或按钮都行,存完立刻重取一次读数(`saveProviderToken` 里做);\n * 3. 令牌**不回显**:输入框 `type=password`,store 不持有它,宿主也不回传。\n *\n * 令牌本身从不进 localStorage、不进日志、不进快照 —— 它只走一次 RPC 到宿主的凭据服务。\n */\n\nimport React from 'react'\n\nimport { useStore } from '../hooks/use-store.js'\nimport { h } from '../shared.js'\nimport type { HudStore, Translate } from '../shared.js'\n\n/** 面板 props:取词与 store(面板状态在快照的 `tokenPanel` 里)。 */\nexport interface TokenPanelProps {\n store: HudStore\n /** 取词。框架按 `locale: NS` 注入;缺了就回键名(与主面板同一套兜底,宁丑不崩)。 */\n t?: Translate\n}\n\n/**\n * 后台凭据设置面板。\n *\n * 平时返回 `null`(不占位);`tokenPanel.open` 为真时才渲染。\n *\n * @param props - 取词与 store。\n * @returns 面板节点或 `null`。\n */\nexport function TokenPanel(props: TokenPanelProps): React.ReactElement | null {\n var store = props.store\n // `t` 缺了也**不能崩**:面板是点胶囊弹出来的,崩掉就退化成\"点了没反应\"——\n // 而这正是这一整个功能要修掉的那种体验。\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var state = useStore(store)\n var panel = state.tokenPanel\n\n // 受控输入:草稿留在组件自己这儿,**不进 store** —— store 是会被 probe 打印的东西。\n var draftRef = React.useState('')\n var draft = draftRef[0]\n var setDraft = draftRef[1]\n\n // 面板关掉就清空草稿:不然下次打开还留着上一次粘的令牌。\n React.useEffect(\n function () {\n if (!panel.open) setDraft('')\n },\n [panel.open],\n )\n\n if (!panel.open) return null\n var provider = panel.provider ?? ''\n\n /** 存一次;失败原因由 store 写进 `tokenPanel.error`。 */\n function submit(): void {\n var value = draft.trim()\n if (value === '' || panel.saving) return\n setDraft('')\n void store.saveProviderToken(value)\n }\n\n var children: React.ReactNode[] = [\n h(\n 'div',\n { key: 'head', className: 'dshh-tokenHead' },\n h('span', { key: 'title', className: 'dshh-tokenTitle' }, t('balance.tokenTitle')),\n h('span', { key: 'provider', className: 'dshh-tokenProvider' }, provider),\n ),\n h('div', { key: 'hint', className: 'dshh-tokenHint' }, t('balance.tokenHint')),\n ]\n\n children.push(\n h('input', {\n key: 'input',\n className: 'dshh-tokenInput',\n 'data-dsh-hud': 'token-input',\n type: 'password',\n value: draft,\n placeholder: t('balance.tokenPlaceholder'),\n spellCheck: false,\n autoComplete: 'off',\n disabled: panel.saving,\n onInput: function (event: { target?: { value?: unknown } }) {\n var next = event && event.target ? event.target.value : ''\n setDraft(typeof next === 'string' ? next : '')\n },\n onKeyDown: function (event: { key?: string; preventDefault?: () => void }) {\n if (event && event.key === 'Enter') {\n if (typeof event.preventDefault === 'function') event.preventDefault()\n submit()\n }\n if (event && event.key === 'Escape') store.closeTokenPanel()\n },\n }),\n )\n\n // 屏上只留一句话:**只存储于本地**(用户只想知道它会不会往外传)。\n // 引用名(`UR_ACCESS_TOKEN`)挪进 `title` —— 肉眼看不见,但排查时鼠标一停就知道\n // 该去凭据里翻哪个名字删掉,不必为此在白话文案里塞一个变量名。\n var refTitle =\n typeof panel.ref === 'string' && panel.ref !== ''\n ? (panel.provider ?? '') + ' · ' + panel.ref\n : undefined\n children.push(\n h(\n 'div',\n { key: 'ref', className: 'dshh-tokenRef', 'data-dsh-hud': 'token-ref', title: refTitle },\n t('balance.tokenRef'),\n ),\n )\n\n // 顺序是 [取消][保存]:整行右对齐,所以保存落在最右边(主操作靠边是对话框惯例)。\n // 早先是 [保存][取消],保存夹在左边,看着像\"次要按钮\"。\n children.push(\n h(\n 'div',\n { key: 'actions', className: 'dshh-tokenActions', 'data-dsh-hud': 'token-actions' },\n h(\n 'button',\n {\n key: 'cancel',\n type: 'button',\n className: 'dshh-tokenCancel',\n 'data-dsh-hud': 'token-cancel',\n onClick: function () {\n store.closeTokenPanel()\n },\n },\n t('balance.tokenCancel'),\n ),\n h(\n 'button',\n {\n key: 'save',\n type: 'button',\n className: 'dshh-tokenSave',\n 'data-dsh-hud': 'token-save',\n disabled: panel.saving || draft.trim() === '',\n onClick: submit,\n },\n panel.saving ? t('balance.tokenSaving') : t('balance.tokenSave'),\n ),\n ),\n )\n\n if (panel.error !== null) {\n children.push(\n h(\n 'div',\n { key: 'error', className: 'dshh-tokenError', role: 'alert' },\n t('balance.tokenError.' + panel.error),\n ),\n )\n }\n\n return h(\n 'div',\n {\n className: 'dshh-tokenPanel',\n 'data-dsh-hud': 'token-panel',\n role: 'dialog',\n 'aria-label': t('balance.tokenTitle'),\n },\n children,\n )\n}\n","/**\n * 侧栏那两处按需展示:⋯ 菜单里的会话摘要、悬停卡片。\n */\n\nimport { branchLabel, rowSummaryOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `⋯` 菜单摘要的 props(座位 `sidebar.workspaces.session.menu.item`)。 */\nexport interface SessionMenuSummaryProps {\n t?: Translate\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n store: HudStore\n}\n\n/** 悬停卡片的 props(座位 `sidebar.session.row.hover`)。 */\nexport interface SessionRowHoverChipProps {\n t?: Translate\n sessionId?: string | null\n store: HudStore\n}\n\n/**\n * `⋯` 菜单里的只读摘要(座位 `sidebar.workspaces.session.menu.item`)。\n *\n * 为什么要有这一处:会话行**行尾的常亮位置归产品**(`time` 自适应宽度 + 悬停才换上的动作条),\n * 插件在行尾拿不到常亮的座位;行首那格只有 16px,只能放一个双字符标记。所以要\"完整信息 +\n * 不用等悬停卡片那 800ms\",`⋯` 菜单是最实在的地方 —— 点一下就有,且不用把鼠标停住。\n *\n * 它是**只读**的:不做任何动作,所以做得像一条说明(弱化颜色、不响应 hover 高亮);\n * 工作区干净时也照实说\"干净\",而不是消失(那样用户会以为没有这个功能)。\n */\nexport function SessionMenuSummary(props: SessionMenuSummaryProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n\n var head: string\n if (summary.row.repo === true) {\n head = branchLabel(summary.row, t)\n if (summary.dirty === true) head += '*'\n var aheadOf = summary.row.ahead\n var behindOf = summary.row.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n var counts = summary.shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n head += counts === '' ? ' · ' + t('panel.clean') : ' · ' + counts\n } else if (summary.row.error !== null) {\n head = t('rows.error', { code: String(summary.row.error) })\n } else {\n head = t('rows.noRepo')\n }\n\n var own: string[] = []\n if (summary.mine > 0) own.push(t('session.pill', { n: String(summary.mine) }))\n if (summary.possible > 0) {\n own.push(t('rows.possible', { n: String(summary.possible), now: String(summary.possibleNow) }))\n }\n var ownLine = own.length === 0 ? t('rows.ownNone') : own.join(' · ')\n\n return h(\n 'div',\n {\n className: 'dshh-menureadout',\n 'data-dsh-hud': 'menu-readout',\n title: summary.row.cwd || undefined,\n },\n [\n h('div', { key: 'git', className: 'dshh-menureadoutLine' }, head),\n h('div', { key: 'own', className: 'dshh-menureadoutLine' }, ownLine),\n ],\n )\n}\n\n/**\n * 侧栏会话行**悬停卡片**里的那一块(座位 `sidebar.session.row.hover`)。\n *\n * 动作条那一枚只有图标级宽度(`!4 ●3`),明细放不下;悬停卡是宽敞的地方 ——\n * 分支/上游、分类型计数、这个会话自己的改动,都在这里说清楚。\n * 没有任何可说的(干净、且本会话没改过)就整块不画,卡片保持原来的样子。\n */\nexport function SessionRowHoverChip(props: SessionRowHoverChipProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n // 上面已确认 summary 非空,它带的 row 就是刚落下来的那一行(同一个对象)。\n var rowData = summary.row\n var mineCount = summary.mine\n var possibleCount = summary.possible\n var shorthands = summary.shorthands\n\n var lines: string[] = []\n if (rowData.repo === true) {\n var head = branchLabel(rowData, t)\n if (rowData.dirty === true) head += '*'\n var aheadOf = rowData.ahead\n var behindOf = rowData.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n if (shorthands.length > 0) {\n head +=\n ' · ' +\n shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n } else {\n // 干净也要说出来 —— 与悬停说明、⋯ 菜单同一个口径(空着看不出是\"干净\"还是\"没读到\")。\n head += ' · ' + t('panel.clean')\n }\n lines.push(head)\n } else if (rowData.repo === false && rowData.error === null) {\n lines.push(t('rows.noRepo'))\n }\n if (rowData.error) lines.push(t('rows.error', { code: String(rowData.error) }))\n var own: string[] = []\n if (mineCount > 0) own.push(t('session.pill', { n: String(mineCount) }))\n if (possibleCount > 0) {\n own.push(t('rows.possible', { n: String(possibleCount), now: String(summary.possibleNow) }))\n }\n // 和第二行对齐:没有改动记录时也明说一句 —— 否则卡片上会缺一行,看着像没加载出来。\n lines.push(own.length === 0 ? t('rows.ownNone') : own.join(' · '))\n if (lines.length === 0) return null\n\n return h(\n 'div',\n {\n className: 'dshh-rowhover',\n 'data-dsh-hud': 'row-hover',\n },\n lines.map(function (line, index) {\n return h('div', { key: String(index), className: 'dshh-rowhoverLine' }, line)\n }),\n )\n}\n","/**\n * 轮尾那一行「本轮图片改动」:只列本轮动过的图,点缩略图走内置预览,⇄ 进 hud 看前后对比。\n */\n\nimport React from 'react'\n\nimport { looksLikeImage } from './image-diff.js'\nimport type { ImagePairEntry } from './image-diff.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudChange,\n HudStore,\n ImagePreview,\n ImageRequest,\n SessionChange,\n SessionChangeCandidate,\n SessionChangesState,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\ninterface TurnSessionSummaryLike {\n cwd?: string | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\ninterface TurnSessionsSnapshotLike {\n byId?: Record<string, TurnSessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\ntype TurnUseSessions = <T>(selector: (snapshot: TurnSessionsSnapshotLike) => T) => T\n\n/** 「本轮改过的一张图」:文件、档位(确定/命令旁听)、那条命令与最近触达时刻。 */\nexport interface TurnImagePick {\n file: string\n name: string\n tier: 'confirmed' | 'possible'\n reason: string\n at: number\n}\n\n/** `imageChangesOf()` 的结果:挑出来的条目 + `exact`(标题该说\"本轮\"还是\"本会话\")。 */\nexport interface TurnImageChanges {\n files: TurnImagePick[]\n exact: boolean\n}\n\n/** `TurnImages` 的 props:store + 框架注入的会话/回合信息 + 内置预览的打开函数。 */\nexport interface TurnImagesProps {\n t: Translate\n store: HudStore\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n useSessions?: TurnUseSessions | null\n /** 内置右侧文档预览的\"打开这个文件\"(缩略图点击用)。 */\n openFile?: ((path: string) => void) | null\n /** 槽位给的 TurnLocation(宿主内部形状),只用来取回合号。 */\n turn?: unknown\n}\n\n/**\n * 轮尾那条「本轮图片改动」:每项一张缩略图 + 文件名。\n *\n * 内置的改动卡片点一行是打开 review tab(图片在那儿看不见),这条是补上的门:\n * **点缩略图 → 槽位给的 `openFile(path)`**,即内置的右侧文档预览(图片原生渲染、可缩放);\n * 旁边那个 `⇄` 才打开 dsh-hud 面板看前后两张 —— 前后对比只有我们有。\n */\nexport function TurnImages(props: TurnImagesProps): React.ReactElement | null {\n var t = props.t\n var store = props.store\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var openFile = props.openFile\n var turnNumber = turnNumberOf(props.turn)\n\n // useSessions 是选择器 hook:选择器参数是必需的。\n var sessions =\n typeof useSessions === 'function'\n ? useSessions(function (snapshot) {\n return snapshot\n })\n : null\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n var state = useStore(store)\n // 缩略图要等字节;缓存本身不在快照里,所以取完自增一下触发重画。\n var tick = React.useState(0)\n var bump = tick[1]\n\n // 轮尾每个已完成的回合都会挂一次:绑定会话 + 没取过会话改动就取一次(宿主侧是内存读,很便宜)。\n React.useEffect(\n function () {\n if (sessionId === undefined || cwd === undefined) return undefined\n store.bindSession(sessionId, cwd)\n if (state.sessionChanges === undefined || state.sessionChanges.phase === 'idle')\n store.loadSessionChanges()\n return undefined\n },\n [sessionId, cwd],\n )\n\n var picked = imageChangesOf(state.sessionChanges, turnNumber)\n var shown = picked.files.slice(0, 4)\n var keys = shown\n .map(function (entry) {\n return entry.file\n })\n .join('\\u0000')\n\n React.useEffect(\n function () {\n if (keys === '' || typeof cwd !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`cwd` 是 `var`,TS 在回调里会重新放宽成可空)。\n const cwdOf = cwd\n var cancelled = false\n var wanted: ImageRequest[] = shown\n .map(function (entry) {\n return { cwd: cwdOf, path: entry.file, side: 'worktree' }\n })\n .filter(function (request) {\n return store.cachedImagePair(request) === null\n })\n if (wanted.length === 0) return undefined\n Promise.all(\n wanted.map(function (request) {\n return store.loadImagePair(request)\n }),\n ).then(function () {\n if (!cancelled)\n bump(function (count) {\n return count + 1\n })\n })\n return function () {\n cancelled = true\n }\n },\n [keys, cwd],\n )\n\n // 会话改动还没到、或这一轮没动过图:整条不渲染(不在每条轮尾留个空壳)。\n if (state.sessionChanges === undefined || state.sessionChanges.phase !== 'ready') return null\n if (shown.length === 0) return null\n\n var rest = picked.files.length - shown.length\n\n function chip(entry: TurnImagePick): React.ReactElement {\n var thumb = thumbSideOf(\n typeof cwd === 'string'\n ? store.cachedImagePair({ cwd: cwd, path: entry.file, side: 'worktree' })\n : null,\n )\n var dot = entry.name.lastIndexOf('.')\n var extension = dot > 0 ? entry.name.slice(dot + 1).toUpperCase() : '?'\n // `◐` 是命令旁听来的(猜的):说清这一点,并把那条命令放进悬停里。\n var hint = entry.file + ' · ' + t('turnImages.open')\n if (entry.tier === 'possible' && entry.reason !== '')\n hint += ' · ' + t('session.possibleTitle', { reason: entry.reason })\n return h(\n 'span',\n {\n key: entry.file,\n className: 'dshh-imgstripItem',\n 'data-dsh-hud': 'turn-image',\n 'data-file': entry.file,\n 'data-tier': entry.tier,\n },\n [\n h(\n 'button',\n {\n key: 'open',\n type: 'button',\n className: 'dshh-imgstripOpen',\n title: hint,\n onClick: function () {\n if (typeof openFile === 'function') openFile(entry.file)\n },\n },\n [\n thumb === null\n ? h(\n 'span',\n { key: 'ph', className: 'dshh-imgstripThumb dshh-imgstripThumbEmpty' },\n extension,\n )\n : h('img', {\n key: 'img',\n className: 'dshh-imgstripThumb',\n alt: entry.name,\n src: 'data:' + thumb.mime + ';base64,' + String(thumb.base64 || ''),\n }),\n entry.tier === 'possible'\n ? h('span', { key: 'tier', className: 'dshh-imgstripTier', title: hint }, '◐')\n : null,\n h('span', { key: 'name', className: 'dshh-imgstripName' }, entry.name),\n ],\n ),\n h(\n 'button',\n {\n key: 'compare',\n type: 'button',\n className: 'dshh-imgstripCompare',\n title: t('turnImages.compare'),\n 'data-dsh-hud': 'turn-image-compare',\n onClick: function () {\n // store 边界:这里只知道文件路径,而 `openDiff` 只读 file/path/origFile,按调用契约补形状。\n var change = { file: entry.file, path: entry.file, origFile: null } as HudChange\n store.openPanel(true)\n store.openDiff(change)\n },\n },\n '⇄',\n ),\n ],\n )\n }\n\n return h('div', { className: 'dshh-imgstrip', 'data-dsh-hud': 'turn-images' }, [\n h(\n 'span',\n { key: 'title', className: 'dshh-imgstripTitle' },\n picked.exact ? t('turnImages.title') : t('turnImages.titleSession'),\n ),\n h('span', { key: 'items', className: 'dshh-imgstripItems' }, shown.map(chip)),\n rest > 0\n ? h(\n 'span',\n { key: 'more', className: 'dshh-imgstripMore' },\n t('turnImages.more', { n: String(rest) }),\n )\n : null,\n ])\n}\n\n/**\n * 从会话改动里挑出\"这一轮改过的图片\",最近的在前。\n *\n * **两档都收**:`●` 确定改过(写文件 / 编辑工具)与 `◐` 命令旁听(`>`、`tee`、`cp`、`rm`…)。\n * 只收确定档不够用 —— 图几乎都是脚本/命令产出的(`python gen.py`、`cp`、`curl -o`),\n * 那些永远进不了确定档,而\"点一下看图\"对它们一样有用。`◐` 是猜的,所以 chip 上带 `◐`、\n * 悬停给出那条命令(措辞与面板里改动清单一致)。\n *\n * 回合号拿不到(`turnNumberOf` 回 null)就不按回合收窄,退回\"本会话最近的图片改动\"。\n *\n * @returns `{ files, exact }`;`exact` 为 false 表示标题该说\"本会话\"而不是\"本轮\"。\n */\nexport function imageChangesOf(\n changes: SessionChangesState,\n turnNumber: number | null,\n): TurnImageChanges {\n if (changes === null || changes === undefined) return { files: [], exact: turnNumber !== null }\n var confirmed: Record<string, SessionChange> = changes.files ? changes.files : {}\n var guessed: Record<string, SessionChangeCandidate> = changes.possible ? changes.possible : {}\n var picked: TurnImagePick[] = []\n var seen: Record<string, true> = {}\n\n for (var file in confirmed) {\n if (!Object.prototype.hasOwnProperty.call(confirmed, file)) continue\n if (!looksLikeImage(file)) continue\n var record: Partial<SessionChange> = confirmed[file] || {}\n if (turnNumber !== null) {\n var turns = Array.isArray(record.turns) ? record.turns : []\n if (turns.indexOf(turnNumber) < 0 && record.lastTurn !== turnNumber) continue\n }\n seen[file] = true\n picked.push({\n file: file,\n name: baseNameOf(file),\n tier: 'confirmed',\n reason: '',\n at: typeof record.lastAt === 'number' ? record.lastAt : 0,\n })\n }\n\n for (var guess in guessed) {\n if (!Object.prototype.hasOwnProperty.call(guessed, guess)) continue\n if (seen[guess] === true) continue\n if (!looksLikeImage(guess)) continue\n var item: Partial<SessionChangeCandidate> = guessed[guess] || {}\n // 命令旁听只记\"最近一次\"的回合号(没有 turns 列表),所以只能按 lastTurn 收窄。\n if (turnNumber !== null && item.lastTurn !== turnNumber) continue\n seen[guess] = true\n picked.push({\n file: guess,\n name: baseNameOf(guess),\n tier: 'possible',\n reason: typeof item.reason === 'string' ? item.reason : '',\n at: typeof item.lastAt === 'number' ? item.lastAt : 0,\n })\n }\n\n picked.sort(function (left, right) {\n return right.at - left.at\n })\n return { files: picked, exact: turnNumber !== null }\n}\n\n/** 缩略图用哪一侧的字节:优先工作区,其次基线(新增/删除时只有一侧有)。 */\nexport function thumbSideOf(entry: unknown): ImagePreview | null {\n // store 边界:图片缓存条目的形状由 store 的 image 片构造,这里只收窄成声明过的联合。\n var pair =\n entry !== null && entry !== undefined && typeof entry === 'object'\n ? (entry as ImagePairEntry)\n : null\n if (pair === null || pair.phase !== 'ready' || !pair.value) return null\n var value = pair.value\n if (value.after && value.after.kind === 'image') return value.after\n if (value.before && value.before.kind === 'image') return value.before\n return null\n}\n\n/**\n * 槽位给的是宿主的 `TurnLocation`。\n *\n * 形状属于宿主内部约定(槽位文档只说\"receives the Turn\"),所以这里宽松取值:\n * 取不到就回 null,由调用方退到\"本会话\"的说法,而不是整条不显示。\n */\nexport function turnNumberOf(turn: unknown): number | null {\n if (typeof turn === 'number' && isFinite(turn)) return turn\n if (turn !== null && turn !== undefined && typeof turn === 'object') {\n // 槽位边界:TurnLocation 的字段形状属于框架内部约定,这里只取用到的 `turn`。\n var record = turn as { turn?: unknown }\n if (typeof record.turn === 'number') return record.turn\n }\n return null\n}\n\n/** 只取文件名(chip 上放不下整条路径)。 */\nexport function baseNameOf(file: string): string {\n var parts = String(file).split('/')\n return parts[parts.length - 1] || String(file)\n}\n","/**\n * 输入框(composer)读写与 ↑ 召回。\n *\n * 这里全部是**纯 DOM 操作**(Lexical 的 contenteditable),最讲究的是\"写进去之后到底成没成\":\n * 粘贴是异步落地的,所以每次写完都要等一拍再核对(`settleComposer`),否则会写第二遍。\n */\n\nimport { probe } from './shared.js'\nimport type { HudStore } from './shared.js'\n\n/* ------------------------------------------------------- 输入框的 DOM 面 */\n\n/**\n * 插件只用到输入框根节点的这几样:找根(`getAttribute` / `isContentEditable` /\n * `parentElement`)、读文本(`innerText` / `textContent`)、写回(`textContent` /\n * `dispatchEvent`)。用**结构类型**而不是 `HTMLElement`:测试里的假 DOM 也满足它,\n * 真实节点天然满足 —— 这里要的本来也不是\"一个 HTMLElement\",而是\"能当输入框用的节点\"。\n */\ninterface ComposerRoot {\n getAttribute?: (name: string) => string | null\n isContentEditable?: boolean\n parentElement?: ComposerRoot | null\n innerText?: string\n textContent?: string | null\n dispatchEvent?: (event: Event) => boolean\n}\n\n/**\n * 结构类型 → 真 `Node`。运行时它本来就是 contenteditable 元素;`ComposerRoot` 只是为了让\n * 测试里的假 DOM 也能满足,所以这里不做运行时检查,只把类型交回给 DOM API。\n */\nfunction nodeOf(root: ComposerRoot): Node {\n return root as unknown as Node\n}\n\n/** 历史翻页方向。 */\ntype HistoryDirection = 'up' | 'down'\n/** 翻完之后光标摆在哪一端。 */\ntype HistoryCaret = 'start' | 'end'\n\n/** 翻页状态机自己的状态:不在历史里时 `index === -1`。 */\ninterface PromptHistoryNavState {\n index: number\n draft: string | null\n}\n\n/** 翻页输入:历史条目(新的在前)与当前输入框的文本/光标。 */\ninterface PromptHistoryInput {\n entries: string[]\n text?: string\n caret?: number\n}\n\n/** 状态机结果:没接管时调用方什么都不做;接管时必然给出要写入的文本与光标位置。 */\ntype PromptHistoryStep =\n | { handled: false; state: PromptHistoryNavState }\n | { handled: true; state: PromptHistoryNavState; text: string; caret: HistoryCaret }\n\n/* ------------------------------------------------------------- 文案 */\n\n/* --------------------------------------------------------- 本地 UI 状态 */\n\n/**\n * 光标此刻允许上下翻历史吗?—— 照抄 OpenCode Desktop 的 `canNavigateHistoryAtCursor`。\n *\n * 它的规矩比 Codex 保守得多:**不在历史里时,`↑` 只在输入框完全为空时才接管**。\n * 于是多行草稿里 `↑` 永远是光标移动,跟内置的 `/` 命令、`@` 提及弹窗也天然不冲突\n * (弹窗必然带着文字)。进了历史之后,只要光标在两端就能继续翻。\n */\nexport function canNavigatePromptHistory(\n direction: HistoryDirection,\n text: string,\n caret: number,\n inHistory: boolean,\n): boolean {\n var length = typeof text === 'string' ? text.length : 0\n var position = Math.max(0, Math.min(typeof caret === 'number' ? caret : 0, length))\n if (inHistory) return position === 0 || position === length\n if (direction === 'up') return position === 0 && length === 0\n return position === length\n}\n\n/**\n * `↑` / `↓` 翻 prompt 历史的**纯状态机**(语义照抄 OpenCode Desktop 的 `navigatePromptHistory`)。\n *\n * `index === -1` 表示\"不在历史里\";`entries[0]` 是最新一条。往旧翻时先把当前草稿存进\n * `draft`,一直翻到最新再往下就把草稿**原样放回**(连光标位置都按方向摆好:往旧放开头、\n * 往新放末尾)。没接管的情况一律回 `handled: false`,调用方据此**不**调用 preventDefault。\n */\nexport function promptHistoryStep(\n state: PromptHistoryNavState,\n direction: HistoryDirection,\n input: PromptHistoryInput | null | undefined,\n): PromptHistoryStep {\n var entries = (input && input.entries) || []\n var text = input && typeof input.text === 'string' ? input.text : ''\n var caret = input && typeof input.caret === 'number' ? input.caret : 0\n var index = state.index\n var draft = state.draft\n\n if (!canNavigatePromptHistory(direction, text, caret, index >= 0))\n return { handled: false, state: state }\n\n if (direction === 'up') {\n if (entries.length === 0) return { handled: false, state: state }\n if (index === -1)\n return { handled: true, state: { index: 0, draft: text }, text: entries[0], caret: 'start' }\n if (index < entries.length - 1) {\n return {\n handled: true,\n state: { index: index + 1, draft: draft },\n text: entries[index + 1],\n caret: 'start',\n }\n }\n return { handled: false, state: state } // 已经到最旧一条:把 ↑ 还给编辑器\n }\n\n if (index > 0)\n return {\n handled: true,\n state: { index: index - 1, draft: draft },\n text: entries[index - 1],\n caret: 'end',\n }\n if (index === 0) {\n return {\n handled: true,\n state: { index: -1, draft: null },\n text: draft === null || draft === undefined ? '' : draft,\n caret: 'end',\n }\n }\n return { handled: false, state: state }\n}\n\n/* -------------------------------------------- 输入框的 ↑ 召回(纯文本) */\n\n/**\n * 从一个事件目标往上找\"输入框根节点\"。\n *\n * DSH 的输入框是 **Lexical** 的 contenteditable(根节点带 `data-lexical-editor=\"true\"`),\n * 不是 textarea —— 所以不能读 `value`。手写 parentElement 上溯(不用 `closest`):\n * 一来少依赖一个 API,二来深度有上限,绝不会误判到页面别处。\n */\nexport function composerRootOf(target: unknown): ComposerRoot | null {\n // 事件目标是 DOM 边界:调用方可能是 EventTarget,我们按结构类型读它的属性。\n var node = target as ComposerRoot | null | undefined\n for (var depth = 0; depth < 6 && node !== null && node !== undefined; depth += 1) {\n if (typeof node.getAttribute === 'function') {\n var marker = node.getAttribute('data-lexical-editor')\n var editable =\n typeof node.isContentEditable === 'boolean'\n ? node.isContentEditable\n : node.getAttribute('contenteditable') === 'true'\n if (marker === 'true' || editable) return node\n }\n node = node.parentElement\n }\n return null\n}\n\n/** 输入框里的纯文本(Lexical 会把内容画在 DOM 里,`innerText` 最贴近用户看到的)。 */\nexport function composerText(root: ComposerRoot | null | undefined): string {\n if (root === null || root === undefined) return ''\n var text =\n typeof root.innerText === 'string'\n ? root.innerText\n : typeof root.textContent === 'string'\n ? root.textContent\n : ''\n return text.replace(/\\u200b/g, '')\n}\n\n/**\n * \"输入框实际上是空的吗\"用的归一化文本。\n *\n * Lexical 的空根节点在 DOM 里通常是 `<p><br></p>`,`innerText` 会给出一个换行符 ——\n * 拿它当\"有内容\"就会让 `↑` 永远排不上队(整个功能静默失效)。所以去掉零宽字符与\n * **只在末尾的空白**:开头的空格是用户真的敲进去的,不能吞。\n */\nexport function composerTextKey(root: ComposerRoot | null | undefined): string {\n return composerText(root).replace(/\\s+$/, '')\n}\n\n/** 写入是否成功:两边都归一化再比 —— Lexical 可能补一个尾换行,那不是\"没写进去\"。 */\nexport function composerTextMatches(text: string, expected: string): boolean {\n return text.replace(/\\s+$/, '') === expected.replace(/\\s+$/, '')\n}\n\n/** 光标在输入框里的字符偏移;没有选区、有选中内容、或拿不到选区时回 null(一律不接管)。 */\nexport function composerCaretOffset(root: ComposerRoot): number | null {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed !== true) return null\n var node = selection.focusNode\n if (!node) return null\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n var before = range.cloneRange()\n try {\n before.setEnd(node, selection.focusOffset)\n } catch (error) {\n return null\n }\n return before.toString().length\n}\n\n/** 把光标放到开头或末尾(`↑` 往旧翻放开头,`↓` 往回翻放末尾 —— 与 OpenCode 一致)。 */\nexport function placeCaretInComposer(root: ComposerRoot, position: HistoryCaret): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n range.collapse(position !== 'end')\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 光标没摆好不影响内容已经写进去了\n }\n}\n\n/** 全选输入框内容(模拟粘贴前先选中,粘贴才能\"替换\"而不是\"追加\")。 */\nexport function selectComposerContents(root: ComposerRoot): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 选不中也还能走 execCommand 那条退路\n }\n}\n\n/** 等\"写入落地\":微任务总有,有 rAF 的浏览器里再多等一帧(Lexical 的粘贴是帧后落 DOM)。 */\nexport function settleComposer(): Promise<void> {\n var chain = Promise.resolve()\n if (typeof window.requestAnimationFrame === 'function') {\n chain = chain.then(function () {\n return new Promise(function (resolve) {\n window.requestAnimationFrame(function () {\n resolve()\n })\n })\n })\n }\n return chain\n}\n\n/**\n * 把一段纯文本写进 Lexical 输入框 —— **三层依次降级,每层都等落地了再回读**。\n *\n * 为什么要等:Lexical 的粘贴处理是**异步**的(它得先把剪贴板读出来)。写完立刻回读会读到\n * 旧内容、误判成失败,于是下一层再插一次 —— 实测表现就是**文字出现两遍**(第一次真机上\n * 就是这样:粘贴插了一遍,`insertText` 又追加了一遍)。\n *\n * 三层次序:① 模拟粘贴(Lexical 的正规插入通道)② `execCommand(\"insertText\")`\n * ③ 直接改文本 + 派发 `input`。**每次降级前都重新全选**,让重试是\"替换\"而不是\"追加\"。\n */\nexport function writeComposerText(\n root: ComposerRoot,\n value: string,\n caret: HistoryCaret,\n): Promise<boolean> {\n function satisfied() {\n return composerTextMatches(composerText(root), value)\n }\n\n return settleComposer()\n .then(function () {\n if (typeof window.ClipboardEvent !== 'function' || typeof window.DataTransfer !== 'function')\n return false\n if (typeof root.dispatchEvent !== 'function') return false\n try {\n selectComposerContents(root)\n var data = new window.DataTransfer()\n data.setData('text/plain', value)\n root.dispatchEvent(\n new window.ClipboardEvent('paste', {\n clipboardData: data,\n bubbles: true,\n cancelable: true,\n }),\n )\n } catch (error) {\n return false\n }\n return settleComposer().then(satisfied)\n })\n .then(function (wrote) {\n if (wrote) return true\n try {\n if (typeof document.execCommand === 'function') {\n selectComposerContents(root)\n if (document.execCommand('insertText', false, value) === true) {\n return settleComposer().then(satisfied)\n }\n }\n } catch (error) {\n // 落到最后一层\n }\n return settleComposer().then(function () {\n if (satisfied()) return true\n try {\n selectComposerContents(root)\n root.textContent = value\n // 上一层的不变量(dispatchEvent 存在)不跨回调作用域,这里用 ! 保留原来的\"抛了就被 catch 掉\"语义。\n if (typeof window.Event === 'function')\n root.dispatchEvent!(new window.Event('input', { bubbles: true }))\n return true\n } catch (error) {\n return false\n }\n })\n })\n .then(function (wrote) {\n if (!wrote) return false\n placeCaretInComposer(root, caret)\n // 重写内容会让编辑器失焦/滚动跳,下一帧再摆一次光标(OpenCode 也是这么做的)。\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(function () {\n placeCaretInComposer(root, caret)\n })\n }\n return true\n })\n}\n\n/**\n * 把\"输入框里按 ↑/↓ 召回上一条 prompt\"接到窗口上 —— 只接线,判断与翻页逻辑都在纯函数里。\n *\n * 门禁(任一不满足就**完全不介入**,`↑`/`↓` 行为与不装插件时一模一样):\n * 默认行为没被别处吃掉、不是输入法组合中、无修饰键、目标是输入框、历史非空、\n * 选区收起、光标位置通过 `canNavigatePromptHistory`。**接管了才 preventDefault**。\n */\nexport function bindPromptHistory(store: HudStore): () => void {\n var nav: { sessionId: string | null; index: number; draft: string | null; writing: boolean } = {\n sessionId: null,\n index: -1,\n draft: null,\n writing: false,\n }\n\n function onKeyDown(event: KeyboardEvent): void {\n if (event === null || event === undefined) return\n if (event.defaultPrevented === true || event.isComposing === true) return\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n if (event.altKey === true || event.ctrlKey === true || event.metaKey === true) return\n if (typeof event.preventDefault !== 'function') return\n\n var root = composerRootOf(event.target)\n if (root === null) return\n\n var snapshot = store.getSnapshot().promptHistory\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId !== null\n ? snapshot.value\n : null\n var entries =\n value !== null &&\n value !== undefined &&\n value.available === true &&\n Array.isArray(value.prompts)\n ? value.prompts\n : []\n if (entries.length === 0) return\n\n if (nav.sessionId !== snapshot.sessionId) {\n nav.sessionId = snapshot.sessionId\n nav.index = -1\n nav.draft = null\n }\n\n var caret = composerCaretOffset(root)\n if (caret === null) return\n var direction: HistoryDirection = event.key === 'ArrowUp' ? 'up' : 'down'\n // 归一化后再判\"空不空\"与\"光标在哪端\":Lexical 空输入框里的那个换行不算内容。\n var text = composerTextKey(root)\n var step = promptHistoryStep({ index: nav.index, draft: nav.draft }, direction, {\n entries: entries,\n text: text,\n caret: Math.max(0, Math.min(caret, text.length)),\n })\n if (!step.handled) return\n\n // 到这里才确定要接管:先挡住默认行为(别让 ↑ 先把光标移走),再写内容。\n event.preventDefault()\n if (typeof event.stopPropagation === 'function') event.stopPropagation()\n\n // 写入是异步的(粘贴要等落地),但翻页状态先记下:连按 ↑ 时不会两次都从同一条开始。\n // 真写不进去再回滚 —— 宁可状态与画面都对不上时退回一步,也不要悄悄漂移。\n var previous = { index: nav.index, draft: nav.draft }\n nav.index = step.state.index\n nav.draft = step.state.draft\n nav.writing = true\n\n writeComposerText(root, step.text, step.caret).then(function (wrote) {\n nav.writing = false\n if (wrote === true) {\n probe.log('prompt-history', {\n index: nav.index,\n entries: entries.length,\n direction: direction,\n })\n return\n }\n nav.index = previous.index\n nav.draft = previous.draft\n probe.log('prompt-history: 写不进输入框(三层都失败),已回滚并放弃接管', {\n entries: entries.length,\n })\n })\n }\n\n /** 用户自己动了内容(不是我们写进去的)→ 退出历史、草稿作废,免得把草稿还原到新内容上。 */\n function onInput(event: Event): void {\n if (nav.writing) return\n if (composerRootOf(event && event.target) === null) return\n nav.index = -1\n nav.draft = null\n }\n\n if (typeof window.addEventListener !== 'function') return function () {}\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('input', onInput, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('input', onInput, true)\n }\n}\n","/**\n * 两份词典(`dshHud` 命名空间)。键一致、值分别是中文与英文;`t()` 取不到会回落成键本身,\n * 所以\"漏翻\"会直接显示给人看 —— 有一条用例专门盯这件事(它扫的是源码,不是产物)。\n */\n\nexport const zh = {\n 'panel.title': 'Git',\n 'panel.refresh': '刷新',\n 'panel.reset': '复位位置',\n 'panel.close': '收起面板',\n 'panel.dragHint': '拖动标题可移动面板,双击标题复位',\n 'panel.loading': '读取中…',\n 'panel.clean': '工作区干净,没有未提交的改动',\n 'panel.notRepo': '当前会话的工作区不是 Git 仓库',\n 'panel.noSession': '还没有绑定到会话的工作区',\n 'panel.summary': '改动概览',\n 'panel.changes': '改动清单',\n 'panel.truncated': '仅显示前 200 条',\n 'panel.readonly': '只读',\n 'panel.upstream': '上游',\n 'panel.updated': '更新于 ',\n 'panel.ahead': '领先 ',\n 'panel.behind': '落后 ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': '点击一行查看该文件的差异',\n 'panel.rowKeys': '在面板里按过之后再按 ↑/↓ 选行、Enter 打开',\n 'panel.commits': '最近提交',\n 'panel.tags': '标签',\n 'panel.tagsEmpty': '这个仓库还没有标签',\n 'panel.tagsCapped': '只显示最近 {n} 个',\n 'tag.click': '点一下看它指向的提交',\n 'tag.annotated': '附注标签',\n 'tag.lightweight': '轻量标签',\n 'diff.close': '关闭差异',\n 'diff.resize': '拖动右边缘调整差异宽度',\n 'diff.resizeCommit': '拖动调整提交明细栏宽度',\n 'diff.worktree': '未暂存',\n 'diff.index': '已暂存',\n 'diff.wrap': '自动换行',\n 'diff.copy': '复制差异',\n 'diff.copied': '已复制',\n 'diff.empty': '这一侧没有差异',\n 'diff.binary': '二进制文件,没有可显示的文本差异',\n // ── 图片预览(二进制改动里认得出的那几种) ──\n 'image.loading': '读取图片…',\n 'image.pair': '并排',\n 'image.flip': '切换',\n 'image.beforeWorktree': '索引',\n 'image.afterWorktree': '工作区',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': '索引',\n 'image.beforeCommit': '父提交',\n 'image.afterCommit': '这次提交',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': '尺寸 {from} → {to}',\n 'image.tooLarge': '超过 {limit} MB 预览上限({size})',\n 'image.notImage': '不是可预览的图片({size})',\n 'image.missing': '这一侧没有这个文件',\n 'image.zoomOut': '缩小',\n 'image.zoomIn': '放大',\n 'image.fitWidth': '适应宽度',\n 'image.zoomValue': '{percent}%',\n // ── 会话计时胶囊 ──\n 'timer.title': '本次会话运行时长',\n 'timer.created': '创建于 {time}',\n 'timer.work': '其中模型 {llm}、工具 {tool}',\n 'timer.turns': '{turns} 轮 / {steps} 步',\n // ── 账号余额胶囊 ──\n 'balance.title': 'DeepSeek 账号余额',\n 'balance.bonus': '赠送余额',\n 'balance.updated': '更新于 {time}',\n 'balance.stale': '最近一次刷新失败,显示的是 {time} 的数',\n 'balance.delta': '较上次刷新 {delta}({time})',\n 'balance.providerRemaining': '余额 {amount}',\n 'balance.providerUsedKey': '本 Key 已用 {amount}',\n 'balance.providerUsedAccount': '账号已用 {amount}',\n 'balance.providerRequests': 'API 请求 {count}',\n 'balance.tokenTitle': '添加访问令牌',\n 'balance.tokenHint': '去站点「个人设置 → 访问令牌」新建,权限勾 profile:read,粘贴即可',\n 'balance.tokenPlaceholder': '粘贴访问令牌',\n 'balance.tokenSave': '保存',\n 'balance.tokenSaving': '保存中…',\n 'balance.tokenCancel': '取消',\n // 这一行换了两次:先是「将写入 UR_ACCESS_TOKEN」(用户问\"这是什么\"),\n // 再是「只存给 ur · UR_ACCESS_TOKEN」(用户还是只想看一句话)。\n // 用户真正关心的是\"这东西会往哪儿传\",答案就是:不往外传,只落在本地凭据库。\n 'balance.tokenRef': '只存储于本地',\n 'balance.tokenError.unknown-provider': '不认识这个提供方,先确认会话用的模型来自哪个站',\n 'balance.tokenError.not-supported': '这个站不支持后台令牌',\n 'balance.tokenError.no-credentials': '凭据服务不可用',\n 'balance.tokenError.bad-token': '令牌格式不对(不该有空格或换行)',\n 'balance.tokenError.failed': '保存失败,稍后再试',\n 'balance.tokenError.unsupported': '这个宿主不支持在这里保存',\n 'balance.tokenError.reason': '保存失败',\n 'balance.usedShort': '已用',\n 'balance.providerUnknown': '--',\n 'balance.reason.unsupported': '该站不提供余额接口',\n 'balance.reason.no-key': '未配置密钥',\n 'balance.reason.network': '网络失败',\n 'balance.reason.unknown-provider': '模型来源不在设置里',\n 'balance.reason.no-provider': '会话还没定下模型来源',\n 'balance.reason.no-session': '会话还没就绪',\n // ── 轮尾的「本轮图片改动」条 ──\n 'turnImages.title': '本轮图片改动',\n 'turnImages.titleSession': '本会话图片改动',\n 'turnImages.open': '在内置预览里打开',\n 'turnImages.compare': '在 dsh-hud 里前后对比',\n 'turnImages.more': '还有 {n} 个',\n 'diff.truncated': '差异过大,仅显示前面部分',\n 'diff.untrackedHint': '未跟踪文件,与空文件对比',\n 'diff.commitHint': '这次提交相对其父提交的差异(只读)',\n 'diff.hunkToggle': '点这一行折叠 / 展开这个 hunk(滚动到它后按 Space 一样)',\n 'commit.files': '这次提交改动',\n 'commit.close': '收起提交明细',\n 'commit.empty': '这次提交没有改动文件(空提交)',\n 'commit.loading': '读取这次提交…',\n 'commit.merge': '合并提交,按第一父提交比较',\n 'commit.unknown': '找不到这次提交:可能已被 rebase / amend 换掉,刷新面板再看',\n 'commit.click': '点击查看这次提交改了什么',\n // ── 会话归因(本会话改过哪些文件) ──\n 'session.line': '本会话改过 {n} 个文件',\n 'tools.now': '现在在跑:{name}',\n 'tools.total': '本会话共 {n} 次工具调用',\n 'tools.byTool': '各工具:',\n 'tools.skills': 'skills:',\n 'tools.recent': '最近:',\n 'rows.ownNone': '本会话没有改动记录',\n 'rows.noRepo': '不在 git 仓库里',\n 'rows.error': '读取失败({code})',\n 'rows.possible': '命令旁听到 {n} 个可能改动的路径({now} 个现在有差异)',\n 'review.count': '已审 {n}/{total}',\n 'review.staleCount': '{n} 个改动已变,需要重看',\n 'review.mark': '标成「这一版我审过了」(键盘:v)',\n 'review.unmark': '取消「已审」(键盘:v)',\n 'review.stale': '改动已变,需要重看(标记的是更早的那一版)',\n 'review.clear': '清空这个工作区的「已审」标记',\n 'review.clearShort': '清空已审',\n 'session.latestTurn': '最近一轮 {n} 个',\n 'session.turn': '第 {n} 轮',\n 'session.turnWithPrompt': '第 {n} 轮:{prompt}',\n 'session.onlyLatest': '只看最近一轮',\n 'session.onlyLatestTitle': '把清单收窄到最近这一轮动过的文件(「这一轮改了什么」)',\n 'session.possible': '另有 {n} 个可能相关(命令旁听)',\n 'session.possibleDropped': '还有 {n} 个超出上限未记',\n 'session.possibleTitle': '可能相关(未确认):{reason}',\n 'session.commands': '最近跑过 {n} 条命令:',\n 'session.commandsMore': '…另有 {n} 条会写文件的命令',\n 'session.commandFailed': '(这条失败了)',\n 'session.gone': '{n} 个已不在 git 现状里(改回去了 / 被忽略 / 被覆盖)',\n 'session.dropped': '另有 {n} 个超出上限未记录',\n 'session.only': '只看本会话改的',\n 'session.onlyTitle':\n '把清单过滤成本会话动过的文件(确定的 + 命令旁听到的「可能相关」,与 git 现状求交)',\n 'session.mine': '本会话改的',\n 'session.other': '不是本会话改的(可能是你自己、编辑器或另一个会话)',\n 'session.writes': '写入 {n} 次',\n 'session.edits': '编辑 {n} 次',\n 'session.since': '自 {time} 起记录(宿主进程启动之前的不算)',\n 'session.pill': '本会话改过 {n} 个文件',\n 'session.empty': '本会话还没有改动(清掉上面的过滤器看看工作区现状)',\n 'pill.loading': '读取中…',\n 'pill.notRepo': '非 git 仓库',\n 'pill.error': '读取失败',\n 'pill.title': '点击展开 Git 面板',\n 'count.modified': '已修改',\n 'count.added': '已暂存新增',\n 'count.deleted': '已删除',\n 'count.renamed': '重命名',\n 'count.copied': '复制',\n 'count.typeChanged': '类型改变',\n 'count.untracked': '未跟踪',\n 'count.conflicted': '冲突',\n 'err.timeout': 'git 超时:仓库过大或磁盘繁忙',\n 'err.no-cwd': '工作区目录不存在',\n 'err.git-missing': '找不到 git 可执行文件',\n 'err.invalid-cwd': '无效的工作区路径',\n 'err.git-failed': 'git 执行失败',\n 'err.invalid-path': '无效的文件路径',\n 'err.stale-host': '宿主半还没有这个端点:插件刚更新,需要重启 DSH 让宿主半重新加载',\n 'err.transport': '无法连接到宿主半',\n}\n\nexport const en = {\n 'panel.title': 'Git',\n 'panel.refresh': 'Refresh',\n 'panel.reset': 'Reset position',\n 'panel.close': 'Collapse panel',\n 'panel.dragHint': 'Drag the title to move the panel; double-click it to reset',\n 'panel.loading': 'Loading…',\n 'panel.clean': 'Working tree clean — nothing uncommitted',\n 'panel.notRepo': 'The current session workspace is not a Git repository',\n 'panel.noSession': 'Not bound to a session workspace yet',\n 'panel.summary': 'Summary',\n 'panel.changes': 'Changes',\n 'panel.truncated': 'Showing the first 200 entries only',\n 'panel.readonly': 'Read-only',\n 'panel.upstream': 'upstream',\n 'panel.updated': 'Updated ',\n 'panel.ahead': 'ahead ',\n 'panel.behind': 'behind ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': 'Click a file to see its diff',\n 'panel.rowKeys': 'Once you have clicked in the panel: ↑/↓ to select a row, Enter to open it',\n 'panel.commits': 'Recent commits',\n 'panel.tags': 'Tags',\n 'panel.tagsEmpty': 'No tags in this repository yet',\n 'panel.tagsCapped': 'Showing the newest {n}',\n 'tag.click': 'Click to open the commit it points at',\n 'tag.annotated': 'Annotated tag',\n 'tag.lightweight': 'Lightweight tag',\n 'diff.close': 'Close diff',\n 'diff.resize': 'Drag the right edge to resize the diff',\n 'diff.resizeCommit': 'Drag to resize the commit pane',\n 'diff.worktree': 'Unstaged',\n 'diff.index': 'Staged',\n 'diff.wrap': 'Wrap lines',\n 'diff.copy': 'Copy diff',\n 'diff.copied': 'Copied',\n 'diff.empty': 'No changes on this side',\n 'diff.binary': 'Binary file — no text diff to show',\n // ── Image preview (binary changes we can recognise) ──\n 'image.loading': 'Reading image…',\n 'image.pair': 'Side by side',\n 'image.flip': 'Flip',\n 'image.beforeWorktree': 'index',\n 'image.afterWorktree': 'worktree',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': 'index',\n 'image.beforeCommit': 'parent',\n 'image.afterCommit': 'this commit',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': 'size {from} → {to}',\n 'image.tooLarge': 'over the {limit} MB preview limit ({size})',\n 'image.notImage': 'not a previewable image ({size})',\n 'image.missing': 'not present on this side',\n 'image.zoomOut': 'Zoom out',\n 'image.zoomIn': 'Zoom in',\n 'image.fitWidth': 'Fit width',\n 'image.zoomValue': '{percent}%',\n // ── Session timer capsule ──\n 'timer.title': \"This session's running time\",\n 'timer.created': 'started {time}',\n 'timer.work': 'model {llm}, tools {tool}',\n 'timer.turns': '{turns} turns / {steps} steps',\n // ── Account balance capsule ──\n 'balance.title': 'DeepSeek account balance',\n 'balance.bonus': 'Bonus balance',\n 'balance.updated': 'updated {time}',\n 'balance.stale': 'Last refresh failed; this is the balance from {time}',\n 'balance.delta': '{delta} since last refresh ({time})',\n 'balance.providerRemaining': 'balance {amount}',\n 'balance.providerUsedKey': '{amount} used on this key',\n 'balance.providerUsedAccount': '{amount} used on this account',\n 'balance.providerRequests': 'API requests {count}',\n 'balance.tokenTitle': 'Add a dashboard access token',\n 'balance.tokenHint':\n 'This site only exposes its current balance to a dashboard session (API keys cannot read it). Create an access token with the profile:read scope in the site settings, then paste it here.',\n 'balance.tokenPlaceholder': 'Paste access token',\n 'balance.tokenSave': 'Save',\n 'balance.tokenSaving': 'Saving…',\n 'balance.tokenCancel': 'Cancel',\n 'balance.tokenRef': 'Stored locally only',\n 'balance.tokenError.unknown-provider':\n 'Unknown provider — check which site this session’s model comes from',\n 'balance.tokenError.not-supported': 'This site does not support dashboard tokens',\n 'balance.tokenError.no-credentials': 'Credentials service unavailable',\n 'balance.tokenError.bad-token': 'Malformed token (it must not contain spaces or newlines)',\n 'balance.tokenError.failed': 'Could not save; try again',\n 'balance.tokenError.unsupported': 'This host cannot save tokens here',\n 'balance.tokenError.reason': 'Could not save',\n 'balance.usedShort': 'used',\n 'balance.providerUnknown': '--',\n 'balance.reason.unsupported': 'this site exposes no balance API',\n 'balance.reason.no-key': 'no key configured',\n 'balance.reason.network': 'network failure',\n 'balance.reason.unknown-provider': 'model provider not in settings',\n 'balance.reason.no-provider': 'session has no model provider yet',\n 'balance.reason.no-session': 'session not ready',\n // ── Turn-tail \"images this turn\" strip ──\n 'turnImages.title': 'Images this turn',\n 'turnImages.titleSession': 'Images this session',\n 'turnImages.open': 'Open in the built-in preview',\n 'turnImages.compare': 'Compare before/after in dsh-hud',\n 'turnImages.more': '{n} more',\n 'diff.truncated': 'Diff is large; showing the beginning only',\n 'diff.untrackedHint': 'Untracked file, diffed against an empty file',\n 'diff.commitHint': 'What this commit changed against its parent (read-only)',\n 'diff.hunkToggle':\n 'Click this row to collapse or expand the hunk (same as Space once it is the current one)',\n 'commit.files': 'Files in this commit',\n 'commit.close': 'Close the commit detail',\n 'commit.empty': 'This commit changes no files (empty commit)',\n 'commit.loading': 'Reading this commit…',\n 'commit.merge': 'Merge commit, compared against its first parent',\n 'commit.unknown':\n 'Commit not found: it may have been rebased or amended away — refresh the panel',\n 'commit.click': 'Click to see what this commit changed',\n 'session.line': '{n} file(s) changed by this session',\n 'tools.now': 'Running now: {name}',\n 'tools.total': '{n} tool call(s) this session',\n 'tools.byTool': 'By tool:',\n 'tools.skills': 'skills:',\n 'tools.recent': 'Recent:',\n 'rows.ownNone': 'this session has no recorded changes',\n 'rows.noRepo': 'not in a git repository',\n 'rows.error': 'read failed ({code})',\n 'rows.possible': '{n} path(s) commands may have touched ({now} still differ)',\n 'review.count': '{n}/{total} reviewed',\n 'review.staleCount': '{n} changed since reviewed',\n 'review.mark': 'Mark this revision as reviewed (key: v)',\n 'review.unmark': 'Unmark reviewed (key: v)',\n 'review.stale': 'Changed since you reviewed it (your mark was an earlier revision)',\n 'review.clear': 'Clear the reviewed marks for this workspace',\n 'review.clearShort': 'Clear reviewed',\n 'session.latestTurn': '{n} in the latest turn',\n 'session.turn': 'turn {n}',\n 'session.turnWithPrompt': 'turn {n}: {prompt}',\n 'session.onlyLatest': 'Latest turn only',\n 'session.onlyLatestTitle': 'Narrow the list to the files the latest turn changed',\n 'session.possible': '{n} more possibly related (seen in commands)',\n 'session.possibleDropped': '{n} more over the cap',\n 'session.possibleTitle': 'Possibly related (unconfirmed): {reason}',\n 'session.commands': 'Last {n} command(s) run:',\n 'session.commandsMore': '…plus {n} more that write files',\n 'session.commandFailed': '(this one failed)',\n 'session.gone': '{n} no longer in the git status (reverted, ignored, or overwritten)',\n 'session.dropped': '{n} more not recorded (over the cap)',\n 'session.only': \"Only this session's\",\n 'session.onlyTitle':\n 'Filter the list to files this session touched (confirmed plus command-inferred, intersected with git)',\n 'session.mine': 'Changed by this session',\n 'session.other': 'Not changed by this session (you, your editor, or another session)',\n 'session.writes': '{n} write(s)',\n 'session.edits': '{n} edit(s)',\n 'session.since': 'Recorded since {time} (anything before the host process started is not)',\n 'session.pill': '{n} file(s) changed by this session',\n 'session.empty':\n 'This session has not changed anything yet (clear the filter above to see the workspace)',\n 'pill.loading': 'Loading…',\n 'pill.notRepo': 'not a git repo',\n 'pill.error': 'read failed',\n 'pill.title': 'Click to open the Git panel',\n 'count.modified': 'modified',\n 'count.added': 'added',\n 'count.deleted': 'deleted',\n 'count.renamed': 'renamed',\n 'count.copied': 'copied',\n 'count.typeChanged': 'type changed',\n 'count.untracked': 'untracked',\n 'count.conflicted': 'conflicted',\n 'err.timeout': 'git timed out: the repository is large or the disk is busy',\n 'err.no-cwd': 'The workspace directory does not exist',\n 'err.git-missing': 'git executable not found',\n 'err.invalid-cwd': 'Invalid workspace path',\n 'err.git-failed': 'git failed',\n 'err.invalid-path': 'Invalid file path',\n 'err.stale-host':\n 'The host half does not have this endpoint yet: the plugin was just updated, restart DSH so the host half reloads',\n 'err.transport': 'Cannot reach the host half',\n}\n","/**\n * 「换模型就换账本」的那个触发器。\n *\n * 为什么需要它:胶囊的取数有 TTL(成功 60 秒),而**换模型不是取数失败**,\n * 它只是让\"当前会话用的是哪个提供方\"变了。TTL 按时钟算,不按事实算,\n * 所以换完模型最长要等一分钟胶囊才改口径 —— 用户切到 `ur` 之后盯着\n * `¥8.60`(平台账号的钱)看一分钟,会以为插件没跟上(他确实是这么问的)。\n *\n * 触发源选 `sessions` 服务的 `modelSelection` 投影:那是**模型选择本身**的\n * 真相(`pending ?? lastUsed`,刚在菜单里点下的那个立刻生效,不必等第一条消息),\n * 也正是宿主解析提供方时用的同一个投影 —— 两边看同一份数据,才不会各说各话。\n *\n * 这一层是**软依赖**:拿不到 `sessions` 就什么都不做,退回原来的轮询节奏。\n * 一条诊断都不报 —— 少了它只是慢一分钟,不是坏掉。\n */\n\nimport type { HudState, HudStore } from './shared.js'\n\n/** 模型投影里我们关心的一点点形状(其余字段不碰)。 */\ninterface ModelSelectionValue {\n lastUsed?: { provider?: unknown } | null\n pending?: { provider?: unknown } | null\n}\n\n/** 投影的一个 face:`getSnapshot` 读、`subscribe` 听(框架提供的形状)。 */\ninterface ProjectionFace {\n getSnapshot: () => ModelSelectionValue | undefined\n subscribe: (listener: () => void) => () => void\n}\n\n/** 客户端 `sessions` 服务里用到的那一点。 */\ninterface SessionsService {\n binding?: (\n id: string,\n ) => { session?: { projections?: { faceOf?: (key: string) => ProjectionFace } } } | undefined\n}\n\n/** 多久检查一次\"这个会话的投影挂上了没\"(纯本地读取,不发请求)。 */\nconst BIND_RETRY_MS = 2000\n\n/**\n * 从投影读数里取\"当前属于哪个提供方\"。\n *\n * `pending` 优先:菜单里刚点下的那个模型还没真正用过,但账本已经该换了 ——\n * 胶囊跟的是\"接下来要花谁的钱\",不是\"上一次花了谁的钱\"。\n */\nfunction providerOf(value: ModelSelectionValue | undefined): string | null {\n if (value === null || typeof value !== 'object') return null\n const chosen = value.pending ?? value.lastUsed\n if (chosen === null || typeof chosen !== 'object') return null\n const provider = (chosen as { provider?: unknown }).provider\n return typeof provider === 'string' && provider !== '' ? provider : null\n}\n\n/**\n * 盯着当前会话的 `modelSelection`:提供方一变就**立刻**重取一次余额。\n *\n * @param ctx - 客户端插件上下文(只用 `get`)。\n * @param store - 共享 store(`loadProviderBalance(true)` 会跳过 TTL)。\n * @returns 卸载函数(停掉定时器与订阅)。\n */\nexport function watchModelSelection(\n ctx: { get: (name: string) => unknown },\n store: HudStore,\n): () => void {\n const raw = ctx.get('sessions') as SessionsService | undefined\n // 没有这个服务:什么都不做,保持原来的 30 秒轮询 + TTL 行为。\n if (raw === undefined || raw === null || typeof raw.binding !== 'function') {\n return function () {}\n }\n // 收窄到一个不可变的局部量:下面几个闭包都要用(TS 不跨闭包保留收窄)。\n const sessions: SessionsService = raw\n\n let disposed = false\n let boundSession: string | null = null\n let unsubscribe: (() => void) | null = null\n let lastProvider: string | null = null\n let retry: number | null = null\n\n /** 投影还没就绪时才需要补挂;挂上了就把定时器停掉(不白跑)。 */\n function startRetry(): void {\n if (retry !== null || disposed) return\n // `window.setInterval`:测试 harness 替换的是 `window.*`,裸调用会留下真的 Node\n // 定时器(用例全过但进程不退出),浏览器里 `window.setInterval` 与全局是同一个。\n retry = window.setInterval(function () {\n sync(store.getSnapshot())\n }, BIND_RETRY_MS)\n }\n\n function stopRetry(): void {\n if (retry === null) return\n window.clearInterval(retry)\n retry = null\n }\n\n function detach(): void {\n if (unsubscribe !== null) {\n const stop = unsubscribe\n unsubscribe = null\n try {\n stop()\n } catch {\n // 投影已经被框架收走了:这里没有任何可做的补救,也不该反过来打崩插件。\n }\n }\n boundSession = null\n lastProvider = null\n }\n\n /** 把订阅挂到当前会话的投影上(投影/绑定还没就绪就下次再试)。 */\n function attach(sessionId: string): void {\n const binding = sessions.binding?.(sessionId)\n const faceOf = binding?.session?.projections?.faceOf\n if (typeof faceOf !== 'function') return\n let face: ProjectionFace\n try {\n face = faceOf('modelSelection')\n } catch {\n return\n }\n if (face === undefined || face === null || typeof face.subscribe !== 'function') return\n\n boundSession = sessionId\n stopRetry()\n lastProvider = providerOf(face.getSnapshot())\n unsubscribe = face.subscribe(function () {\n if (disposed) return\n const next = providerOf(face.getSnapshot())\n // 只认\"提供方变了\":同一个站下面换模型(`ur/deepseek-v4.1-flash` → `ur/gpt-6-luna`)\n // 账本没变,没必要打一次请求。\n if (next === null || next === lastProvider) return\n lastProvider = next\n void store.loadProviderBalance(true)\n })\n }\n\n /** 看一眼当前会话,必要时重挂。 */\n function sync(state: HudState): void {\n if (disposed) return\n const sessionId = state.sessionId\n if (typeof sessionId !== 'string' || sessionId === '') {\n detach()\n return\n }\n if (sessionId === boundSession) return\n detach()\n attach(sessionId)\n // 没挂上(绑定还没物化 / 这个会话没有 modelSelection 投影):过一会儿再试。\n if (boundSession === null) startRetry()\n }\n\n const stopStore = store.subscribe(function () {\n sync(store.getSnapshot())\n })\n sync(store.getSnapshot())\n\n return function () {\n disposed = true\n stopRetry()\n stopStore()\n detach()\n }\n}\n","/**\n * 「账号余额」这一片 store:把 DSH 自己的账号接口包装成\"一份快照 + 一个取数函数\"。\n *\n * 为什么这么绕:余额不是一个投影,而是宿主替我们去 DeepSeek 平台查的账\n * (`remote.account.getBalance`)。所以这里做三件事 ——\n *\n * 1. **软依赖**:`remote.account` 与 `modules` 都是动态注入。没有账号能力(或没有\n * 版本号可用)时这一整片安静地不工作,余额胶囊不出现;插件其余部分照常。\n * 2. **限流**:余额按分钟变,轮询却跟别的读数不同频(见 `BALANCE_POLL_MS`)。\n * 这里再按 TTL 兜一层:轮询、窗口聚焦、点击三者撞到一起时只打一次平台。\n * 3. **失败不清零**:读不到时保留上一次的数字并打 `failed` 标记(界面据此提示\n * \"可能已过期\"),而不是把胶囊抹掉 —— 账目读数短暂失败不代表余额变成 0。\n */\n\nimport type { HudBalance, HudBalanceWallet } from '../shared.js'\nimport type {\n AccountClientMetadata,\n AccountRemoteService,\n AccountSlice,\n AccountWallet,\n GetState,\n ModulesService,\n StoreContext,\n StoreSet,\n} from './types.js'\n\nimport { isPositiveAmount } from '../format.js'\nimport { BALANCE_RETRY_MS, BALANCE_TTL_MS, probe } from '../shared.js'\n\n/** 启动清单里宿主这一版的发布身份(`window.__DSH_BOOT__.release`)。 */\ninterface BootRelease {\n release?: { version?: string; commit?: string } | null\n}\n\n/**\n * 建「账号余额」这一片。\n * @param ctx - 客户端根上下文(用到 `effect` / `inject` / `locale`)。\n * @param getState - 读当前快照。\n * @param set - 浅合并写快照。\n * @returns 本片对外暴露的方法。\n */\nexport function createAccountSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n): AccountSlice {\n /** 账号命名空间;没注入到就是 `null`(这个宿主没有账号插件,或还没起来)。 */\n var accountService: AccountRemoteService | null = null\n /** 启动清单服务:只借用它的 `manifest.release.version`。 */\n var modulesService: ModulesService | null = null\n /** 平台链接(用量页 / 充值页):`getState()` 给,取到一次就一直用。 */\n var links: { usageUrl: string | null; topUpUrl: string | null } = {\n usageUrl: null,\n topUpUrl: null,\n }\n /** 在途请求:三次触发撞到一起时共用一次。 */\n var inflight: Promise<HudBalance | null> | null = null\n /** 上次真正发请求的时刻(TTL 的起点)。 */\n var lastAttemptAt = 0\n /** 上次的结果档位:成功用长 TTL,其它用短 TTL。 */\n var lastReady = false\n\n // 两条软依赖分开注入:缺 `modules` 不该把账号能力一起废掉(版本还能从启动清单里取)。\n if (typeof ctx.inject === 'function') {\n ctx.effect(function () {\n // 与内置插件同一写法(`dsh-client-ui-sidebar-documentpreview`):父命名空间与子服务一起注入,\n // `scope.remote.account` 才保证有;只注子服务在 Cordis 里不保证父壳存在。\n return ctx.inject(['remote', 'remote.account'], function (scope) {\n accountService = scope.remote && scope.remote.account ? scope.remote.account : null\n probe.log('account', { bound: accountService !== null })\n // 服务可能比 apply 晚到:一绑上就补一次,别让胶囊等到下一轮 5 分钟的节拍才出现。\n if (accountService !== null) loadBalance()\n return function () {\n accountService = null\n }\n })\n }, 'dsh-hud: account service')\n\n ctx.effect(function () {\n return ctx.inject(['modules'], function (scope) {\n modulesService = scope.modules || null\n // 版本可能就是从这条依赖里来的:同样补一次(TTL 会挡住重复请求)。\n if (modulesService !== null) loadBalance()\n return function () {\n modulesService = null\n }\n })\n }, 'dsh-hud: boot manifest')\n }\n\n /**\n * 宿主这一版的版本号。\n *\n * 为什么非要它:账号接口的调用元数据要带 `x-client-version`,而 DSH 内置插件那半边\n * 是打包时**内联**了版本常量的(`\"0.2.1-rc.3\"`),外部插件的产物里没有这行字面量。\n * 所以从启动清单里现取:`window.__DSH_BOOT__.release.version` 是宿主自己发布在每份\n * 图里的发布身份,`ctx.modules.manifest.release.version` 是同一份数据的服务视角。\n * 两条都取不到就**不发请求**(宁可没有这一枚胶囊,也不要带一个假版本号去问平台)。\n *\n * @returns 版本字符串;都取不到回 `null`。\n */\n function releaseVersion(): string | null {\n var boot = (globalThis as { __DSH_BOOT__?: BootRelease }).__DSH_BOOT__\n var fromBoot = boot && boot.release ? boot.release.version : undefined\n if (typeof fromBoot === 'string' && fromBoot !== '') return fromBoot\n var manifest = modulesService ? modulesService.manifest : null\n var fromModules = manifest && manifest.release ? manifest.release.version : undefined\n if (typeof fromModules === 'string' && fromModules !== '') return fromModules\n return null\n }\n\n /** 当前界面语言(宿主再把它折成平台用的 `zh_CN` / `en_US`)。 */\n function activeLocale(): string {\n try {\n // `locale` 在 Cordis 上是个带 `getSnapshot()` 的服务;这里按结构读,不去依赖它的类型。\n var locale = ctx.locale as\n { getSnapshot?: () => { active?: string } | null } | null | undefined\n var snapshot =\n locale && typeof locale.getSnapshot === 'function' ? locale.getSnapshot() : null\n var active = snapshot && snapshot.active\n if (typeof active === 'string' && active !== '') return active\n } catch (error) {\n // 语言服务取不到不该影响余额:宿主那边会按 en_US 处理。\n }\n return 'en-US'\n }\n\n /**\n * 读账号投影里的两个平台链接。\n *\n * 为什么单独要它:余额接口只回钱包,**累计消耗 / 本月消耗只在平台用量页上**\n * (平台页还有「今天 / 本月 / 自定」那种时间维度,DSH 不代理那份数据)。\n * 这一枚胶囊因此提供一条直达那一页的路 —— 链接从账号投影里拿,不写死域名。\n *\n * `getState` 是直连方法(不碰网络),也不是所有宿主版本都有:缺了就安静地\n * 返回上一次的值,胶囊退化成\"只有余额\"。\n *\n * @param service - 账号命名空间。\n * @returns 两个链接(取不到就是上一次的、可能都是 `null`)。\n */\n function readLinks(service: AccountRemoteService): Promise<{\n usageUrl: string | null\n topUpUrl: string | null\n }> {\n if (typeof service.getState !== 'function') return Promise.resolve(links)\n return Promise.resolve()\n .then(function () {\n return service.getState!()\n })\n .then(function (result) {\n var value = result && result.ok === true ? result.value : null\n var next = value && value.links ? value.links : null\n if (next === null) return links\n var usageUrl = typeof next.usageUrl === 'string' ? next.usageUrl : null\n var topUpUrl = typeof next.topUpUrl === 'string' ? next.topUpUrl : null\n if (usageUrl === null && topUpUrl === null) return links\n return { usageUrl: usageUrl, topUpUrl: topUpUrl }\n })\n .catch(function () {\n return links\n })\n }\n\n /** 账号接口的调用元数据(时区偏移按\"东为正\"给,与 DSH 自己一致)。 */\n function clientMetadata(version: string): AccountClientMetadata {\n return {\n version: version,\n locale: activeLocale(),\n timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,\n }\n }\n\n /** 平台给的数组 → 本片的钱包列表(逐项校验;形状不对的项直接丢,不猜)。 */\n function walletsOf(value: unknown): HudBalanceWallet[] {\n if (!Array.isArray(value)) return []\n var out: HudBalanceWallet[] = []\n for (var index = 0; index < value.length; index += 1) {\n var item = value[index] as AccountWallet | null\n if (!item || typeof item !== 'object') continue\n if (typeof item.currency !== 'string' || typeof item.balance !== 'string') continue\n out.push({ currency: item.currency, balance: item.balance })\n }\n return out\n }\n\n /** 两份快照的账目是否一致(一致就不 set,省一次重渲染)。 */\n function sameWallets(left: HudBalanceWallet[], right: HudBalanceWallet[]): boolean {\n if (left.length !== right.length) return false\n for (var index = 0; index < left.length; index += 1) {\n if (left[index].currency !== right[index].currency) return false\n if (left[index].balance !== right[index].balance) return false\n }\n return true\n }\n\n function publish(next: HudBalance | null): HudBalance | null {\n var previous = getState().balance\n if (next === null) {\n if (previous !== null) set({ balance: null })\n return null\n }\n var previousPrev = previous !== null && previous.prev ? previous.prev.at : null\n var nextPrev = next.prev ? next.prev.at : null\n var same =\n previous !== null &&\n previous.failed === next.failed &&\n previous.usageUrl === next.usageUrl &&\n previousPrev === nextPrev &&\n sameWallets(previous.wallets, next.wallets) &&\n sameWallets(previous.bonus, next.bonus)\n if (!same) set({ balance: next })\n return next\n }\n\n /** 读失败:留着上一次的数字、打上 `failed`,让界面能说\"可能已过期\"。 */\n function markFailed(): HudBalance | null {\n lastReady = false\n var previous = getState().balance\n if (previous === null) {\n probe.log('balance', { failed: true, had: false })\n return null\n }\n probe.log('balance', { failed: true, had: true })\n return publish({\n wallets: previous.wallets,\n bonus: previous.bonus,\n at: previous.at,\n failed: true,\n usageUrl: previous.usageUrl,\n topUpUrl: previous.topUpUrl,\n // 失败那一轮不动 `prev`:它永远指向最近一次**成功**的读数,否则\"较上次刷新\"会被\n // 一次失败搅成\"跟更早的一次比\"。\n prev: previous.prev || { wallets: previous.wallets, at: previous.at },\n })\n }\n\n /**\n * 取一次余额。\n * @param force - 跳过 TTL(点击胶囊时用)。\n * @returns 最新快照;没法取时回上一次的(可能为 `null`)。**从不 reject**。\n */\n function loadBalance(force?: boolean): Promise<HudBalance | null> {\n var now = Date.now()\n if (force !== true && lastAttemptAt !== 0) {\n var ttl = lastReady ? BALANCE_TTL_MS : BALANCE_RETRY_MS\n if (now - lastAttemptAt < ttl) return Promise.resolve(getState().balance)\n }\n if (accountService === null) return Promise.resolve(getState().balance)\n var version = releaseVersion()\n if (version === null) {\n probe.log('balance', { skipped: 'no-release-version' })\n return Promise.resolve(getState().balance)\n }\n if (inflight !== null) return inflight\n\n var service = accountService\n lastAttemptAt = now\n inflight = Promise.resolve()\n .then(function () {\n return Promise.all([\n service.getBalance(clientMetadata(version as string)),\n readLinks(service),\n ])\n })\n .then(function (pair) {\n links = pair[1]\n inflight = null\n var result = pair[0]\n if (!result || result.ok !== true) return markFailed()\n var payload = result.value\n // `null` = 当前没有登录态:不是错误,只是没账目可显示。\n if (payload === null || payload === undefined) {\n lastReady = false\n probe.log('balance', { signedOut: true })\n return publish(null)\n }\n if (payload.status !== 'ready') return markFailed()\n var wallets = walletsOf(payload.value)\n var bonus = walletsOf(payload.bonusWallets).filter(function (wallet) {\n return isPositiveAmount(wallet.balance)\n })\n if (wallets.length === 0 && bonus.length === 0) {\n lastReady = false\n probe.log('balance', { empty: true })\n return publish(null)\n }\n lastReady = true\n var at = Date.now()\n var before = getState().balance\n probe.log('balance', { wallets: wallets.length, bonus: bonus.length })\n return publish({\n wallets: wallets,\n bonus: bonus,\n at: at,\n failed: false,\n usageUrl: links.usageUrl,\n topUpUrl: links.topUpUrl,\n // 上一次成功读到的钱包:界面据此算\"较上次刷新\"的变化量。\n // 第一次根本没有前一份,这里就是 `null`(那时也就没有变化量可谈)。\n prev: before !== null ? { wallets: before.wallets, at: before.at } : null,\n })\n })\n .catch(function (error) {\n inflight = null\n probe.log('balance', { thrown: error instanceof Error ? error.message : String(error) })\n return markFailed()\n })\n\n return inflight\n }\n\n return { loadBalance: loadBalance }\n}\n","/**\n * 「当前会话模型来源」的余额这一片。\n *\n * 为什么需要它:账号那一路(`store/account.ts`)只认 DeepSeek 平台账号的账本。会话换到\n * 第三方中转站(`ur` / `api404` / `muyuan` …)之后,胶囊还画着平台账号的 `¥8.60`,\n * 那是**另一个账本上的钱** —— 看着就像 bug。这一片按会话当前模型去问宿主\"这个提供方\n * 还剩多少\",再把结论压成一份 `HudProviderLedger`:\n *\n * - `account`:会话用的就是平台账号 → 胶囊继续走 `HudBalance`(不重复取数);\n * - `provider`:读到了这个提供方的余额 → 胶囊改画它;\n * - `unavailable`:知道是哪个提供方但读不到 → **不画**(不是画别人的余额);\n * - `unknown`:会话/投影还没就绪(首帧常态)→ 先按账号那一路画,别让胶囊闪一下。\n *\n * 三条设计约束:\n *\n * 1. **按会话取**:换会话就换账本,所以 `sessionId` 一变就得重取(不吃上一个会话的缓存)。\n * 2. **慢节拍**:中转站被高频轮询会限流,宿主那边还有一分钟缓存;这里再按 TTL 兜一层。\n * 3. **失败不清零**:读不到时保留上一次的结果(`unavailable` 只在**没有**上一次\n * provider 结果时才生效),避免网络抖一下就让胶囊在\"有数/没数\"之间跳。\n */\n\nimport type { HudProviderBalance, HudProviderLedger, HudProviderToken } from '../shared.js'\nimport type {\n GetState,\n ProviderBalancePayload,\n ProviderSlice,\n StoreContext,\n StoreSet,\n TokenPanelState,\n} from './types.js'\n\nimport { CHANNEL, probe } from '../shared.js'\n\n/** 同一会话两次取数的最小间隔(宿主侧缓存一分钟,这里配合它不空转)。 */\nexport const PROVIDER_TTL_MS = 60000\n/** 没取到时多久再试。 */\nexport const PROVIDER_RETRY_MS = 30000\n/**\n * 失败后的第一次重试间隔,之后逐次翻倍,到 `PROVIDER_RETRY_MS` 封顶。\n *\n * 为什么要退避而不是一上来就 30 秒:重启/刚开会话时**宿主常常还答不上来**\n *(模型选择投影还没就绪),第一次问必然落空。用固定 30 秒兜的话,胶囊要空半分钟 ——\n * 用户看到的就是\"重启后没立即显示\"。1 秒起、翻倍到 30 秒,正常情况一两秒内就出来了,\n * 而真出故障时也不会变成每秒一次的重试风暴。\n */\nexport const PROVIDER_RETRY_BASE_MS = 1000\n\n/** 端点名(宿主半 `rpc.ts` 里登记的那个)。 */\nconst ENDPOINT = 'provider-balance'\n\n/** 一个可展示的钱包列表(逐项校验,形状不对的项直接丢,不猜)。 */\nfunction walletsOf(value: unknown): Array<{ currency: string; balance: string }> {\n if (!Array.isArray(value)) return []\n var out: Array<{ currency: string; balance: string }> = []\n for (var index = 0; index < value.length; index += 1) {\n var item = value[index] as { currency?: unknown; balance?: unknown } | null\n if (!item || typeof item !== 'object') continue\n if (typeof item.currency !== 'string' || typeof item.balance !== 'string') continue\n out.push({ currency: item.currency, balance: item.balance })\n }\n return out\n}\n\n/** 十进制字符串校验(不合法就当没给)。 */\nfunction amountOf(value: unknown): string | null {\n if (typeof value !== 'string') return null\n var raw = value.trim()\n return /^-?\\d+(?:\\.\\d+)?$/.test(raw) ? raw : null\n}\n\n/** 端点答复 → 快照;形状不对回 `null`(当作\"读不到\")。 */\nfunction balanceOf(payload: ProviderBalancePayload): HudProviderBalance | null {\n if (payload.available !== true || payload.account === true) return null\n var raw = (payload as { balance?: unknown }).balance\n if (raw === null || typeof raw !== 'object') return null\n var item = raw as {\n provider?: unknown\n kind?: unknown\n currency?: unknown\n remaining?: unknown\n usedAccount?: unknown\n usedKey?: unknown\n requests?: unknown\n placeholderLimit?: unknown\n wallets?: unknown\n bonus?: unknown\n at?: unknown\n }\n if (typeof item.provider !== 'string' || item.provider === '') return null\n if (item.kind !== 'official' && item.kind !== 'relay') return null\n var remaining = amountOf(item.remaining)\n var usedAccount = amountOf(item.usedAccount)\n var usedKey = amountOf(item.usedKey)\n var requests =\n typeof item.requests === 'number' && Number.isFinite(item.requests) ? item.requests : null\n var wallets = walletsOf(item.wallets)\n // 一个可展示的数字都没有:这不是余额,别画。\n if (remaining === null && usedAccount === null && usedKey === null && wallets.length === 0) {\n return null\n }\n return {\n provider: item.provider,\n kind: item.kind,\n currency: typeof item.currency === 'string' && item.currency !== '' ? item.currency : null,\n remaining: remaining,\n usedAccount: usedAccount,\n usedKey: usedKey,\n requests: requests,\n placeholderLimit: item.placeholderLimit === true,\n wallets: wallets,\n bonus: walletsOf(item.bonus),\n at: typeof item.at === 'number' && Number.isFinite(item.at) ? item.at : Date.now(),\n }\n}\n\n/**\n * 失败答复里的\"是哪个提供方 / 为什么读不到\"。\n *\n * 有这两个字段,胶囊就能画成 `ur —`(并说清是\"该站没有余额接口\"还是\"没配密钥\"),\n * 而不是直接消失 —— 消失分不清\"这个站真的没有\"和\"插件坏了\"。\n *\n * @param result - 宿主的答复信封。\n * @returns `{provider, reason}`;读不出就给 `null`(调用方按无信息处理)。\n */\nfunction failureFieldsOf(result: unknown): { provider: string | null; reason: string | null } {\n var payload = (result as { value?: unknown } | null)?.value\n if (payload === null || typeof payload !== 'object') return { provider: null, reason: null }\n var item = payload as { provider?: unknown; reason?: unknown }\n return {\n provider: typeof item.provider === 'string' && item.provider !== '' ? item.provider : null,\n reason: typeof item.reason === 'string' && item.reason !== '' ? item.reason : null,\n }\n}\n\n/** 宿主给的「后台凭据配了没」;形状不对就当没有(老宿主不给这个字段)。 */\nfunction tokenOf(payload: unknown): HudProviderToken | null {\n if (payload === null || typeof payload !== 'object') return null\n var item = (payload as { token?: unknown }).token\n if (item === null || typeof item !== 'object') return null\n var token = item as { ref?: unknown; configured?: unknown }\n if (typeof token.ref !== 'string' || token.ref === '') return null\n return { ref: token.ref, configured: token.configured === true }\n}\n\n/**\n * 建「提供方余额」这一片。\n * @param ctx - 客户端根上下文(只用 `connection.rpc`)。\n * @param getState - 读当前快照。\n * @param set - 浅合并写快照。\n * @returns 本片对外暴露的方法。\n */\n/**\n * 失败后的重试间隔:1s、2s、4s、8s、16s,然后封顶在 `PROVIDER_RETRY_MS`。\n *\n * @param failStreak - 连续失败次数(0 = 还没失败过)。\n * @returns 毫秒。\n */\nexport function providerRetryDelayMs(failStreak: number): number {\n if (!(failStreak > 0)) return PROVIDER_RETRY_BASE_MS\n var delay = PROVIDER_RETRY_BASE_MS * Math.pow(2, failStreak - 1)\n return delay > PROVIDER_RETRY_MS ? PROVIDER_RETRY_MS : delay\n}\n\nexport function createProviderSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n): ProviderSlice {\n /** 在途请求(一次只打一个)。 */\n var inflight: Promise<HudProviderLedger | null> | null = null\n /** 请求代次:每发一次 +1,落地时对不上号就作废(见 `loadProviderBalance`)。 */\n var requestTicket = 0\n /** 连续失败次数(成功即归零):只用来算退避间隔。 */\n var failStreak = 0\n /** 上次真正发请求的时刻。 */\n var lastAttemptAt = 0\n /** 上次取数针对哪个会话 —— 换了会话就必须重取,不能吃上一个会话的账本。 */\n var lastSessionId: string | null = null\n /** 上次是成功(长 TTL)还是失败(短 TTL)。 */\n var lastReady = false\n\n /** 写一份新结论(没变化就不 set,省一次重渲染)。 */\n function publish(\n mode: HudProviderLedger['mode'],\n balance: HudProviderBalance | null,\n extra?: { provider?: string | null; reason?: string | null },\n token?: HudProviderToken | null,\n ): HudProviderLedger {\n var previous = getState().provider\n // token 没给就沿用上一次的:失败路径不重问一遍状态(它本来就没变,而且\n // \"读不到\"与\"没配令牌\"是两件事,前者不该把后者抹掉)。\n var nextToken = token === undefined ? (previous?.token ?? null) : token\n // `provider` 在 `provider` 模式下**必须**有值:胶囊的点击路径(配后台令牌)靠它决定\n // \"给哪个站存令牌\"。早先这里只从 `extra` 取,而正常读取走的是\n // `publish('provider', balance, undefined, token)` —— 于是 `ledger.provider` 是 `null`,\n // 面板打开后 provider 为空,`saveProviderToken` 一看就 `return false`:不报错、不发请求、\n // 什么都不发生。用户看到的就是\"保存了没什么变化\"。\n // 账本自己就带着这个事实(`balance.provider`),没理由再让它空着。\n var provider = extra?.provider ?? balance?.provider ?? null\n var next: HudProviderLedger = {\n mode: mode,\n balance: balance,\n provider: provider,\n reason: extra?.reason ?? null,\n token: nextToken,\n }\n if (\n previous === undefined ||\n previous.mode !== next.mode ||\n previous.provider !== next.provider ||\n previous.reason !== next.reason ||\n (previous.token?.configured ?? null) !== (next.token?.configured ?? null) ||\n (previous.token?.ref ?? null) !== (next.token?.ref ?? null) ||\n (previous.balance === null) !== (next.balance === null) ||\n (previous.balance !== null &&\n next.balance !== null &&\n (previous.balance.provider !== next.balance.provider ||\n previous.balance.remaining !== next.balance.remaining ||\n previous.balance.usedAccount !== next.balance.usedAccount ||\n previous.balance.usedKey !== next.balance.usedKey ||\n previous.balance.requests !== next.balance.requests ||\n previous.balance.at !== next.balance.at))\n ) {\n set({ provider: next })\n }\n return next\n }\n\n /**\n * 换会话时把账本清成\"还不知道\"。\n *\n * 为什么必须清:不清就会**在新会话里继续挂着上一个会话的读数和站名**,直到新答案回来。\n * 那段时间屏幕上写的是 `ur` 的钱,而这次会话可能花的是 `muyuan` 的钱 —— 正是\n * \"绝不把别人的钱画成你的\"那条约束要防的事。清成 `unknown` 后胶囊画 `--`,\n * 一眼就知道\"还在查\",比显示一个错的数强。\n */\n function resetForSession(): void {\n // 在途请求也一并作废:它回来的是上一个会话的账本。\n requestTicket += 1\n inflight = null\n lastAttemptAt = 0\n lastSessionId = null\n failStreak = 0\n lastReady = false\n set({ provider: { mode: 'unknown', balance: null, provider: null, reason: null, token: null } })\n }\n\n /**\n * 取一次\"当前会话模型来源\"的余额。\n * @param force - 跳过 TTL(点击胶囊时用)。\n * @returns 最新结论(可能沿用上一次);**从不 reject**。\n */\n function loadProviderBalance(force?: boolean): Promise<HudProviderLedger | null> {\n var sessionId = getState().sessionId\n var now = Date.now()\n // 换了会话(或从无到有):立刻重取,不吃上一个会话的结论。\n var switched = sessionId !== lastSessionId\n if (force !== true && !switched && lastAttemptAt !== 0) {\n var ttl = lastReady ? PROVIDER_TTL_MS : providerRetryDelayMs(failStreak)\n if (now - lastAttemptAt < ttl) return Promise.resolve(getState().provider)\n }\n // 换了会话就别复用上一个会话的在途请求:它回来的是**另一个账本**的数字,\n // 而这次调用方要的是当前会话的(复用会让新会话白等一个 TTL)。\n // `force` 同理不能复用:它请求的是\"**此刻之后**的事实\"(典型场景就是刚存完令牌,\n // 手上那个在途请求是用旧凭据发出的,用它等于让新令牌白等一个 TTL)。\n if (inflight !== null && !switched && force !== true) return inflight\n if (typeof sessionId !== 'string' || sessionId === '') {\n // 还没有会话(首帧):按\"还不知道\"处理,胶囊照旧看账号那一路。\n lastSessionId = null\n return Promise.resolve(publish('unknown', null))\n }\n\n // 换会话就重置退避:这是另一个账本,不该继承上一个会话攒下的罚时。\n if (switched) failStreak = 0\n lastAttemptAt = now\n lastSessionId = sessionId\n // 代次:谁最后发出,谁说了算。`force` 现在能绕过在途去重 → 可能同时有两个请求在飞,\n // 若让先发后到的那份落地,胶囊就会拿旧凭据的结论盖掉新凭据的。\n var ticket = ++requestTicket\n inflight = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<ProviderBalancePayload>(CHANNEL, ENDPOINT, {\n args: { sessionId: sessionId, force: force === true },\n })\n })\n .then(function (result) {\n // 有更新的请求已经发出去了:这份结论作废(它可能是旧凭据/旧会话的)。\n if (ticket !== requestTicket) return getState().provider\n inflight = null\n // 会话在等结果期间又换了:这一份结论不属于当前会话,丢掉。\n if (getState().sessionId !== sessionId) return getState().provider\n if (!result || result.ok !== true || !result.value) {\n lastReady = false\n failStreak += 1\n probe.log('provider-balance', { failed: true })\n return keepOr('unavailable', failureFieldsOf(result), tokenOf(result?.value))\n }\n var payload = result.value\n var token = tokenOf(payload)\n if (payload.available === true && payload.account === true) {\n // 这个提供方的账本就是平台账号那一路:胶囊回到 `HudBalance`,这里不画数。\n lastReady = true\n failStreak = 0\n probe.log('provider-balance', { account: true })\n return publish('account', null, undefined, token)\n }\n var balance = balanceOf(payload)\n if (balance === null) {\n lastReady = false\n failStreak += 1\n probe.log('provider-balance', {\n unavailable: payload.available !== true ? payload.reason : 'empty',\n // 宿主在 message 里带的原因细节(例如\"实际认出哪些提供方\"):只进日志,不上界面。\n message: (payload as { message?: unknown }).message ?? null,\n })\n return keepOr('unavailable', failureFieldsOf(result), token)\n }\n lastReady = true\n failStreak = 0\n probe.log('provider-balance', {\n provider: balance.provider,\n kind: balance.kind,\n remaining: balance.remaining === null,\n usedAccount: balance.usedAccount === null,\n usedKey: balance.usedKey === null,\n placeholder: balance.placeholderLimit,\n })\n return publish('provider', balance, undefined, token)\n })\n .catch(function (error) {\n if (ticket !== requestTicket) return getState().provider\n inflight = null\n lastReady = false\n failStreak += 1\n probe.log('provider-balance', {\n thrown: error instanceof Error ? error.message : String(error),\n })\n return keepOr('unavailable')\n })\n\n return inflight\n }\n\n /**\n * 读不到时的结论:**有**上一次的 provider 余额就继续用它(网络抖一下不该让胶囊消失),\n * 没有才落到 `unavailable`。\n *\n * @param fallbackMode - 没有上一次结果时用的模式。\n * @returns 写进快照的结论。\n */\n function keepOr(\n fallbackMode: HudProviderLedger['mode'],\n extra?: { provider?: string | null; reason?: string | null },\n token?: HudProviderToken | null,\n ): HudProviderLedger {\n var previous = getState().provider\n var stale =\n previous !== undefined && previous.mode === 'provider' && previous.balance !== null\n ? previous.balance\n : null\n if (stale !== null) return publish('provider', stale, undefined, token)\n return publish(fallbackMode, null, extra, token)\n }\n\n /**\n * 打开后台凭据设置面板。\n *\n * @param provider - 哪个提供方。\n * @param ref - 宿主给的凭据引用名(老宿主不给就 `null`,面板照样能存)。\n */\n function openTokenPanel(provider: string, ref: string | null): void {\n set({\n tokenPanel: {\n open: true,\n provider: provider,\n ref: ref,\n supported: true,\n saving: false,\n error: null,\n } satisfies TokenPanelState,\n })\n }\n\n /** \"关着\"的面板状态:取消、存成功、初始值都从这里来(免得三份各自漂移)。 */\n function closedTokenPanel(): TokenPanelState {\n return {\n open: false,\n provider: null,\n ref: null,\n supported: true,\n saving: false,\n error: null,\n }\n }\n\n /** 收起设置面板(取消 / 存完自动收起都走这里)。 */\n function closeTokenPanel(): void {\n set({ tokenPanel: closedTokenPanel() })\n }\n\n /**\n * 把用户粘贴的令牌交给宿主,写进凭据存储。\n *\n * 为什么由宿主写:令牌只该落在 DSH 自己的凭据库里(`~/.dsh/.credentials.yaml`),\n * 客户端不碰文件、不存 localStorage —— 存那里等于把密钥摊在一个任何脚本都读得到的地方。\n *\n * @param token - 用户粘贴的令牌(宿主会校验长度与形状)。\n * @returns 是否存成功(面板据此显示错误或自动收起)。\n */\n async function saveProviderToken(token: string): Promise<boolean> {\n var panel = getState().tokenPanel\n var provider = panel.provider\n if (provider === null || provider === '') {\n // 静默 `return false` 是这一整轮 bug 的形态:面板不报错、不发请求、也不关闭,\n // 用户只看到\"点了没反应\"。缺少提供方是个可报的错,就报出来。\n probe.log('provider-credential', { failed: 'no-provider' })\n set({\n tokenPanel: {\n ...getState().tokenPanel,\n saving: false,\n error: 'unknown-provider',\n },\n })\n return false\n }\n set({ tokenPanel: { ...panel, saving: true, error: null } })\n try {\n // 泛型参数就是端点返回的载荷本身(`RpcResponse<T>.value` 才是它)。\n var result = await ctx.connection.rpc.call<{\n saved?: boolean\n ref?: string\n reason?: string\n message?: string | null\n }>(CHANNEL, 'provider-credential', { args: { provider: provider, token: token } })\n var value = result && result.ok === true ? result.value : null\n if (!value || value.saved !== true) {\n var reason = value?.reason ?? 'failed'\n probe.log('provider-credential', {\n provider: provider,\n reason: reason,\n message: value?.message ?? null,\n })\n // 失败**不关**:面板得留着让人看见原因、改一改再存。\n set({ tokenPanel: { ...getState().tokenPanel, saving: false, error: reason } })\n return false\n }\n probe.log('provider-credential', { provider: provider, saved: true, ref: value.ref ?? null })\n // 存成功就把面板**当场关掉**。\n // 早先是\"置一个 savedAt、由组件里的 setTimeout(900ms) 收起\"——那条路不可靠:\n // 收起动作挂在渲染副作用上,宿主/页面任何一次异常重渲染都能把它漏掉,用户就看着\n // 一个已经存好的面板干等(他原话就是\"保存成功后要自动关闭\")。\n // 现在关闭是 store 里的一句同步状态变更,没有定时器、没有副作用依赖。\n set({ tokenPanel: closedTokenPanel() })\n // 重取**不等**:网络慢(宿主超时 8 秒)时面板早该关了,\n // 数字自己在后台到位 —— 胶囊从\"已用\"变成\"余额\"就是保存成功的回执。\n void loadProviderBalance(true)\n return true\n } catch (error) {\n probe.log('provider-credential', {\n thrown: error instanceof Error ? error.message : String(error),\n })\n set({ tokenPanel: { ...getState().tokenPanel, saving: false, error: 'failed' } })\n return false\n }\n }\n\n return {\n loadProviderBalance: loadProviderBalance,\n resetForSession: resetForSession,\n openTokenPanel: openTokenPanel,\n closeTokenPanel: closeTokenPanel,\n saveProviderToken: saveProviderToken,\n }\n}\n","/**\n * 「会话归因与审阅」这一片 store:本会话改过哪些文件、哪些标记为已审阅。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { ReviewedMap, SessionChange, SessionChangeCandidate } from '../shared.js'\nimport type {\n AttributionHelpers,\n AttributionSlice,\n GetState,\n SessionChangesPayload,\n StoreContext,\n StoreSet,\n} from './types.js'\n\nimport { CHANNEL, probe, reviewKeyOf, writeReviewed } from '../shared.js'\n\nexport function createAttributionSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: AttributionHelpers,\n): AttributionSlice {\n /** 会话归因那次请求(在途合并)。 */\n var sessionInflight: Promise<unknown> | null = null\n\n /** 胶囊调用:把当前会话的工作区写进 store,变了才重新取数。 */\n function bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void {\n if (getState().sessionId === sessionId && getState().cwd === (cwd || null)) return\n set({\n sessionId: sessionId === undefined ? null : sessionId,\n cwd: cwd || null,\n status: null,\n errorCode: null,\n commits: null,\n commitsOid: null,\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n // 差异与提交明细都绑定在某个工作区上:换了工作区就把它们收起,\n // 否则会显示另一个仓库的文件或提交。键盘光标同理,它指向的是清单里的行。\n diff: null,\n commit: null,\n cursor: null,\n phase: cwd ? 'loading' : 'idle',\n })\n // 工具活动同理:换会话就清空,别显示上一个会话正在跑的工具。\n set({\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n })\n // 归因是\"这个会话\"的事实:换会话就先清空,别把上一个会话的标记留在新会话的清单上。\n set({\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n })\n // 回合信息同理:换会话就按新会话重新接(旧的先解掉)。\n helpers.bindTurns(sessionId)\n helpers.refresh()\n }\n /**\n * 取\"本会话改过哪些文件\"。\n *\n * 与 `status` 同一条命:状态刷新时顺带取一次(都是本进程内的读,很便宜)。失败就保持\n * `files: null`(界面不打标记、也不过这个滤),不会挡住 git 那半边。\n */\n function loadSessionChanges(): Promise<unknown> {\n var cwd = getState().cwd\n var sessionId = getState().sessionId\n if (!cwd || !sessionId) return Promise.resolve()\n if (sessionInflight) return sessionInflight\n if (getState().sessionChanges.phase === 'idle')\n set({ sessionChanges: Object.assign({}, getState().sessionChanges, { phase: 'loading' }) })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionChangesPayload>(CHANNEL, 'session-changes', {\n args: { cwd: cwd, sessionId: sessionId },\n })\n })\n .then(function (result) {\n sessionInflight = null\n if (getState().cwd !== cwd || getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value && result.value.cwdMismatch === true) {\n probe.log('session-changes', { cwdMismatch: true })\n }\n if (result && result.ok === true && result.value && result.value.cwdMismatch !== true) {\n var files: Record<string, SessionChange> = {}\n var list = Array.isArray(result.value.files) ? result.value.files : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.file === 'string' && item.file !== '') files[item.file] = item\n }\n var possible: Record<string, SessionChangeCandidate> = {}\n var possibleList = Array.isArray(result.value.possible) ? result.value.possible : []\n for (var pIndex = 0; pIndex < possibleList.length; pIndex += 1) {\n var possibleItem = possibleList[pIndex]\n if (possibleItem && typeof possibleItem.file === 'string' && possibleItem.file !== '') {\n possible[possibleItem.file] = possibleItem\n }\n }\n var since = typeof result.value.since === 'number' ? result.value.since : null\n var dropped = typeof result.value.dropped === 'number' ? result.value.dropped : 0\n probe.log('session-changes', {\n files: Object.keys(files).length,\n possible: Object.keys(possible).length,\n commands: Array.isArray(result.value.commands) ? result.value.commands.length : 0,\n dropped: dropped,\n since: since === null ? null : new Date(since).toISOString(),\n })\n set({\n sessionChanges: {\n phase: 'ready',\n files: files,\n possible: possible,\n commands: Array.isArray(result.value.commands) ? result.value.commands : [],\n since: since,\n dropped: dropped,\n possibleDropped:\n typeof result.value.possibleDropped === 'number' ? result.value.possibleDropped : 0,\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-changes-failed', { code: code || 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: code || 'transport',\n },\n })\n })\n .catch(function () {\n sessionInflight = null\n if (getState().cwd !== cwd) return\n probe.log('session-changes-failed', { code: 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: 'transport',\n },\n })\n })\n\n sessionInflight = task\n return task\n }\n /**\n * 标记 / 取消\"这个文件的这一版我审过了\"。\n *\n * 签名用的是**当前**这一版(宿主 stat 出来的 size+mtimeMs):下次改动一落盘签名就变,\n * 标记自动降级成\"改动已变,需要重看\" —— 而不是继续冒充\"已审\"。\n */\n function toggleReviewed(file: string): void {\n if (typeof file !== 'string' || file.length === 0) return\n var signature = getState().review ? getState().review[file] : undefined\n if (typeof signature !== 'string') {\n probe.log('review-no-signature', { file: file })\n return\n }\n var next: ReviewedMap = {}\n for (var key in getState().reviewed)\n if (Object.prototype.hasOwnProperty.call(getState().reviewed, key))\n next[key] = getState().reviewed[key]\n var markKey = helpers.reviewedKeyOf(file)\n if (next[markKey] !== undefined && next[markKey].signature === signature) delete next[markKey]\n else next[markKey] = { signature: signature, at: Date.now() }\n set({ reviewed: writeReviewed(next) })\n }\n\n /** 清掉**当前工作区**的全部\"已审\"标记(别的仓库不动)。 */\n function clearReviewed(): void {\n var prefix = reviewKeyOf(getState().cwd, '')\n var next: ReviewedMap = {}\n var removed = 0\n for (var key in getState().reviewed) {\n if (!Object.prototype.hasOwnProperty.call(getState().reviewed, key)) continue\n if (key.indexOf(prefix) === 0) {\n removed += 1\n continue\n }\n next[key] = getState().reviewed[key]\n }\n if (removed === 0) return\n set({ reviewed: writeReviewed(next) })\n }\n\n return {\n bindSession: bindSession,\n clearReviewed: clearReviewed,\n loadSessionChanges: loadSessionChanges,\n toggleReviewed: toggleReviewed,\n }\n}\n","/**\n * 「提交与标签」这一片 store:打开某次提交、看它里面某个文件的差异、标签列表。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudCommitFile } from '../shared.js'\nimport type {\n CommitDiffPayload,\n CommitPayload,\n CommitRef,\n CommitsHelpers,\n CommitsSlice,\n GetState,\n LogPayload,\n StoreContext,\n StoreSet,\n TagsPayload,\n} from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, probe, readUi, readZoom, sameCommit } from '../shared.js'\n\nexport function createCommitsSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: CommitsHelpers,\n): CommitsSlice {\n /** 提交那几次请求(在途合并)。 */\n var commitInflight: Promise<unknown> | null = null\n /** 提交里某个文件的差异:与工作区差异**分开**合并(拆分前两者共用一个变量,\n * 拆分后留在差异片里,这里再引用就会 ReferenceError)。 */\n var commitDiffInflight: Promise<unknown> | null = null\n\n /**\n * 打开「某次提交」的视图:先取这次提交的改动清单,再自动打开第一个文件的差异,\n * 这样一次点击就能看到\"这次提交改了什么\"。\n *\n * 提交是历史事实,取回来就不再变,因此不参与轮询;只有 HEAD 变了它也不受影响\n * (面板显示的是你点的那一次提交)。\n */\n function openCommit(commit: CommitRef): Promise<unknown> {\n if (!getState().cwd || !commit || !commit.sha) return Promise.resolve()\n var cwd = getState().cwd\n var sha = commit.sha\n\n set({\n commit: {\n phase: 'loading',\n sha: sha,\n shortSha:\n typeof commit.sha === 'string' && commit.sha.length > 7 ? commit.sha.slice(0, 7) : sha,\n subject: commit.subject || '',\n author: commit.author || '',\n date: commit.date || '',\n committedAt: typeof commit.committedAt === 'number' ? commit.committedAt : null,\n refs: commit.refs || [],\n merge: false,\n files: [],\n truncated: false,\n errorCode: null,\n },\n diff: null,\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitPayload>(CHANNEL, 'commit', {\n args: { cwd: cwd, sha: sha },\n })\n })\n .then(function (result) {\n commitInflight = null\n var current = getState().commit\n // 期间用户可能已经返回清单、点了别的提交,或换了工作区。\n if (!current || current.sha !== sha || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var files = Array.isArray(value.files) ? value.files : []\n var meta = value.commit\n if (meta !== null && meta !== undefined) {\n set({\n commit: {\n phase: 'ready',\n sha: meta.sha || sha,\n shortSha: meta.shortSha || sha,\n subject: meta.subject || '',\n author: meta.author || '',\n date: meta.date || '',\n committedAt: typeof meta.committedAt === 'number' ? meta.committedAt : null,\n refs: meta.refs || [],\n merge: meta.merge === true,\n files: files,\n truncated: value.truncated === true,\n errorCode: null,\n },\n })\n // 空提交(或者清单被截断到没内容)就停在左栏,不去开一个空差异。\n // 有内容就顺手打开第一个文件——一次点击就看到\"改了什么\";这里把\n // diff 的 promise 交回去,于是 await openCommit 就是等整次点击完成。\n if (files.length > 0) return openCommitDiff(files[0])\n return undefined\n }\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n commit: Object.assign({}, current, {\n phase: 'error',\n files: [],\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitInflight = null\n var current = getState().commit\n if (!current || current.sha !== sha) return\n set({ commit: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitInflight = task\n return task\n }\n /**\n * 打开这次提交里某个文件的差异。与工作区差异共用右栏,但只有一个\"侧\"\n * (提交相对其父提交),因此工具栏不显示 未暂存/已暂存 两个页签。\n */\n function openCommitDiff(file: HudCommitFile): Promise<unknown> {\n var current = getState().commit\n if (!getState().cwd || !current || !file || !file.file) return Promise.resolve()\n var cwd = getState().cwd\n var sha = current.sha\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n\n set({\n cursor: file.file,\n diff: {\n source: 'commit',\n phase: 'loading',\n sha: sha,\n shortSha: current.shortSha,\n subject: current.subject,\n path: file.path || file.file,\n file: file.file,\n origPath: file.origFile || null,\n data: null,\n side: 'commit',\n wrap: wrap,\n zoom:\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi()),\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitDiffPayload>(CHANNEL, 'commit-diff', {\n args: { cwd: cwd, sha: sha, path: file.file, origPath: file.origFile || null },\n })\n })\n .then(function (result) {\n commitDiffInflight = null\n var now = getState().diff\n // 期间可能已经返回清单、点了别的文件,或换了提交。\n if (\n !now ||\n now.source !== 'commit' ||\n now.sha !== sha ||\n now.file !== file.file ||\n getState().cwd !== cwd\n )\n return\n if (result && result.ok === true && result.value) {\n set({\n diff: Object.assign({}, now, { phase: 'ready', data: result.value, errorCode: null }),\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: Object.assign({}, now, {\n phase: 'error',\n data: null,\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitDiffInflight = null\n var now = getState().diff\n if (!now || now.source !== 'commit' || now.file !== file.file) return\n set({ diff: Object.assign({}, now, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitDiffInflight = task\n return task\n }\n /**\n * 点最近提交里的一行:打开这次提交的明细;点已经打开的那条则收起。\n * (明细占的是右栏,左栏主面板只是高亮那一行。)\n */\n function toggleCommit(commit: CommitRef): Promise<unknown> | void {\n if (!commit || !commit.sha) return Promise.resolve()\n var openCommitState = getState().commit\n if (openCommitState !== null && sameCommit(commit.sha, openCommitState.sha))\n return closeCommit()\n return openCommit(commit)\n }\n /** 收起提交明细:它带来的差异一起收起来(清单换了,光标也要清)。 */\n function closeCommit(): void {\n if (getState().commit === null) return\n var openDiff = getState().diff\n var keep = openDiff && openDiff.source === 'commit' ? null : openDiff\n set({ commit: null, diff: keep, cursor: null })\n }\n /**\n * 取最近提交。失败只让这一段不显示,不影响面板其余部分——\n * 提交是背景信息,不该抢走「工作区状态」的注意力。\n */\n function loadCommits(cwd: string, oid: string | null): Promise<unknown> {\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<LogPayload>(CHANNEL, 'log', {\n args: { cwd: cwd, count: 10 },\n })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var commits =\n result && result.ok === true && result.value && Array.isArray(result.value.commits)\n ? result.value.commits\n : []\n set({ commits: commits, commitsOid: oid })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n set({ commits: [], commitsOid: oid })\n })\n }\n /** 取标签列表(面板开着时才取)。失败只让这一段不显示。 */\n function loadTags(cwd: string): Promise<unknown> {\n // 10s 新鲜度窗口:面板每 8s 刷新一次,这里避免每次都对 git 跑一遍 for-each-ref;\n // 又能让\"刚打的新 tag\"在十来秒内出现(HEAD 没动也得看到)。\n if (getState().tags !== null && Date.now() - getState().tagsAt < 10000) return Promise.resolve()\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<TagsPayload>(CHANNEL, 'tags', { args: { cwd: cwd } })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var value = result && result.ok === true ? result.value : null\n var tags = value && Array.isArray(value.tags) ? value.tags : []\n probe.log('tags', {\n count: tags.length,\n truncated: value ? value.truncated === true : false,\n })\n set({\n tags: tags,\n tagsAt: Date.now(),\n tagsTruncated: value ? value.truncated === true : false,\n })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n probe.log('tags-failed', { code: 'transport' })\n set({ tags: [], tagsAt: Date.now(), tagsTruncated: false })\n })\n }\n\n return {\n closeCommit: closeCommit,\n loadCommits: loadCommits,\n loadTags: loadTags,\n openCommit: openCommit,\n openCommitDiff: openCommitDiff,\n toggleCommit: toggleCommit,\n }\n}\n","/**\n * 「差异视图」这一片 store:打开哪份差异、页签、换行,清单里上下走与 hunk 折叠。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudChange, WorktreeDiffPayload } from '../shared.js'\nimport type { DiffHelpers, DiffSlice, GetState, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, readUi, readZoom } from '../shared.js'\n\nexport function createDiffSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: DiffHelpers,\n): DiffSlice {\n /** 差异那次请求(在途合并)。 */\n var diffInflight: Promise<unknown> | null = null\n\n /**\n * 打开一个改动文件的差异视图:一次取回**两侧**的 rows,\n * 默认落在有内容的那一侧(工作区优先),页签切换因此不需要再发请求。\n */\n function openDiff(change: HudChange): Promise<unknown> {\n if (!getState().cwd || !change || !change.file) return Promise.resolve()\n var cwd = getState().cwd\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n var zoom =\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi())\n set({\n // 工作区差异与「某次提交的明细」是互斥的两种右栏内容,换过去就收起明细。\n commit: null,\n cursor: change.file,\n diff: {\n source: 'worktree',\n phase: 'loading',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: wrap,\n zoom: zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<WorktreeDiffPayload>(CHANNEL, 'diff', {\n args: { cwd: cwd, path: change.file, origPath: change.origFile || null },\n })\n })\n .then(function (result) {\n diffInflight = null\n var current = getState().diff\n // 期间用户可能已经返回清单或换了文件。\n if (!current || current.file !== change.file || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var worktreeRows = value.worktree && value.worktree.rows ? value.worktree.rows.length : 0\n var indexRows = value.index && value.index.rows ? value.index.rows.length : 0\n set({\n diff: {\n source: 'worktree',\n phase: 'ready',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: value,\n side: worktreeRows > 0 || indexRows === 0 ? 'worktree' : 'index',\n wrap: current.wrap,\n zoom: current.zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: {\n source: 'worktree',\n phase: 'error',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: current.wrap,\n errorCode: code || 'git-failed',\n },\n })\n })\n .catch(function () {\n diffInflight = null\n var current = getState().diff\n if (!current || current.file !== change.file) return\n set({ diff: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n // 到这里 task 一定已被赋值;先挂进在途合并变量再返回(返回类型才不是可空的)。\n diffInflight = task\n return task\n }\n function closeDiff(): void {\n if (getState().diff === null) return\n set({ diff: null })\n }\n function setDiffSide(side: string): void {\n var diff = getState().diff\n if (!diff || diff.source === 'commit' || (side !== 'worktree' && side !== 'index')) return\n // 换侧等于换了一份 rows:hunk 序号与折叠状态都不再对应得上。\n set({ diff: Object.assign({}, diff, { side: side, hunk: 0, collapsed: [] }) })\n }\n /**\n * 在当前清单里上下移动一格(↑/↓ 只移动;`[`/`]` 移动并直接打开)。\n *\n * 还没选过时:向下从第一条开始,向上从最后一条开始 —— 这样 ↑ 和 ↓ 都能\n * \"先落在一个合理的位置上\",不需要用户先点一行。\n *\n * @param delta - 1 向下、-1 向上。\n * @param open - 移动后是否立刻打开。\n * @returns 选中的那条,清单为空时返回 null。\n */\n function stepFile(delta: number, open: boolean): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n\n var index = -1\n for (var cursor = 0; cursor < list.length; cursor += 1) {\n if (list[cursor].file === getState().cursor) {\n index = cursor\n break\n }\n }\n\n var next =\n index === -1\n ? delta > 0\n ? 0\n : list.length - 1\n : Math.min(Math.max(0, index + delta), list.length - 1)\n var entry = list[next]\n if (!entry || !entry.file) return null\n\n set({ cursor: entry.file })\n if (open) helpers.openListEntry(entry)\n return entry\n }\n /** Enter:打开键盘选中的那条;还没选过就打开第一条。 */\n function openCursor(): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n for (var index = 0; index < list.length; index += 1) {\n if (list[index].file === getState().cursor) {\n helpers.openListEntry(list[index])\n return list[index]\n }\n }\n return stepFile(1, true)\n }\n /**\n * n / p:跳到下一个 / 上一个 hunk。跳到折叠着的 hunk 会顺手展开它 ——\n * 不然按键看起来什么都没发生。\n */\n function stepHunk(delta: number): void {\n var diff = getState().diff\n if (!diff) return\n var total = helpers.hunkCount()\n if (total === 0) return\n var current = typeof diff.hunk === 'number' ? diff.hunk : 0\n var next = Math.min(Math.max(0, current + delta), total - 1)\n var collapsed = (diff.collapsed || []).filter(function (index) {\n return index !== next\n })\n set({ diff: Object.assign({}, diff, { hunk: next, collapsed: collapsed }) })\n }\n /** 折叠 / 展开一个 hunk(点 hunk 头,或 Space 作用于当前 hunk)。 */\n function toggleHunk(index: number | undefined): void {\n var diff = getState().diff\n if (!diff || typeof index !== 'number' || index < 0) return\n var collapsed = diff.collapsed || []\n var next =\n collapsed.indexOf(index) >= 0\n ? collapsed.filter(function (item) {\n return item !== index\n })\n : collapsed.concat([index])\n set({ diff: Object.assign({}, diff, { hunk: index, collapsed: next }) })\n }\n function toggleDiffWrap(): void {\n var diff = getState().diff\n if (!diff) return\n set({ diff: Object.assign({}, diff, { wrap: diff.wrap !== true }) })\n helpers.persist()\n }\n\n return {\n closeDiff: closeDiff,\n openCursor: openCursor,\n openDiff: openDiff,\n setDiffSide: setDiffSide,\n stepFile: stepFile,\n stepHunk: stepHunk,\n toggleDiffWrap: toggleDiffWrap,\n toggleHunk: toggleHunk,\n }\n}\n","import { CHANNEL, imagePairKey } from '../shared.js'\nimport type { ImageRequest, ImagePayload } from '../shared.js'\nimport type { ImageSlice, ImageView, StoreContext } from './types.js'\n\n/**\n * 「图片预览」这一片 store:二进制改动两侧的字节与尺寸,带一个同步缓存。\n */\n\nexport function createImageSlice(ctx: StoreContext): ImageSlice {\n var imagePairs = new Map<string, ImageView>()\n /** 这一片自己的缓存上限(照宿主半的 `CACHE_MAX_ENTRIES` 同一个量级)。 */\n var IMAGE_CACHE_MAX = 8\n\n /**\n * 同步看一眼缓存:命中就返回条目,否则 null。\n *\n * 有了它,`ImageDiff` 的**第一次渲染**就能把已经取过的图画出来 ——\n * 关掉再打开同一张图不该先闪一下\"读取图片…\",也不该再发一次请求。\n */\n function cachedImagePair(request: ImageRequest): ImageView | null {\n if (request === null || request === undefined) return null\n var entry = imagePairs.get(imagePairKey(request))\n return entry === undefined ? null : entry\n }\n\n /** 供 `ImageDiff` 用:取一个二进制改动两侧的字节与尺寸(同一个请求只发一次)。 */\n function loadImagePair(request: ImageRequest): Promise<ImageView> {\n var key = imagePairKey(request)\n var cached = imagePairs.get(key)\n if (cached !== undefined) return Promise.resolve(cached)\n\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<ImagePayload>(CHANNEL, 'image', { args: request })\n })\n .then(function (result) {\n var entry: ImageView =\n result && result.ok === true && result.value\n ? { phase: 'ready', value: result.value }\n : {\n phase: 'error',\n code:\n result && result.error && result.error.details ? result.error.details.code : null,\n }\n if (imagePairs.size >= IMAGE_CACHE_MAX) imagePairs.clear()\n imagePairs.set(key, entry)\n return entry\n })\n .catch(function () {\n return { phase: 'error', code: 'transport' }\n })\n }\n\n return { cachedImagePair: cachedImagePair, loadImagePair: loadImagePair }\n}\n","/**\n * 「会话信息」这一片 store:会话运行时长(3 秒 / 60 秒两档 TTL)与 prompt 历史。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`(由 `createStore` 注入)。\n */\n\nimport type { HudPromptHistory, HudSessionTime } from '../shared.js'\nimport type { GetState, SessionInfoSlice, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, PROMPT_HISTORY_MAX } from '../shared.js'\n\nexport function createSessionInfoSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n): SessionInfoSlice {\n /**\n * 会话运行时长的缓存:每分钟最多问一次(统计数字不需要更密),同会话复用。\n *\n * 与图片那种\"重\"缓存分开存:这个条目只有几十字节,TTL 到期直接覆盖。\n */\n var sessionTimes = new Map<string, { at: number; value: HudSessionTime }>()\n /** 拿到数的缓存时长。 */\n var SESSION_TIME_TTL_MS = 60000\n /**\n * **没拿到**时的重问间隔。\n *\n * 会话是异步恢复到宿主 `sessions` 存储里的:重启后头几百毫秒问 `session-time` 得到的是\n * \"这个会话还不存在\"(实测启动 0.6 秒时 live=0,30 秒后才 live=1)。\n * 把这种答案按 60 秒缓存,就会让计时胶囊在你重启后一两分钟才出现 —— 所以负面结果只记 3 秒。\n */\n var SESSION_TIME_RETRY_MS = 3000\n /** 取一个会话的运行时长(含内置 `sessionStats` 的累计数字);拿不到回 `available: false`。 */\n function loadSessionTime(sessionId: string): Promise<HudSessionTime> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false })\n var cached = sessionTimes.get(sessionId)\n if (cached !== undefined) {\n var ttl =\n cached.value && cached.value.available === true\n ? SESSION_TIME_TTL_MS\n : SESSION_TIME_RETRY_MS\n if (Date.now() - cached.at < ttl) return Promise.resolve(cached.value)\n }\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudSessionTime>(CHANNEL, 'session-time', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n var value: HudSessionTime =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId }\n if (sessionTimes.size >= 16) sessionTimes.clear()\n sessionTimes.set(sessionId, { at: Date.now(), value: value })\n var previous: HudSessionTime | null =\n getState().sessionTime && getState().sessionTime.sessionId === sessionId\n ? getState().sessionTime.value\n : null\n // 深度比较没必要:这两个字段(会话创建时刻 + 累计墙钟)就是整份值里会变的部分。\n var same =\n previous !== null &&\n previous.available === value.available &&\n previous.createdAt === value.createdAt &&\n previous.llmMs === value.llmMs &&\n previous.toolMs === value.toolMs &&\n previous.turns === value.turns &&\n previous.steps === value.steps\n if (!same) set({ sessionTime: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId }\n })\n }\n /**\n * prompt 历史:宿主从内置 `turnOutline` 投影里取,比客户端自己记一份可靠得多\n * (分页、压实、重启、换设备都不影响)。prompt 只在\"一整轮发出去\"之后才会变,\n * 所以 5 秒 TTL 足够,而且顺手把值写进快照 —— 按键那侧只读快照,不必再 await。\n */\n var promptHistories = new Map<string, { at: number; value: HudPromptHistory }>()\n var PROMPT_HISTORY_TTL_MS = 5000\n\n function loadPromptHistory(sessionId: string): Promise<HudPromptHistory> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false, prompts: [] })\n var cached = promptHistories.get(sessionId)\n if (cached !== undefined && Date.now() - cached.at < PROMPT_HISTORY_TTL_MS)\n return Promise.resolve(cached.value)\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudPromptHistory>(CHANNEL, 'prompt-history', {\n args: { sessionId: sessionId, limit: PROMPT_HISTORY_MAX },\n })\n })\n .then(function (result) {\n var value: HudPromptHistory =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId, prompts: [] }\n if (promptHistories.size >= 16) promptHistories.clear()\n promptHistories.set(sessionId, { at: Date.now(), value: value })\n var previous: HudPromptHistory | null =\n getState().promptHistory && getState().promptHistory.sessionId === sessionId\n ? getState().promptHistory.value\n : null\n var same =\n previous !== null &&\n previous.available === value.available &&\n (previous.prompts || []).length === (value.prompts || []).length &&\n (previous.prompts || []).every(function (item, index) {\n return item === value.prompts[index]\n })\n if (!same) set({ promptHistory: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId, prompts: [] }\n })\n }\n\n return { loadPromptHistory: loadPromptHistory, loadSessionTime: loadSessionTime }\n}\n","/**\n * 「悬停提示」这一片 store:把气泡的文案与锚点放进快照,由 overlay 里那枚座位去画。\n */\n\nimport type { GetState, StoreSet, TipAnchor, TipHandlers, TipSlice } from './types.js'\nimport type { TipContent } from '../shared.js'\nimport { normalizeTipContent } from '../shared.js'\n\n/** 事件里的锚点矩形:真实 DOM 给的是 DOMRect(结构上就是这三个数)。 */\ninterface AnchorTarget {\n getBoundingClientRect?: () => TipAnchor\n}\n\nexport function createTipSlice(getState: GetState, set: StoreSet): TipSlice {\n /** 显示某枚胶囊的悬停提示(`rect` 由调用方从 `getBoundingClientRect()` 给)。 */\n function showTip(key: string, content: TipContent, rect: TipAnchor | null): void {\n var normalized = normalizeTipContent(content)\n if (normalized.text.length === 0) return\n var at: TipAnchor =\n rect && typeof rect === 'object'\n ? { left: rect.left, top: rect.top, right: rect.right }\n : { left: 0, top: 0, right: 0 }\n // `text` 与 `lines` 一起存:渲染只认 `lines`,`text` 留给日志/断言/兼容老读者。\n set({ tip: { key: key, text: normalized.text, lines: normalized.lines, rect: at } })\n }\n\n /** 收起悬停提示。带 `key` 时只在\"还是这一枚\"的情况下收 —— 免得旧的 leave 把新的提示关掉。 */\n function hideTip(key?: string): void {\n var current = getState().tip\n if (current === null || current === undefined) return\n if (typeof key === 'string' && current.key !== key) return\n set({ tip: null })\n }\n\n /**\n * 一枚胶囊的悬停接线:算出锚点矩形,把文案交给 store。\n *\n * 为什么走 store 而不是就地渲染气泡:气泡必须**逃出行内盒**(不被 dock 那行的裁切与\n * 堆叠上下文限制),所以它由 `shell.overlay` 里一枚单独的座位画,位置用 `position:fixed`。\n */\n function tipHandlers(key: string, contentOf: () => TipContent): TipHandlers {\n return {\n onPointerEnter: function (event) {\n // 事件目标是 DOM 边界:只按 `getBoundingClientRect` 这一项能力收窄。\n var target = event && event.currentTarget ? (event.currentTarget as AnchorTarget) : null\n var rect =\n target && typeof target.getBoundingClientRect === 'function'\n ? target.getBoundingClientRect()\n : null\n showTip(key, contentOf(), rect)\n },\n onPointerLeave: function () {\n hideTip(key)\n },\n onPointerDown: function () {\n hideTip(key)\n },\n }\n }\n\n return { hideTip: hideTip, showTip: showTip, tipHandlers: tipHandlers }\n}\n","/**\n * 「工具活动 / 侧栏摘要 / 回合信息」这一片 store:三样都跟会话走、与工作区无关,各自一条轮询节拍;回合信息还带一个 sessions 动态注入。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { HudSessionOverviewRow, HudToolActive, TurnsState } from '../shared.js'\nimport type {\n GetState,\n SessionStatusesPayload,\n SessionToolsPayload,\n StoreContext,\n SessionsService,\n StoreSet,\n ToolsSlice,\n TurnFace,\n TurnSession,\n} from './types.js'\n\nimport { CHANNEL, probe } from '../shared.js'\n\n/** 回合投影的订阅记账:`{ sessionId, dispose }`;`null` 表示没接。 */\ninterface TurnsBinding {\n sessionId: string | null\n dispose: (() => void) | null\n}\n\nexport function createToolsSlice(ctx: StoreContext, getState: GetState, set: StoreSet): ToolsSlice {\n /** 工具活动那次请求(在途合并)。 */\n var toolsInflight: Promise<unknown> | null = null\n /** 侧栏摘要那次请求(在途合并)。 */\n var rowsInflight: Promise<unknown> | null = null\n /** 回合投影的订阅:`{ sessionId, dispose }`;`null` 表示没接。 */\n var turnsBound: TurnsBinding | null = null\n /** `sessions` 客户端服务(动态注入;没到齐就是 null,回合信息不显示)。 */\n var turnSessions: SessionsService | null = null\n\n // 回合信息要的 `sessions` 客户端服务:**动态注入**等它到齐(软依赖)——\n // 缺它时 git、归因、胶囊一切照旧,只是不显示\"第几轮\"。到齐后把当前会话接上。\n if (typeof ctx.inject === 'function') {\n ctx.effect(function () {\n return ctx.inject(['sessions'], function (scope) {\n turnSessions = scope.sessions\n probe.log('turn-services', { sessions: true })\n bindTurns(getState().sessionId)\n return function () {\n turnSessions = null\n unbindTurns()\n }\n })\n }, 'dsh-hud: turn services')\n }\n\n /**\n * 接上 `turnOutline` 投影(\"第几轮改的\")。\n *\n * 软依赖:`sessions` 服务、已物化的会话、这个投影三者缺一个就安静地不接 —— 归因、\n * git、胶囊一切照旧,只是不显示回合信息。整段包在 try/catch 里,读不到不该把面板带崩。\n */\n function unbindTurns(): void {\n if (turnsBound === null) return\n try {\n if (typeof turnsBound.dispose === 'function') turnsBound.dispose()\n } catch (error) {\n // 解绑失败不影响接下来的绑定。\n }\n turnsBound = null\n set({ turns: { available: false, prompts: {}, latest: null } })\n }\n\n function bindTurns(sessionId: string | null | undefined): void {\n if (turnsBound !== null && turnsBound.sessionId === sessionId) return\n unbindTurns()\n if (!sessionId || turnSessions === null) return\n try {\n var bound = turnSessions.binding(sessionId)\n var live = bound ? bound.session : undefined\n if (!live || !live.projections || typeof live.projections.faceOf !== 'function') return\n var face = live.projections.faceOf('turnOutline')\n if (!face || typeof face.getSnapshot !== 'function') return\n // 已用 typeof 确认 getSnapshot 是函数:这里断言成\"带 getSnapshot 的 face\",\n // 让闭包里可以保持 this 绑定地调用它(把方法拆出来再调会丢 this)。\n var liveFace = face as TurnFace & { getSnapshot: () => unknown }\n var apply = function () {\n try {\n set({ turns: normalizeTurns(liveFace.getSnapshot()) })\n } catch (error) {\n probe.log('turns-read-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n apply()\n var dispose = typeof liveFace.subscribe === 'function' ? liveFace.subscribe(apply) : null\n turnsBound = { sessionId: sessionId, dispose: dispose }\n } catch (error) {\n // 会话还没物化、投影还没注册、注入没到位 —— 都只意味着\"这一轮拿不到回合信息\"。\n probe.log('turns-bind-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n /** 归一 `turnOutline` 快照:`{ turns: [{ turn, prompt }], draft }`。 */\n function normalizeTurns(snapshot: unknown): TurnsState {\n // 投影快照是外部服务给的 unknown:先收成记录,再逐项判断。\n var record =\n snapshot !== null && typeof snapshot === 'object' ? (snapshot as { turns?: unknown }) : null\n var entries = record !== null && Array.isArray(record.turns) ? record.turns : null\n if (entries === null) return { available: false, prompts: {}, latest: null }\n var prompts: Record<number, string> = {}\n var latest: number | null = null\n for (var index = 0; index < entries.length; index += 1) {\n var entry = entries[index]\n if (!entry || typeof entry !== 'object') continue\n // 每条 turn 也是外部形状:同样先收成记录再逐字段判断。\n var item = entry as { turn?: unknown; prompt?: unknown }\n if (typeof item.turn !== 'number') continue\n if (typeof item.prompt === 'string' && item.prompt.length > 0)\n prompts[item.turn] = item.prompt\n if (latest === null || item.turn > latest) latest = item.turn\n }\n return { available: true, prompts: prompts, latest: latest }\n }\n\n /**\n * 取侧栏每条会话行要的摘要(**一次批量**,不是 N 次 RPC)。\n *\n * 会话列表由**宿主**枚举(它有 `sessions` 服务),客户端不用把名单端过去;宿主没有\n * 那个服务时回 `unavailable: true`,那些 chip 就整片不画(其余功能照旧)。\n */\n function refreshRows(): Promise<unknown> {\n if (rowsInflight) return rowsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionStatusesPayload>(CHANNEL, 'session-statuses', {\n args: {},\n })\n })\n .then(function (result) {\n rowsInflight = null\n if (result && result.ok === true && result.value) {\n var byId: Record<string, HudSessionOverviewRow> = {}\n var list = Array.isArray(result.value.sessions) ? result.value.sessions : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.sessionId === 'string') byId[item.sessionId] = item\n }\n probe.log('session-statuses', {\n sessions: list.length,\n unavailable: result.value.unavailable === true,\n worktrees: typeof result.value.worktrees === 'number' ? result.value.worktrees : null,\n truncated: result.value.truncated === true,\n })\n set({\n rows: {\n phase: 'ready',\n byId: byId,\n unavailable: result.value.unavailable === true,\n truncated: result.value.truncated === true,\n updatedAt: Date.now(),\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-statuses-failed', { code: code || 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n rowsInflight = null\n probe.log('session-statuses-failed', { code: 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n rowsInflight = task\n return task\n }\n\n /** 取本会话的工具活动(正在跑哪个 / 跑过哪些 / 各几次)。 */\n function loadTools(): Promise<unknown> {\n var sessionId = getState().sessionId\n if (!sessionId) return Promise.resolve()\n if (toolsInflight) return toolsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionToolsPayload>(CHANNEL, 'session-tools', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n toolsInflight = null\n if (getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var active: HudToolActive | null =\n value.active && typeof value.active === 'object' ? value.active : null\n var tools = {\n phase: 'ready',\n active: active,\n recent: Array.isArray(value.recent) ? value.recent : [],\n counts: Array.isArray(value.counts) ? value.counts : [],\n skills: Array.isArray(value.skills) ? value.skills : [],\n skillsDropped: typeof value.skillsDropped === 'number' ? value.skillsDropped : 0,\n total: typeof value.total === 'number' ? value.total : 0,\n updatedAt: Date.now(),\n }\n // 内容没变就不要 set:这个节拍很密(见 TOOLS_POLL_MS),无谓的重渲染会到处都是。\n var previous = getState().tools\n var same =\n previous !== null && previous.active !== null && tools.active !== null\n ? previous.active.callId === tools.active.callId && previous.total === tools.total\n : previous !== null &&\n previous.active === null &&\n tools.active === null &&\n previous.total === tools.total &&\n previous.recent.length === tools.recent.length &&\n (previous.recent.length === 0 || previous.recent[0].at === tools.recent[0].at)\n if (same) return\n probe.log('session-tools', {\n active: tools.active === null ? null : tools.active.name,\n total: tools.total,\n kinds: tools.counts.length,\n // 技能单独露面:排查\"技能名有没有记到\"时不必再靠猜(这一版新增的字段)。\n skills: tools.skills.length,\n })\n set({ tools: tools })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-tools-failed', { code: code || 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n toolsInflight = null\n probe.log('session-tools-failed', { code: 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n toolsInflight = task\n return task\n }\n\n return {\n bindTurns: bindTurns,\n loadTools: loadTools,\n refreshRows: refreshRows,\n unbindTurns: unbindTurns,\n }\n}\n","/**\n * 共享 store 的组装层:自己管状态、订阅、持久化与布局,七片各自管自己的域。\n *\n * 它对外只暴露浏览器半需要的那一套(打开面板 / 差异 / 提交 / 会话列表摘要 / 审阅 …),\n * 每个\"片\"的入口都只是转发到这个文件外面的小工厂。\n */\n\nimport { diffPayload } from '../components/diff-view.js'\nimport type { HudChange, HudState, HudStore, HudStatus, PanelPos } from '../shared.js'\nimport {\n CHANNEL,\n COMMIT_LIST_WIDTH,\n COMMIT_MIN_WIDTH,\n DIFF_DEFAULT_WIDTH,\n DIFF_MIN_WIDTH,\n ZOOM_STEPS,\n readReviewed,\n readUi,\n reviewKeyOf,\n visibleChangeRows,\n writeUi,\n} from '../shared.js'\nimport type { StoreContext } from './types.js'\nimport { createAccountSlice } from './account.js'\nimport { createProviderSlice } from './provider.js'\nimport { createAttributionSlice } from './attribution.js'\nimport { createCommitsSlice } from './commits.js'\nimport { createDiffSlice } from './diff.js'\nimport { createImageSlice } from './image.js'\nimport { createSessionInfoSlice } from './session-info.js'\nimport { createTipSlice } from './tip.js'\nimport { createToolsSlice } from './tools.js'\nexport function createStore(ctx: StoreContext): HudStore {\n var imageSlice = createImageSlice(ctx)\n var ui = readUi()\n var pos: PanelPos | null =\n ui.pos &&\n typeof ui.pos === 'object' &&\n typeof ui.pos.left === 'number' &&\n typeof ui.pos.top === 'number'\n ? { left: ui.pos.left, top: ui.pos.top }\n : null\n\n var state: HudState = {\n phase: 'idle',\n sessionId: null,\n cwd: null,\n status: null,\n errorCode: null,\n updatedAt: null,\n panelOpen: ui.open === true,\n pos: pos,\n /** 胶囊所在 dock 行的视口顶部坐标:面板据此贴到它上方,而不是用写死的偏移。 */\n anchorTop: null,\n /** 正在查看的差异;`null` 表示面板停在改动清单。 */\n diff: null,\n /**\n * 正在查看的提交(点了「最近提交」里的一行);`null` 表示左栏是工作区清单。\n * 打开时左栏换成这次提交的改动清单,右栏打开它第一个文件的差异。\n */\n commit: null,\n /**\n * 键盘高亮的那个文件(清单里的 `file`);`null` 表示还没用键盘选过。\n * 指向 `file` 而不是下标:轮询刷新后清单条目可能增减,下标会指错行。\n */\n cursor: null,\n /** 最近提交;`commitsOid` 记住它是哪个 HEAD 取的,oid 变了才重取。 */\n commits: null,\n commitsOid: null,\n /**\n * 标签列表。与提交不同,**新 tag 可以打在老提交上**(HEAD 不动),所以不能只按 oid 判断;\n * 改用\"上次取的时刻\"做新鲜度窗口:面板开着时每次刷新都看一眼,但 10s 内不重复问 git。\n */\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n /** 差异栏宽度(面板右边缘可拖),持久化。 */\n diffWidth: typeof ui.diffWidth === 'number' ? ui.diffWidth : DIFF_DEFAULT_WIDTH,\n /** 提交明细栏宽度(它与差异栏之间那条抓握可拖),持久化。 */\n commitWidth: typeof ui.commitWidth === 'number' ? ui.commitWidth : COMMIT_LIST_WIDTH,\n /**\n * 会话归因:本会话改过哪些文件(宿主半按 fs 事件记的)。\n *\n * `files` 为 `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)——\n * 界面据此不打标记、也不过这个滤。\n */\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n /** 「只看本会话改的」开关(持久化,跟\"自动换行\"一样)。 */\n onlySession: ui.onlySession === true,\n /** 「只看最近一轮」开关:把清单收窄到最近这一轮动过的文件。 */\n onlyLatest: ui.onlyLatest === true,\n /** 这一版每个改动文件的签名(`file → signature`);空表表示\"这次没要\"。 */\n review: {},\n /**\n * 本会话的工具活动:`active`(正在跑哪个)+ `recent` + 计数。\n *\n * 实时秒数由客户端本地算(`active.startedAt`),不靠把轮询压到亚秒级。\n */\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n // 会话时长(计时胶囊用):`{ sessionId, value }`;取数写进快照,渲染那边就是纯函数。\n sessionTime: { sessionId: null, value: null },\n // prompt 历史(输入框 `↑` 召回用):`{ sessionId, value }`,value 里 prompts 是**新的在前**。\n promptHistory: { sessionId: null, value: null },\n // 账号余额:**账号级**读数(换会话、换工作区都不该让它消失)。`null` = 还没读到 /\n // 没登录 / 这个宿主没有账号能力 —— 三种情况界面一样是\"不画这枚胶囊\"。\n balance: null,\n // 当前会话模型来源的账本:首帧还不知道是哪个提供方,先按平台账号那一路画。\n provider: { mode: 'unknown', balance: null },\n // 后台凭据设置面板:平时关着,点胶囊(且宿主说\"这个站还没配令牌\")时才开。\n tokenPanel: {\n open: false,\n provider: null,\n ref: null,\n supported: true,\n saving: false,\n error: null,\n },\n // 胶囊悬停提示:`{ key, text, rect }` 或 null。**自己画**,不用浏览器原生 tooltip ——\n // 原生那个在真机上按了没反应(试过三种节点/容器查证,都不是被谁挡住),\n // 而它延迟、样式、能否出现我们都控制不了;自己画则位置、换行、颜色全说了算。\n tip: null,\n /** 侧栏行内 chip 要的摘要:一次批量取回,按 sessionId 索引。 */\n rows: { phase: 'idle', byId: {}, unavailable: false, truncated: false, updatedAt: null },\n /**\n * 我审过的版本:`工作区 + NUL + 文件 → { signature, at }`,存在自己的键里\n * (`dsh-hud.reviewed.v1`,不跟界面开关混在一起)。\n */\n reviewed: readReviewed(),\n /**\n * 回合信息(`turnOutline` 投影):`第 N 轮 → 那一轮的请求预览`。\n *\n * `available: false` 表示这个宿主没有这个投影(或还没读到)——界面这时不提回合,\n * 也绝不因为读不到它影响别的部分。\n */\n turns: { available: false, prompts: {}, latest: null },\n }\n\n var listeners = new Set<() => void>()\n var inflight: Promise<unknown> | null = null\n /** 会话归因那次请求(在途合并)。 */\n\n function getSnapshot(): HudState {\n return state\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return function () {\n listeners.delete(listener)\n }\n }\n\n function emit(): void {\n listeners.forEach(function (listener) {\n listener()\n })\n }\n\n function set(patch: Partial<HudState>): void {\n state = Object.assign({}, state, patch)\n emit()\n }\n\n // 悬停提示片:用到 state/set,所以在两者定义之后再组装。\n var tipSlice = createTipSlice(function () {\n return state\n }, set)\n var sessionInfoSlice = createSessionInfoSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n // 账号余额:软依赖 + 自己的 TTL(它打的是平台网络,不是本地 git)。\n var accountSlice = createAccountSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n // 当前会话模型来源的余额:按会话取、慢节拍(中转站被轮询会限流)。\n var providerSlice = createProviderSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var toolsSlice = createToolsSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var commitsSlice = createCommitsSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n closeDiff: function () {\n return diffSlice.closeDiff()\n },\n },\n )\n var attributionSlice = createAttributionSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 惰性包装:这几样分属工具片与组装层,用包装就不必纠结创建顺序。\n bindTurns: function (sessionId) {\n return toolsSlice.bindTurns(sessionId)\n },\n loadTools: function () {\n return toolsSlice.loadTools()\n },\n refresh: function () {\n return refresh()\n },\n refreshRows: function () {\n return toolsSlice.refreshRows()\n },\n reviewedKeyOf: reviewedKeyOf,\n },\n )\n var diffSlice = createDiffSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 这两样暂住在 store 里(清单选择 / 提交差异),等提交那一片搬走时再挪过去。\n hunkCount: hunkCount,\n openCommitDiff: commitsSlice.openCommitDiff,\n openListEntry: openListEntry,\n persist: persist,\n visibleFiles: visibleFiles,\n },\n )\n\n function persist(): void {\n var diff = state.diff\n writeUi({\n onlySession: state.onlySession === true,\n onlyLatest: state.onlyLatest === true,\n open: state.panelOpen,\n pos: state.pos,\n wrap: diff ? diff.wrap === true : false,\n zoom:\n diff && typeof diff.zoom === 'number' && ZOOM_STEPS.indexOf(diff.zoom) >= 0\n ? diff.zoom\n : null,\n diffWidth: state.diffWidth,\n commitWidth: state.commitWidth,\n })\n }\n\n /** 拖动提交栏右边那条抓握时只改内存;松手才落盘。 */\n function setCommitWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(COMMIT_MIN_WIDTH, Math.round(width))\n if (state.commitWidth === next) return\n set({ commitWidth: next })\n }\n\n /** 拖动右边缘时只改内存;松手才落盘(persistLayout)。 */\n function setDiffWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(DIFF_MIN_WIDTH, Math.round(width))\n if (state.diffWidth === next) return\n set({ diffWidth: next })\n }\n\n /* --------------------------------------------------- 图片预览(二进制) */\n\n /**\n * 图片两侧的缓存:键与请求一一对应。\n *\n * 一张图的 base64 可能上兆,所以命中就不重取;条目数有上限(超了整表清空,\n * 和宿主的 `CACHE_MAX_ENTRIES` 同一个思路:宁可少缓存,也不要无界增长)。\n */\n\n /**\n * 收起右栏。提交明细把差异包在里面,所以它优先整体退掉;\n * 只有工作区差异时才只是关掉差异。\n */\n function closeDetail(): void {\n if (state.commit !== null) return commitsSlice.closeCommit()\n return diffSlice.closeDiff()\n }\n\n /**\n * 当前能上下选的那份清单:打开提交明细时是这次提交的改动,否则是工作区清单。\n *\n * 清单必须跟视图一致 —— 否则打开着提交明细时按 ↑/↓ 会去动工作区的行,\n * 高亮和打开的文件对不上。\n */\n function visibleFiles(): HudChange[] {\n if (state.commit !== null) {\n return Array.isArray(state.commit.files) ? state.commit.files : []\n }\n // 与面板渲染**共用同一份**过滤结果:过滤只发生在一个地方,↑/↓ 才不会落到看不见的行上。\n return visibleChangeRows(state)\n }\n\n /** 打开清单里的一条:提交明细打开时走提交差异,否则走工作区差异。 */\n function openListEntry(file: HudChange): Promise<unknown> {\n if (state.commit !== null) return commitsSlice.openCommitDiff(file)\n return diffSlice.openDiff(file)\n }\n\n /** 当前差异里有几个 hunk —— n / p 的边界就是它。 */\n function hunkCount(): number {\n var payload = diffPayload(state.diff)\n var rows = payload && payload.rows ? payload.rows : []\n var total = 0\n for (var index = 0; index < rows.length; index += 1) {\n if (rows[index].kind === 'hunk') total += 1\n }\n return total\n }\n\n /**\n * 图片缩放的档位(`null` = 适应宽度)。\n *\n * 放在 store 而不是组件里:一来记住(和\"自动换行\"一样落盘),二来组件的 hooks\n * 在这个假 React 里驱动不了嵌套状态 —— store 的状态任何位置都读得到。\n */\n function setZoom(value: number | null): void {\n var diff = state.diff\n if (!diff) return\n var next = value === null || ZOOM_STEPS.indexOf(value) >= 0 ? value : null\n set({ diff: Object.assign({}, diff, { zoom: next }) })\n persist()\n }\n\n /** dock 行的位置变了才写,避免每次测量都触发重渲染。 */\n function setAnchorTop(top: number | null): void {\n if (state.anchorTop === top) return\n set({ anchorTop: top })\n }\n\n function refresh(): Promise<unknown> {\n // 会话归因跟 status 同一条命(两个都是\"当前会话 + 当前工作区\"的只读事实)。\n attributionSlice.loadSessionChanges()\n // 工具活动只跟会话有关(没有工作区也该显示\"正在跑什么\")。\n toolsSlice.loadTools()\n // 回合投影:会话还没物化时这里会安静地不接,下一次刷新再试。\n toolsSlice.bindTurns(state.sessionId)\n if (!state.cwd) return Promise.resolve()\n if (inflight) return inflight\n var cwd = state.cwd\n\n set({ phase: state.status ? 'ready' : 'loading' })\n\n inflight = Promise.resolve()\n .then(function () {\n // 面板打开时才要\"这一版\"的签名:胶囊那一路不需要,白付一次 stat。\n return ctx.connection.rpc.call<HudStatus>(CHANNEL, 'status', {\n args: { cwd: cwd, review: state.panelOpen === true },\n })\n })\n .then(function (result) {\n inflight = null\n if (state.cwd !== cwd) return\n if (result && result.ok === true) {\n var statusValue = result.value || null\n set({\n phase: 'ready',\n status: statusValue,\n // 送回来的\"这一版\"签名(没要或没改动就是空表)。\n review:\n statusValue && statusValue.review && typeof statusValue.review === 'object'\n ? statusValue.review\n : {},\n errorCode: null,\n updatedAt: Date.now(),\n })\n // 提交只取一次:面板打开时取,且只有 HEAD 变了(提交过、切分支、拉取)才重取。\n if (\n statusValue &&\n state.panelOpen &&\n (state.commits === null || state.commitsOid !== statusValue.oid)\n ) {\n commitsSlice.loadCommits(cwd, statusValue.oid)\n }\n // 标签与 HEAD 无关(新 tag 可以打在老提交上),所以只要面板开着就问一次(10s 窗口)。\n if (statusValue && state.panelOpen) commitsSlice.loadTags(cwd)\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({ phase: 'error', errorCode: code || 'git-failed' })\n })\n .catch(function () {\n inflight = null\n if (state.cwd !== cwd) return\n set({ phase: 'error', errorCode: 'transport' })\n })\n\n return inflight\n }\n\n /** 审阅存档的键:**按工作区分开**,同一个文件名在别的仓库里是另一码事。 */\n function reviewedKeyOf(file: string): string {\n return reviewKeyOf(state.cwd, file)\n }\n\n /** 「只看最近一轮」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlyLatest(): void {\n set({ onlyLatest: state.onlyLatest !== true })\n persist()\n }\n\n /** 「只看本会话改的」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlySession(): void {\n set({ onlySession: state.onlySession !== true })\n persist()\n }\n\n function openPanel(open: boolean): void {\n if (state.panelOpen === open) return\n set({ panelOpen: open })\n persist()\n if (open) refresh()\n }\n\n function togglePanel(): void {\n openPanel(!state.panelOpen)\n }\n\n /** 拖动过程中的临时位置:只改内存,不写 localStorage。 */\n function movePanel(next: PanelPos): void {\n set({ pos: next })\n }\n\n /** 松开指针后的位置:落盘。 */\n function commitPanel(next: PanelPos): void {\n set({ pos: next })\n persist()\n }\n\n function resetPanel(): void {\n set({ pos: null })\n persist()\n }\n\n return {\n getSnapshot: getSnapshot,\n subscribe: subscribe,\n /**\n * 绑定当前会话(胶囊/turn-images 的 effect 调它)。\n *\n * 绑完之后**立刻**去问\"这个会话的模型来源是哪个账本\":早先这一步挂在另一个 effect 里,\n * 而那个 effect 的依赖只有 `sessionId` —— 它先于 `bindSession` 跑,此时 store 里的\n * `sessionId` 还是 `null`,于是 provider 侧永远是 `unknown`(胶囊一直画平台账号余额),\n * 要等五分钟那一次轮询才会纠正。会话一换就顺手问,才是对的时机。\n */\n bindSession: function (sessionId, cwd) {\n var previous = state.sessionId\n attributionSlice.bindSession(sessionId, cwd)\n if (state.sessionId === previous) return\n // 换会话 = 换账本。先把旧读数清掉,再**立刻**问一次新会话的模型来源:\n // - 不清:新会话会顶着上一个会话的站名和金额,直到新答案回来(那是别人的钱);\n // - 不问:要等 30 秒轮询才纠正(用户报过\"切了会话还是旧数\")。\n providerSlice.resetForSession()\n if (state.sessionId !== null) providerSlice.loadProviderBalance()\n },\n refresh: refresh,\n openPanel: openPanel,\n togglePanel: togglePanel,\n movePanel: movePanel,\n commitPanel: commitPanel,\n resetPanel: resetPanel,\n setAnchorTop: setAnchorTop,\n setDiffWidth: setDiffWidth,\n setCommitWidth: setCommitWidth,\n persistLayout: persist,\n openDiff: diffSlice.openDiff,\n loadImagePair: imageSlice.loadImagePair,\n cachedImagePair: imageSlice.cachedImagePair,\n loadSessionChanges: attributionSlice.loadSessionChanges,\n loadSessionTime: sessionInfoSlice.loadSessionTime,\n loadPromptHistory: sessionInfoSlice.loadPromptHistory,\n loadBalance: accountSlice.loadBalance,\n resetForSession: providerSlice.resetForSession,\n loadProviderBalance: providerSlice.loadProviderBalance,\n openTokenPanel: providerSlice.openTokenPanel,\n closeTokenPanel: providerSlice.closeTokenPanel,\n saveProviderToken: providerSlice.saveProviderToken,\n showTip: tipSlice.showTip,\n hideTip: tipSlice.hideTip,\n tipHandlers: tipSlice.tipHandlers,\n closeDiff: diffSlice.closeDiff,\n closeDetail: closeDetail,\n setDiffSide: diffSlice.setDiffSide,\n toggleDiffWrap: diffSlice.toggleDiffWrap,\n setZoom: setZoom,\n openCommit: commitsSlice.openCommit,\n openCommitDiff: commitsSlice.openCommitDiff,\n toggleCommit: commitsSlice.toggleCommit,\n closeCommit: commitsSlice.closeCommit,\n stepFile: diffSlice.stepFile,\n openCursor: diffSlice.openCursor,\n stepHunk: diffSlice.stepHunk,\n toggleHunk: diffSlice.toggleHunk,\n toggleOnlySession: toggleOnlySession,\n toggleOnlyLatest: toggleOnlyLatest,\n refreshRows: toolsSlice.refreshRows,\n loadTools: toolsSlice.loadTools,\n toggleReviewed: attributionSlice.toggleReviewed,\n clearReviewed: attributionSlice.clearReviewed,\n }\n}\n","/**\n * 样式落地:把 `styles.ts` 那份表插进 `<head>`(用 `CSS_TAG_ID` 防重复注入),\n * 以及从已注入的样式里取一个值的助手。\n */\n\nimport { CSS } from './styles.js'\nimport { CSS_TAG_ID } from './shared.js'\n/**\n * 审阅进度:`{ marks: {file → 'fresh'|'stale'}, done, total, staleCount }`。\n *\n * 判据是**签名**:宿主 stat 出来的 size+mtimeMs 和存档里的是同一个才算\"这一版审过了\"。\n * 签名变了(又被改动)就把那条标记降级成 `stale`,界面显示\"改动已变,需要重看\" ——\n * 而不是继续冒充已审。没有签名的文件(宿主这次没算)不参与统计。\n */\n\n/**\n * 从自有样式表里抠出计数芯片的横向间隔。\n *\n * 用来回答\"页面上跑着的到底是哪一版 CSS\"—— 改完样式没生效时,这一眼就能分清是\n * \"bundle 还是旧的\"还是\"样式规则没生效\"。\n */\nexport function cssChipsGap(css: string): string | null {\n var match = /\\.dshh-chips\\{[^}]*gap:([^;}]+)/.exec(css)\n return match === null ? null : match[1].trim()\n}\n\n/* ---------------------------------------------------------------- 样式 */\n\n/* ---------------------------------------------------------------- 样式注入 */\n\n/**\n * 幂等注入自有样式表,并**自己打上 `data-plugin`**。\n *\n * 框架的 client-modules 会在每次模块物化后 `claimStyles(id)`:把当时所有\n * **没有 `data-plugin`** 的 `<style>` 认领给刚物化的那个模块,然后在 HMR 的\n * replace / invalidate / prune 路径上 `removeOwnedStyles(id)` 按 `data-plugin`\n * 删掉。不打标的表会被别的模块领走,在那次 rebuild 里连同别人的表一起删掉 ——\n * 而我们的注入只在插件激活时跑一次,于是页面永久退回骨架(原生 `button`、\n * 没有间隙 / 省略号 / 居中几何)。\n *\n * 框架自己的包与同门的 dsh-git-graph 都按这个约定打标;tag 存在时复用同一节点\n * (HMR 会先把旧表删掉,再由新的 fiber 重新调用本函数)。\n *\n * @returns 该样式节点;没有 `document` 时返回 `null`。\n */\nexport function ensureStyles(): HTMLStyleElement | null {\n if (typeof document === 'undefined') return null\n var selector = 'style[data-plugin-css=\"' + CSS_TAG_ID + '\"]'\n // 选择器只可能命中我们自己注入的 <style>(tag id 是插件私有的):这里断言成 HTMLStyleElement。\n var tag = (\n typeof document.querySelector === 'function' ? document.querySelector(selector) : null\n ) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n // 值必须等于客户端模块 id(= 包名):客户端的 `removeOwnedStyles(id)` 靠它回收样式。\n tag.setAttribute('data-plugin', '@konnga/dsh-hud')\n tag.setAttribute('data-plugin-css', CSS_TAG_ID)\n tag.setAttribute('data-dsh-hud', 'styles')\n document.head.appendChild(tag)\n }\n tag.textContent = CSS\n return tag\n}\n","/**\n * dsh-hud — 浏览器半(`dsh.client` bundle)的**插件面**。\n *\n * 两个座位,一个共享 store:\n *\n * - `conversation.input.dock` → 胶囊:输入框上方那一行里靠左的紧凑状态胶囊\n * (分支摘要,或\"非 git 仓库\"),点击开关面板。这个座位是**会话作用域**,\n * 框架直接把当前 `sessionId` 交给它,它再从 `useSessions` 快照里取该会话的\n * `cwd` 写进共享 store —— 这是 root 作用域的面板**读不到**的东西。\n * - `shell.overlay` → 悬浮面板:收起来时渲染 `null`(不留会挡住输入框的气泡),\n * 展开时是悬浮面板,默认停在输入框上方。\n *\n * 所有 git 工作都在宿主半(`/dsh-hud-rpc`);这里只负责座位注册、轮询节拍、\n * 语言字典与样式注入 —— 具体行为都在 store / hooks / components 里。\n *\n * ⚠️ `lib/client.js` 是从 `src/client/**` **打包出来的产物**(`npm run build`):浏览器半只支持\n * \"一个经典脚本\",所以源码分模块、产物是单文件(见 `rolldown.config.mjs`)。别直接改产物。\n */\n\nimport { CSS } from './styles.js'\nimport { HudDockPill } from './components/dock-pill.js'\nimport { HudFloatingPanel } from './components/floating-panel.js'\nimport { PillTip } from './components/pill-tip.js'\nimport { TokenPanel } from './components/token-panel.js'\nimport { SessionMenuSummary, SessionRowHoverChip } from './components/sidebar.js'\nimport { TurnImages } from './components/turn-images.js'\nimport { bindPromptHistory } from './composer.js'\nimport { en, zh } from './i18n.js'\nimport {\n BALANCE_POLL_MS,\n NS,\n POLL_MS,\n PROVIDER_POLL_MS,\n PROVIDER_RETRY_TICK_MS,\n ROWS_POLL_MS,\n TOOLS_POLL_MS,\n probe,\n} from './shared.js'\nimport { watchModelSelection } from './model-watch.js'\nimport { createStore } from './store/index.js'\nimport type { StoreContext } from './store/types.js'\nimport { cssChipsGap, ensureStyles } from './styles-apply.js'\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\n\nexport var inject = ['slots', 'connection', 'locale']\n\n/* --------------------------------------------------------- 框架注入面 */\n\n/**\n * 客户端插件上下文里用到的部分(框架给的 `ctx`)。\n *\n * 座位定义在注册时才拿到 `sessionId`(会话作用域座位有、root 作用域没有),\n * 所以这里把 `inject` 的形参声明出来,让回调里的 `sessionId` 有类型可依。\n */\ninterface HudSlotDefinition {\n name: string\n id: string\n order?: number\n locale?: string\n inject?: (sessionId?: string) => Record<string, unknown>\n}\n\ninterface HudSlots {\n inject(slot: string, callback: () => unknown): unknown\n register(definition: HudSlotDefinition, component: unknown): unknown\n}\n\ninterface HudLocale {\n register(\n namespace: string,\n dictionaries: { zh: Record<string, string>; en: Record<string, string> },\n ): (() => void) | void\n}\n\ninterface HudPluginContext extends StoreContext {\n slots: HudSlots\n locale: HudLocale\n /** 按名取其它客户端服务;宿主没装那个半边时返回 `undefined`(`sessions` 就是软依赖)。 */\n get: (name: string) => unknown\n}\n\n/* ---------------------------------------------------------------- 插件 */\n\n/**\n * 客户端插件主体。\n * @param ctx - 客户端根上下文(slots、connection、locale)。\n */\nexport function apply(ctx: HudPluginContext): void {\n ctx.effect(function () {\n return ctx.locale.register(NS, { zh: zh, en: en })\n }, 'dsh-hud: dictionaries')\n\n ctx.effect(function () {\n var element = ensureStyles()\n return function () {\n if (element && element.parentNode) element.parentNode.removeChild(element)\n }\n }, 'dsh-hud: styles')\n\n var store = createStore(ctx)\n\n // 起手留一行:诊断里**有没有这一行**就足以区分\"客户端半压根没加载\"与\"加载了但\n // 哪一环没接上\"(这一行由 apply 发,与后面的投影/名单无关)。\n probe.log('apply', {\n inject: typeof ctx.inject === 'function',\n slots: typeof ctx.slots === 'object',\n // 跑着的那一版自有样式表里,计数芯片的间隔是多少:改完样式没生效时用它分辨\n // \"bundle 还是旧的\"还是\"规则没生效\"。\n chips: cssChipsGap(CSS),\n })\n\n // 面板与胶囊都要能反映磁盘上的实时变化;页面不可见时不打请求。\n // 顺手补一次样式表:框架的 HMR 记账会把插件的 `<style>` 删掉,删掉就自愈。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n ensureStyles()\n store.refresh()\n }, POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: status poll')\n\n // 从别处回到页面时立刻补一次(定时器在隐藏期间被跳过)。\n ctx.effect(function () {\n var onFocus = function () {\n ensureStyles()\n store.refresh()\n }\n window.addEventListener('focus', onFocus)\n return function () {\n window.removeEventListener('focus', onFocus)\n }\n }, 'dsh-hud: focus refresh')\n\n // 余额(账号那一路 + 当前模型来源那一路):**最慢的一条节拍**(默认 5 分钟)。\n //\n // 它背后是宿主替我们去 DeepSeek 平台查账(一次真实网络请求),而余额按分钟变;\n // 页面隐藏时照例跳过。点击胶囊会带 `force` 立刻重取一次(见 `balance-pill.ts`)。\n // 这一条起手也跑一次:胶囊要在首屏就出现,而不是等五分钟。\n ctx.effect(function () {\n store.loadBalance()\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.loadBalance()\n }, BALANCE_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: account balance poll')\n\n /**\n * 「当前会话模型来源」的余额:自己的节拍,比账号那一路快。\n *\n * 为什么是 30 秒而不是 5 分钟:第一次取数常常赶在投影就绪之前(刚开会话、刚换模型),\n * 那时宿主答不上来;如果下一次机会在五分钟之后,胶囊就有五分钟是空白的。\n * 真实请求次数由 store 自己的 TTL 兜着(成功 60s、失败 30s),这里只是\"多久问一次\"。\n */\n ctx.effect(function () {\n var stopped = false\n var handle: number | null = null\n\n /**\n * 下一次隔多久再问。\n *\n * 稳态是 `PROVIDER_POLL_MS`(30 秒);**还没问出来**的时候用 1 秒重问 ——\n * 这才是\"重启后胶囊迟迟不出现\"的症结:宿主在刚启动那一下常常还读不到模型选择投影,\n * 第一次问必然落空,而原来的固定 30 秒节拍意味着胶囊要空半分钟。\n * 真实请求次数仍由 store 的 TTL 兜着(成功 60s、失败按 1s 起指数退避到 30s),\n * 这里只是\"多久看一眼\",看的时候多半是空转。\n */\n function nextDelay(): number {\n var ledger = store.getSnapshot().provider\n var settled =\n ledger !== undefined &&\n ledger !== null &&\n ledger.mode !== 'unknown' &&\n typeof ledger.provider === 'string' &&\n ledger.provider !== ''\n return settled ? PROVIDER_POLL_MS : PROVIDER_RETRY_TICK_MS\n }\n\n function tick(): void {\n if (stopped) return\n if (document.visibilityState !== 'hidden') store.loadProviderBalance()\n // 用 `window.setTimeout`(而不是裸 `setTimeout`):测试 harness 只替换 `window.*`,\n // 裸调用会落到真的 Node 定时器上 —— 表现就是\"用例全过、进程永不退出\"。\n handle = window.setTimeout(tick, nextDelay())\n }\n\n tick()\n return function () {\n stopped = true\n if (handle !== null) window.clearTimeout(handle)\n }\n }, 'dsh-hud: provider balance poll')\n\n // 换模型 = 换账本:TTL 按时钟算、不按事实算,所以模型一变就得主动重取一次\n // (否则切到 `ur` 之后会先盯着平台账号的 `¥8.60` 看满一分钟)。\n ctx.effect(function () {\n return watchModelSelection(ctx, store)\n }, 'dsh-hud: model selection -> provider balance')\n\n // 悬停气泡单独一枚 overlay 座位:它要浮在 dock 行之外(行内会被裁切/堆叠上下文限制)。\n // order 比面板小 —— 面板开着时仍然压在最上层。\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-pill-tip',\n order: 10,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n PillTip,\n )\n })\n\n // 后台令牌设置面板:与气泡同一层(都是浮在 dock 之上、面板之下)。\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-token-panel',\n order: 15,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n TokenPanel,\n )\n })\n\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-panel',\n order: 20,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n HudFloatingPanel,\n )\n })\n\n // 侧栏行内 chip 的数据:起手取一次,之后按自己的节奏更新(页面隐藏时跳过)。\n ctx.effect(function () {\n store.refreshRows()\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.refreshRows()\n }, ROWS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: session rows poll')\n\n // 工具活动单独一条节拍(3s;宿主是内存读,密一点才看得到\"正在跑\")。\n //\n // 走 `store.loadTools()`:这里**曾经是裸 `loadTools()`**,而那个函数只存在于 store 内部、\n // 也没暴露出来 —— 于是这条节拍每 3 秒抛一次 ReferenceError(被 interval 吞掉,只在控制台\n // 留痕),工具读数实际靠 `refresh()` 的 8 秒节拍在维持。已暴露并改为经 store 调用。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.loadTools()\n }, TOOLS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: tool activity poll')\n\n // `⋯` 菜单里的一条只读摘要:行尾拿不到常亮座位,这里是最实在的\"完整信息\"落点。\n ctx.slots.inject('sidebar.workspaces.session.menu.item', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.workspaces.session.menu.item',\n id: 'dsh-hud-session-menu',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionMenuSummary,\n )\n })\n\n // 悬停卡片里那块详情(宽敞的地方;动作条那枚放不下的东西都在这儿)。\n ctx.slots.inject('sidebar.session.row.hover', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.session.row.hover',\n id: 'dsh-hud-session-hover',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionRowHoverChip,\n )\n })\n\n ctx.slots.inject('conversation.input.dock', function () {\n return ctx.slots.register(\n {\n name: 'conversation.input.dock',\n id: 'dsh-hud-pill',\n order: 20,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n HudDockPill,\n )\n })\n\n // 轮尾(内置「已编辑 N 个文件」卡片所在的那条尾巴)的一扇门:本轮动过图,\n // 点缩略图就在内置文档预览里打开;`⇄` 才进本面板看前后对比。\n ctx.slots.inject('conversation.chat.turnTail', function () {\n return ctx.slots.register(\n {\n name: 'conversation.chat.turnTail',\n id: 'dsh-hud-turn-images',\n order: 30,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n TurnImages,\n )\n })\n\n // 输入框里的 ↑ 召回(纯文本):**最后接线** —— 逻辑全在纯函数里,这一层只负责\n // 读值 / 写值 / 摆光标,且**真接管了才拦默认行为**。刻意排在最后注册,好让\n // \"第一个 keydown 监听是面板那条\"这个既有事实保持不变。\n ctx.effect(function () {\n return bindPromptHistory(store)\n }, 'dsh-hud: prompt history (↑ 召回)')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAa,MAAM;CAIjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;CAOA;CAEA;CAEA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAEA;CAEA;CAEA;CACA;CAEA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAKA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;C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"}