@yangzhe1991/dsh-code-review 0.1.2 → 0.1.3

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 CHANGED
@@ -612,6 +612,8 @@ main { margin-left: 220px; padding: 16px 24px 60px; }
612
612
  min-width: 0;
613
613
  white-space: pre-wrap;
614
614
  word-break: break-word;
615
+ /* \u5236\u8868\u7B26\u6309 4 \u4E2A\u5B57\u7B26\u5BBD\u5EA6\u6E32\u67D3(\u6D4F\u89C8\u5668\u9ED8\u8BA4 8,\u4EE3\u7801\u9605\u8BFB\u592A\u677E) */
616
+ tab-size: 4;
615
617
  }
616
618
  .dsh-cr-del .dsh-cr-num-old, .dsh-cr-del .dsh-cr-cell-old,
617
619
  .dsh-cr-change .dsh-cr-num-old, .dsh-cr-change .dsh-cr-cell-old {
@@ -719,7 +721,7 @@ main { margin-left: 220px; padding: 16px 24px 60px; }
719
721
  .submit-hint { font-size: 12px; color: var(--dsw-alias-label-secondary); }
720
722
  .rawwrap { margin: 16px 24px; }
721
723
  .rawsummary { font-size: 12px; color: var(--dsw-alias-label-secondary); cursor: pointer; }
722
- .rawpre { white-space: pre-wrap; word-break: break-word; font: inherit; margin: 8px 0 0; }
724
+ .rawpre { white-space: pre-wrap; word-break: break-word; font: inherit; margin: 8px 0 0; tab-size: 4; }
723
725
  `;
724
726
  var STANDALONE_STATUS_LABEL = {
725
727
  modified: "\u4FEE\u6539",
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client/index.tsx", "../src/client/diff-parse.ts", "../src/client/diff-highlight.ts", "../src/client/diff-view.ts"],
4
- "sourcesContent": ["/**\n * @yangzhe1991/dsh-code-review \u63D2\u4EF6,\u6D4F\u89C8\u5668\u534A \u2014\u2014 Git Diff \u72EC\u7ACB\u5BA1\u67E5\u6807\u7B7E\u9875\u3002\n *\n * \u4EA4\u4E92:\u804A\u5929\u7A97\u53E3\u5185\u4FDD\u6301\u5B98\u65B9\u539F\u59CB\u663E\u793A(\u4E0D\u5C55\u5F00\u3001\u4E0D\u8F6C\u6362),\u68C0\u6D4B\u5230 git diff\n * \u8F93\u51FA\u7684\u5757\u4E0A\u81EA\u52A8\u52A0\u4E00\u4E2A\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE;\u70B9\u51FB\u540E\u5728\u72EC\u7ACB\u6D4F\u89C8\u5668\u6807\u7B7E\n * \u9875\u6E32\u67D3\u5B8C\u6574\u53CC\u5217 Code Review \u89C6\u56FE(\u89C1 diff-view.ts)\u3002\u5F39\u7A97\u5FC5\u987B\u7531\u7528\u6237\n * \u624B\u52BF\u89E6\u53D1,\u6240\u4EE5\u6807\u7B7E\u9875\u53EA\u80FD\u505A\u6210\u6309\u94AE\u5F62\u6001\u3002\n *\n * \u4E24\u4E2A\u6309\u94AE\u6CE8\u5165\u6765\u6E90(\u4E92\u8865,\u8986\u76D6 diff \u51FA\u73B0\u7684\u4E24\u4E2A\u9762):\n * 1. DOM \u63A2\u6D4B:markdown \u4EE3\u7801\u5757(.md-code-block,```diff/```patch \u6216\u5185\u5BB9\n * \u5F62\u4F3C diff)\u4E0E\u8BE6\u60C5\u9762\u677F\u91CC\u7684\u7EC8\u7AEF\u5361\u7247([data-terminal])\u3002\u6D41\u5F0F\u671F\u95F4\u5185\u5BB9\n * \u6301\u7EED\u53D8\u5316,\u53BB\u6296\u7A33\u5B9A\u540E\u624D\u52A0\u6309\u94AE;\u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff \u65F6\u79FB\u9664\u6309\u94AE\u3002\n * 2. \u6570\u636E\u5C42:bash \u5DE5\u5177\u7ED3\u679C\u8F93\u51FA\u5F62\u4F3C diff \u65F6,\u6298\u53E0\u7684\u5DE5\u5177\u884C\u5728 DOM \u91CC\u6CA1\u6709\n * \u8F93\u51FA\u5185\u5BB9(\u968F\u5C55\u5F00\u624D\u6302\u8F7D),\u7EAF DOM \u63A2\u6D4B\u770B\u4E0D\u5230 \u2014\u2014 \u7531 session \u4F5C\u7528\u57DF\n * \u7EC4\u4EF6\u8BA2\u9605 chat \u5FEB\u7167,\u628A\u7ED3\u679C\u6587\u672C\u6309\u8282\u70B9 key \u5B58\u8868,\u626B\u63CF\u5668\u7ED9\u5BF9\u5E94\u7684\n * \u5DE5\u5177\u884C\u672C\u4F53([data-sample=\"bash\"])\u52A0\u6309\u94AE\u3002\u5DE5\u5177\u884C\u5185\u7684\u7EC8\u7AEF\u5361\u7247\u63A2\u6D4B\n * \u4F1A\u8DF3\u8FC7(\u907F\u514D\u540C\u4E00\u5757\u51FA\u73B0\u4E24\u4E2A\u6309\u94AE)\u3002\n */\nimport { useEffect } from 'react'\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// \u89E6\u53D1 SlotMap \u58F0\u660E\u5408\u5E76:shell.overlay \u7531 layout\u3001header.actions \u7531 conversation \u58F0\u660E\u3002\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// \u58F0\u660E\u5408\u5E76:\u5B98\u65B9 locale \u670D\u52A1\u6302\u8F7D\u5728 cordis Context \u4E0A(\u7531 dsh-client-locale\n// \u63D0\u4F9B;\u7C7B\u578B\u4E3A unknown,\u8FD0\u884C\u65F6\u505A\u5F62\u72B6\u6821\u9A8C \u2014\u2014 \u4E0D\u76F4\u63A5\u4F9D\u8D56\u8BE5\u5305\u7684\u7C7B\u578B)\u3002\nimport type {} from '@deepseek-ai/cordis'\nimport { isDiffText, parseGitDiff } from './diff-parse'\nimport { buildCommentReport, buildStandaloneHtml, CR_TEXTS } from './diff-view'\nimport type { Lang, ReviewComment } from './diff-view'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /** \u5B98\u65B9 locale \u670D\u52A1(\u53EF\u9009:\u5B98\u65B9\u88C5\u914D\u4E0D\u542B locale \u65F6\u4E3A undefined)\u3002 */\n locale?: unknown\n }\n}\n\n// \u2014\u2014 \u53EF\u8C03\u53C2\u6570 \u2014\u2014\n\n/** \u7A33\u5B9A\u53BB\u6296\u7A97\u53E3:\u5185\u5BB9\u5728\u8FD9\u4E2A\u7A97\u53E3\u5185\u4E0D\u518D\u53D8\u5316\u624D\u52A0\u6309\u94AE(\u6D41\u5F0F\u671F\u95F4\u4E0D\u65AD\u91CD\u7F6E)\u3002 */\nconst STABLE_MS = 1000\n/** \u5168\u91CF\u626B\u63CF\u53BB\u6296(MutationObserver \u89E6\u53D1\u9891\u7E41,\u5408\u5E76\u6210\u4E00\u6B21\u626B\u63CF)\u3002 */\nconst SCAN_DEBOUNCE = 300\n/** \u89E3\u6790\u5931\u8D25\u540E\u7684\u91CD\u8BD5\u95F4\u9694(\u7578\u5F62 diff \u5185\u5BB9\u7A33\u5B9A\u540E\u53EF\u80FD\u53D8\u5408\u6CD5)\u3002 */\nconst RETRY_MS = 3000\n/** \u515C\u5E95\u5168\u91CF\u626B\u63CF\u95F4\u9694:\u8986\u76D6\u4EFB\u4F55\u6F0F\u6389\u7684 mutation \u8DEF\u5F84(\u7406\u8BBA\u4E0D\u8BE5\u6709)\u3002 */\nconst SAFETY_SCAN_MS = 10000\n\n/** \u6CE8\u5165\u7684 <style> \u662F\u5426\u5DF2\u5B58\u5728,\u907F\u514D\u91CD\u590D\u6CE8\u5165\u3002 */\nlet styleInjected = false\n\n// \u2014\u2014 \u6309\u94AE\u6837\u5F0F(\u5B98\u65B9\u8BED\u4E49\u53D8\u91CF,\u6DF1\u6D45\u4E3B\u9898\u81EA\u52A8\u9002\u914D)\u2014\u2014\n\nconst BUTTON_CSS = `\n.dsh-cr-openbtn {\n margin-left: auto;\n flex: none;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n background: transparent;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 1px 10px;\n cursor: pointer;\n white-space: nowrap;\n}\n.dsh-cr-openbtn:hover {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-border-l3);\n}\n`\n\n/** \u5168\u5C40\u6CE8\u5165\u4E00\u6B21\u6309\u94AE\u6837\u5F0F(\u6D4F\u89C8\u5668\u7AEF bundle \u7684\u6A21\u5757\u7EA7\u526F\u4F5C\u7528)\u3002 */\nfunction ensureStyle(): void {\n if (styleInjected || typeof document === 'undefined') return\n styleInjected = true\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-code-review'\n tag.textContent = BUTTON_CSS\n document.head.appendChild(tag)\n}\n\n// \u2014\u2014 \u72EC\u7ACB\u6807\u7B7E\u9875\u6253\u5F00 \u2014\u2014\n\n/** \u72EC\u7ACB\u6807\u7B7E\u9875\u9700\u8981\u7EE7\u627F\u7684 DSW \u4E3B\u9898\u53D8\u91CF(\u53D6\u4E0D\u5230\u65F6\u7ED9\u6D45\u8272\u8FD1\u4F3C\u503C\u515C\u5E95)\u3002 */\nconst THEME_VAR_DEFAULTS: Record<string, string> = {\n '--dsw-alias-bg-base': '#ffffff',\n '--dsw-alias-bg-layer-1': '#f7f7f8',\n '--dsw-alias-markdown-code-block': '#f4f4f5',\n '--dsw-alias-label-primary': '#1a1a1a',\n '--dsw-alias-label-secondary': '#555555',\n '--dsw-alias-label-tertiary': '#888888',\n '--dsw-alias-border-l2': '#e5e5e7',\n '--dsw-alias-border-l3': '#cfcfd2',\n '--dsw-alias-state-error-primary': '#d92d20',\n '--dsw-alias-state-success-primary': '#12b76a',\n '--dsw-alias-state-business-primary': '#3b6fe0',\n '--ds-font-family-code': 'ui-monospace, SFMono-Regular, Menlo, monospace',\n // \u8BED\u6CD5\u9AD8\u4EAE token \u989C\u8272:\u5B98\u65B9 shiki \u4E3B\u9898\u53D8\u91CF(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4)\u3002\n '--shiki-token-keyword': '#7c3aed',\n '--shiki-token-string': '#16a34a',\n '--shiki-token-comment': '#9aa0a6',\n '--shiki-token-constant': '#d97706',\n '--shiki-token-function': '#2563eb',\n}\n\n/** \u4ECE\u5BBF\u4E3B\u9875\u9762\u6536\u96C6\u4E3B\u9898\u53D8\u91CF\u7684\u5B9E\u9645\u503C(\u72EC\u7ACB\u6807\u7B7E\u9875\u4E0E DSH \u6DF1\u6D45\u8272\u4FDD\u6301\u4E00\u81F4)\u3002 */\nfunction collectThemeVars(): Record<string, string> {\n const out: Record<string, string> = {}\n const styles = getComputedStyle(document.body)\n for (const name of Object.keys(THEME_VAR_DEFAULTS)) {\n const value = styles.getPropertyValue(name).trim()\n out[name] = value !== '' ? value : THEME_VAR_DEFAULTS[name]\n }\n return out\n}\n\n/**\n * \u6253\u5F00\u72EC\u7ACB\u5BA1\u67E5\u6807\u7B7E\u9875\u3002\u5FC5\u987B\u5728\u7528\u6237\u70B9\u51FB\u624B\u52BF\u5185\u540C\u6B65\u6267\u884C(Blob URL \u751F\u6210 +\n * window.open \u5168\u540C\u6B65),\u5426\u5219\u4F1A\u88AB\u6D4F\u89C8\u5668\u5F39\u7A97\u62E6\u622A\u3002URL \u5EF6\u8FDF\u91CA\u653E\u3002\n * \u8FD4\u56DE\u7684\u5B50\u7A97\u53E3\u5F15\u7528\u767B\u8BB0\u8FDB reviewWindows,\u8BC4\u8BBA\u56DE\u4F20\u65F6\u6821\u9A8C\u6D88\u606F\u6765\u6E90\u3002\n */\n/** \u5B98\u65B9 locale \u670D\u52A1\u7684\u6700\u5C0F\u5F62\u72B6(\u8FD0\u884C\u65F6\u5F62\u72B6\u6821\u9A8C,\u4E0D\u4F9D\u8D56\u5B98\u65B9\u5305\u7C7B\u578B)\u3002 */\ninterface LocaleService {\n getLocale(): { active: string }\n}\n\n/** \u5B98\u65B9 locale \u670D\u52A1(apply \u65F6\u63A5\u7BA1;\u5B98\u65B9\u88C5\u914D\u4E0D\u542B locale \u65F6\u7559 null)\u3002 */\nlet localeService: LocaleService | null = null\n\n/**\n * \u68C0\u6D4B DSH \u754C\u9762\u8BED\u8A00:\u4F18\u5148\u5B98\u65B9 locale \u670D\u52A1(\u7528\u6237\u8BBE\u7F6E > \u6D4F\u89C8\u5668\u8BED\u8A00),\n * \u62FF\u4E0D\u5230\u65F6\u9000\u56DE\u6D4F\u89C8\u5668\u8BED\u8A00(navigator),\u975E zh \u4E00\u5F8B\u6309\u82F1\u6587\u3002\n * \u6BCF\u6B21\u73B0\u8BFB \u2014\u2014 \u7528\u6237\u5728\u8BBE\u7F6E\u91CC\u5207\u6362\u8BED\u8A00\u540E,\u4E0B\u4E00\u6B21\u751F\u6210\u6309\u94AE/\u6807\u7B7E\u9875\u5373\u751F\u6548,\n * \u65E0\u9700\u8BA2\u9605 locale/change \u4E8B\u4EF6\u3002\n */\nfunction detectLang(): Lang {\n if (localeService !== null) {\n return localeService.getLocale().active === 'en' ? 'en' : 'zh'\n }\n const nav = navigator.languages?.[0] ?? navigator.language ?? ''\n return nav.toLowerCase().startsWith('zh') ? 'zh' : 'en'\n}\n\nfunction openReviewTab(text: string): void {\n const files = parseGitDiff(text)\n if (files.length === 0) return\n const html = buildStandaloneHtml(files, collectThemeVars(), text, detectLang())\n const url = URL.createObjectURL(new Blob([html], { type: 'text/html' }))\n // \u987A\u5E26\u6E05\u6389\u5DF2\u5173\u95ED\u7684\u65E7\u5B50\u7A97\u53E3\u5F15\u7528\u3002\n for (const win of reviewWindows) {\n if (win === null || win.closed) reviewWindows.delete(win)\n }\n const win = window.open(url, '_blank')\n if (win !== null) reviewWindows.add(win)\n window.setTimeout(() => URL.revokeObjectURL(url), 60000)\n}\n\n// \u2014\u2014 \u8BC4\u8BBA\u56DE\u4F20(\u72EC\u7ACB\u6807\u7B7E\u9875 \u2192 DSH \u5BF9\u8BDD\u6846)\u2014\u2014\n\n/** \u672C\u63D2\u4EF6\u6253\u5F00\u7684\u5BA1\u67E5\u5B50\u7A97\u53E3(\u6D88\u606F\u6765\u6E90\u6821\u9A8C\u7528)\u3002 */\nconst reviewWindows = new Set<Window | null>()\n\n/** \u5F53\u524D\u4F1A\u8BDD\u7684 composer \u5199\u5165\u5668(header.actions \u7EC4\u4EF6\u6302\u8F7D\u65F6\u6CE8\u518C)\u3002 */\nlet composerWriter: ((text: string) => void) | null = null\n\n/** \u6821\u9A8C\u56DE\u4F20\u8BC4\u8BBA\u7684\u5F62\u72B6,\u8FC7\u6EE4\u975E\u6CD5\u6761\u76EE(\u8DE8\u7A97\u53E3\u6570\u636E\u4E0D\u53EF\u4FE1)\u3002 */\nfunction sanitizeComments(raw: unknown): ReviewComment[] {\n if (!Array.isArray(raw)) return []\n const out: ReviewComment[] = []\n for (const item of raw) {\n if (typeof item !== 'object' || item === null) continue\n const c = item as { path?: unknown; lineNo?: unknown; text?: unknown }\n if (typeof c.path !== 'string' || typeof c.lineNo !== 'number' || typeof c.text !== 'string') continue\n if (c.text.trim() === '') continue\n out.push({ path: c.path, lineNo: Math.trunc(c.lineNo), text: c.text })\n }\n return out\n}\n\n/**\n * \u5904\u7406\u5BA1\u67E5\u9875\u56DE\u4F20\u7684\u8BC4\u8BBA:\u7EC4\u88C5\u300C\u6587\u4EF6\u540D:\u884C\u53F7 \u2014 \u8BC4\u8BBA\u300D\u62A5\u544A\u6587\u672C,\u5199\u5165\n * \u5F53\u524D\u4F1A\u8BDD\u7684 composer,\u5E76\u5411\u5B50\u7A97\u53E3\u56DE\u6267\u3002\u6D88\u606F\u6765\u6E90\u6821\u9A8C:\u5FC5\u987B\u662F\u672C\u63D2\u4EF6\n * \u6253\u5F00\u7684\u7A97\u53E3(\u4E0E origin \u76F8\u6BD4\u66F4\u53EF\u9760,blob \u5B50\u7A97\u53E3\u7684 origin \u8BED\u4E49\u6709\u5DEE\u5F02)\u3002\n */\nfunction handleReviewMessage(event: MessageEvent): void {\n const data = event.data\n if (typeof data !== 'object' || data === null) return\n if (data.type !== 'dsh-code-review-comments') return\n if (!reviewWindows.has(event.source as Window | null)) return\n const comments = sanitizeComments(data.comments)\n if (comments.length === 0) return\n const kind = data.kind === 'lgtm' ? 'lgtm' : 'comment'\n const report = buildCommentReport(comments, kind, detectLang())\n const source = event.source as Window\n if (composerWriter !== null) {\n composerWriter(report)\n source.postMessage({ type: 'dsh-code-review-ack', ok: true }, '*')\n } else {\n source.postMessage({ type: 'dsh-code-review-ack', ok: false }, '*')\n }\n}\n\n// \u2014\u2014 \u6309\u94AE\u6CE8\u5165(\u7EAF DOM,\u4E0D\u4F9D\u8D56 React)\u2014\u2014\n\n/**\n * \u7ED9\u5BBF\u4E3B\u5143\u7D20(\u4EE3\u7801\u5757 banner / \u7EC8\u7AEF\u5361\u7247 header / bash \u5DE5\u5177\u884C\u672C\u4F53)\u52A0\u6309\u94AE:\n * \u2460\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D;\u2461 \u4EE3\u7801\u5757\u573A\u666F\u518D\u52A0\u300C\u5C55\u5F00/\u6536\u8D77\u300D\u5F00\u5173(\u9ED8\u8BA4\u6298\u53E0\n * \u539F\u59CB diff \u6587\u672C,\u804A\u5929\u6D41\u4FDD\u6301\u7D27\u51D1)\u3002\u5DF2\u5B58\u5728\u7684\u6309\u94AE\u8DF3\u8FC7\u3002\n * @param host \u6309\u94AE\u6302\u8F7D\u70B9(React \u4E0D\u8FFD\u8E2A\u6211\u4EEC\u7684\u8282\u70B9,\u91CD\u6E32\u67D3\u4E00\u822C\u4E0D\u52A8\u5B83)\u3002\n * @param getText \u70B9\u51FB\u65F6\u83B7\u53D6 diff \u539F\u59CB\u6587\u672C(\u73B0\u8BFB,\u907F\u514D\u8FC7\u671F\u5F15\u7528)\u3002\n * @param fold \u4EE3\u7801\u5757\u6298\u53E0\u5F00\u5173(\u975E\u4EE3\u7801\u5757\u573A\u666F\u4F20 null)\u3002\n */\nfunction ensureButtons(\n host: HTMLElement,\n getText: () => string | null,\n fold: { folded: boolean; onToggle: () => void } | null,\n): void {\n const texts = CR_TEXTS[detectLang()]\n if (host.querySelector('[data-dsh-cr-btn]') === null) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'dsh-cr-openbtn'\n btn.dataset.dshCrBtn = ''\n btn.textContent = texts.openTab\n btn.title = texts.openTabTitle\n btn.addEventListener('click', (event) => {\n // bash \u5DE5\u5177\u884C\u672C\u4F53\u6574\u884C\u53EF\u70B9(\u5C55\u5F00),\u5FC5\u987B\u963B\u6B62\u4E8B\u4EF6\u4F20\u64AD\u5230\u5B98\u65B9\u70B9\u51FB\u3002\n event.stopPropagation()\n event.preventDefault()\n const text = getText()\n if (text !== null) openReviewTab(text)\n })\n host.appendChild(btn)\n }\n if (fold !== null && host.querySelector('[data-dsh-cr-fold]') === null) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'dsh-cr-openbtn'\n btn.dataset.dshCrFold = ''\n btn.textContent = fold.folded ? texts.expand : texts.collapse\n btn.title = fold.folded ? texts.expandTitle : texts.collapseTitle\n btn.addEventListener('click', (event) => {\n event.stopPropagation()\n event.preventDefault()\n fold.onToggle()\n })\n host.appendChild(btn)\n }\n}\n\n/** \u79FB\u9664\u5BBF\u4E3B\u4E0A\u7684\u6240\u6709\u6309\u94AE(\u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff / \u89E3\u6790\u5931\u8D25\u65F6)\u3002 */\nfunction removeButtons(host: HTMLElement): void {\n for (const btn of host.querySelectorAll('[data-dsh-cr-btn], [data-dsh-cr-fold]')) {\n btn.remove()\n }\n}\n\n// \u2014\u2014 \u5019\u9009\u5757\u63A2\u6D4B(DOM \u6765\u6E90)\u2014\u2014\n\n/** \u63A2\u6D4B\u7ED3\u679C:\u539F\u59CB\u5185\u5BB9 + \u662F\u5426\u503C\u5F97\u52A0\u6309\u94AE + \u662F\u5426\u8FD8\u5728\u6D41\u5F0F/\u8FD0\u884C\u3002 */\ninterface Probe {\n /** \u5B98\u65B9\u539F\u59CB\u5185\u5BB9\u6587\u672C(\u5185\u5BB9\u7B7E\u540D,\u7528\u4E8E\u5224\u65AD\u300C\u5185\u5BB9\u662F\u5426\u53D8\u5316\u300D)\u3002 */\n text: string\n /** \u662F\u5426\u503C\u5F97\u52A0\u6309\u94AE(\u8BED\u8A00\u6807\u7B7E\u662F diff/patch,\u6216\u6587\u672C\u5F62\u4F3C git diff)\u3002 */\n candidate: boolean\n /** \u7EC8\u7AEF\u5361\u7247\u4ECD\u5728\u8FD0\u884C(\u8F93\u51FA\u672A\u5B8C\u6210)\u3002 */\n running: boolean\n}\n\n/**\n * \u5B9A\u4F4D markdown \u4EE3\u7801\u5757\u7684\u5B98\u65B9\u539F\u59CB\u5185\u5BB9\u5BB9\u5668(body)\u3002\n *\n * \u4E0D\u80FD\u6309\u300C\u7B2C 2 \u4E2A\u5B50\u8282\u70B9\u300D\u53D6:React \u5728\u8BED\u6CD5\u9AD8\u4EAE\u5668\u5C31\u7EEA\u65F6\u4F1A\u628A body \u4ECE\n * `<pre>` \u6362\u6210\u9AD8\u4EAE `<div>`,\u800C\u65B0\u8282\u70B9\u4F1A append \u5230\u7236\u7EA7\u672B\u5C3E(React \u53EA\u8FFD\u8E2A\n * \u81EA\u5DF1\u7684\u5B50\u8282\u70B9,\u6211\u4EEC\u7684\u6309\u94AE\u662F\u672A\u8FFD\u8E2A\u8282\u70B9,\u4F1A\u88AB\u6324\u5230\u524D\u9762)\u3002\n * \u56E0\u6B64\u6309\u7ED3\u6784\u627E:\u8DF3\u8FC7\u6211\u4EEC\u7684\u8282\u70B9(data-dsh-cr-btn),\u7B2C\u4E00\u4E2A PRE \u5373 body;\n * \u6CA1\u6709 PRE \u65F6,\u7B2C\u4E00\u4E2A\u300C\u975E banner\u300D\u7684 DIV \u5373\u9AD8\u4EAE body\u3002\n */\nfunction findCodeBlockBody(el: HTMLElement): HTMLElement | null {\n const first = el.firstElementChild\n for (const child of el.children) {\n if (!(child instanceof HTMLElement)) continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (child.tagName === 'PRE') return child\n }\n for (const child of el.children) {\n if (!(child instanceof HTMLElement)) continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (child === first) continue // banner\n if (child.tagName === 'DIV') return child\n }\n return null\n}\n\n/** \u63A2\u6D4B markdown \u4EE3\u7801\u5757:\u7ED3\u6784\u4E3A [banner(\u8BED\u8A00\u6807\u7B7E+\u590D\u5236\u6309\u94AE), body]\u3002 */\nfunction probeCodeBlock(el: HTMLElement): Probe | null {\n const banner = el.firstElementChild\n const body = findCodeBlockBody(el)\n if (!(banner instanceof HTMLElement) || body === null) return null\n const lang = (banner.firstElementChild?.textContent ?? '').trim().toLowerCase()\n const text = body.textContent ?? ''\n const candidate = lang === 'diff' || lang === 'patch' || isDiffText(text)\n return { text, candidate, running: false }\n}\n\n/**\n * \u5728\u7EC8\u7AEF\u5361\u7247\u91CC\u627E\u8F93\u51FA\u5BB9\u5668:\u8DF3\u8FC7\u7B2C 0 \u4E2A\u5B50\u8282\u70B9(\u5934\u90E8:\u547D\u4EE4/\u72B6\u6001/\u590D\u5236\u6309\u94AE),\n * \u7B2C\u4E00\u4E2A\u5185\u5BB9\u5F62\u4F3C git diff \u7684 div \u5373\u8F93\u51FA\u5BB9\u5668(\u7A7A\u6001/\u5934\u90E8\u90FD\u4E0D\u4F1A\u5F62\u4F3C diff,\n * \u5929\u7136\u6392\u9664);\u6211\u4EEC\u7684\u6309\u94AE\u5E26 data-dsh-cr-btn,\u4E00\u5E76\u8DF3\u8FC7\u3002\n */\nfunction findOutputContainer(wrapper: HTMLElement): HTMLElement | null {\n const children = wrapper.children\n for (let i = 1; i < children.length; i += 1) {\n const child = children[i]\n if (!(child instanceof HTMLElement) || child.tagName !== 'DIV') continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (isDiffText(child.textContent ?? '')) return child\n }\n return null\n}\n\n/**\n * \u8F93\u51FA\u5BB9\u5668\u9010\u884C\u53D6\u6587\u672C:\u6BCF\u884C\u662F div.line(ANSI \u5DF2\u6E32\u67D3\u6210 span,textContent \u5373\n * \u51C0\u5316\u540E\u7684\u7EAF\u6587\u672C);\u6298\u53E0\u6309\u94AE\u7B49\u975E\u884C\u5143\u7D20\u8DF3\u8FC7\u3002\n */\nfunction joinOutputLines(container: HTMLElement): string {\n const parts: string[] = []\n for (const child of container.children) {\n if (child instanceof HTMLElement && child.tagName === 'DIV') {\n parts.push(child.textContent ?? '')\n }\n }\n return parts.join('\\n')\n}\n\n/** \u63A2\u6D4B\u7EC8\u7AEF\u5361\u7247:\u8FD0\u884C\u4E2D\u76F4\u63A5\u6807\u8BB0 running,\u7531\u8C03\u7528\u65B9\u8DF3\u8FC7\u3002 */\nfunction probeTerminal(el: HTMLElement): Probe | null {\n if (el.hasAttribute('data-running')) {\n return { text: '', candidate: false, running: true }\n }\n const output = findOutputContainer(el)\n if (output === null) return null\n const text = joinOutputLines(output)\n return { text, candidate: isDiffText(text), running: false }\n}\n\n/** \u6309\u5143\u7D20\u7C7B\u578B\u5206\u53D1\u63A2\u6D4B\u3002 */\nfunction probeFor(el: HTMLElement): Probe | null {\n if (el.classList.contains('md-code-block')) return probeCodeBlock(el)\n if (el.hasAttribute('data-terminal')) {\n // \u5DE5\u5177\u884C\u5185\u7684\u7EC8\u7AEF\u5361\u7247:\u6309\u94AE\u52A0\u5728\u884C\u672C\u4F53\u4E0A(\u7531\u6570\u636E\u5C42\u6CE8\u5165,\u6298\u53E0\u65F6\u4E5F\n // \u53EF\u89C1),\u8FD9\u91CC\u8DF3\u8FC7,\u907F\u514D\u540C\u4E00\u5757\u51FA\u73B0\u4E24\u4E2A\u6309\u94AE\u3002\n if (el.closest('[data-sample=\"bash\"]') !== null) return null\n return probeTerminal(el)\n }\n return null\n}\n\n/** \u6309\u94AE\u6302\u8F7D\u70B9:\u4EE3\u7801\u5757 = banner,\u7EC8\u7AEF\u5361\u7247 = header(\u90FD\u662F\u5B98\u65B9\u7B2C\u4E00\u4E2A\u5B50\u8282\u70B9)\u3002 */\nfunction buttonHostFor(el: HTMLElement): HTMLElement | null {\n const first = el.firstElementChild\n return first instanceof HTMLElement ? first : null\n}\n\n// \u2014\u2014 \u6570\u636E\u5C42(\u6298\u53E0\u7684 bash \u5DE5\u5177\u884C)\u2014\u2014\n\n/**\n * \u6570\u636E\u5C42\u6587\u672C\u8868:node key \u2192 { sessionId, diff \u6587\u672C }\u3002\n *\n * \u6298\u53E0\u7684 bash \u5DE5\u5177\u884C\u5728 DOM \u91CC\u53EA\u6709\u4E00\u884C\u6458\u8981,\u8F93\u51FA\u5185\u5BB9\u968F\u5C55\u5F00\u624D\u6302\u8F7D,\n * DOM \u63A2\u6D4B\u770B\u4E0D\u5230 \u2014\u2014 \u53EA\u80FD\u4ECE chat \u5FEB\u7167(\u6570\u636E\u5C42)\u62FF\u5230\u7ED3\u679C\u6587\u672C\u3002\n */\nconst pendingTexts = new Map<string, { sessionId: string; text: string }>()\n\n/** chat \u5FEB\u7167\u7684\u6700\u5C0F\u5F62\u72B6(\u4E0E dsh-web-enhance \u540C\u53E3\u5F84,\u53EA\u53D6\u6240\u9700\u5B57\u6BB5)\u3002 */\ninterface ChatLike {\n order: readonly string[]\n nodes: { get(key: string): { kind: string; data: unknown } | undefined }\n}\n\n/** \u4ECE tool-call \u8282\u70B9 data \u91CC\u53D6\u5DE5\u5177\u7ED3\u679C\u6839(root: RunningToolCall | ToolResultNode)\u3002 */\nfunction readToolRoot(data: unknown): unknown {\n if (typeof data !== 'object' || data === null) return null\n return (data as { root?: unknown }).root ?? null\n}\n\n/** \u5DE5\u5177\u7ED3\u679C\u662F\u5426\u5DF2\u843D\u5730(settled ToolResultNode:\u5E26 content \u6570\u7EC4)\u3002 */\nfunction isSettledToolResult(root: unknown): root is { call: { name: string } | null; content: readonly unknown[] } {\n if (typeof root !== 'object' || root === null) return false\n const r = root as { call?: unknown; content?: unknown }\n if (!Array.isArray(r.content)) return false\n // call \u5934\u53EF\u80FD\u88AB\u7A97\u53E3\u622A\u65AD(call: null),\u4FDD\u5B88\u8D77\u89C1\u53EA\u8BA4\u5F97\u5230\u5DE5\u5177\u540D\u7684\u3002\n if (r.call !== null && (typeof r.call !== 'object' || r.call === null || typeof (r.call as { name?: unknown }).name !== 'string')) {\n return false\n }\n return true\n}\n\n/** \u62FC\u63A5\u5DE5\u5177\u7ED3\u679C content \u91CC\u7684\u6587\u672C\u5757(\u5DE5\u5177\u8F93\u51FA\u901A\u5E38\u662F\u4E00\u6BB5 text \u5757)\u3002 */\nfunction toolResultText(root: unknown): string {\n if (!isSettledToolResult(root)) return ''\n const parts: string[] = []\n for (const block of root.content) {\n if (typeof block !== 'object' || block === null) continue\n const b = block as { type?: unknown; text?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text)\n }\n return parts.join('\\n')\n}\n\n/**\n * \u628A\u5F53\u524D\u4F1A\u8BDD chat \u5FEB\u7167\u91CC\u300C\u8F93\u51FA\u5F62\u4F3C git diff \u7684 bash \u5DE5\u5177\u7ED3\u679C\u300D\u540C\u6B65\u8FDB\n * \u6587\u672C\u8868:\u5148\u6E05\u6389\u672C\u4F1A\u8BDD\u65E7\u6761\u76EE(\u9632\u5DE8\u578B\u6587\u672C\u8DE8\u4F1A\u8BDD\u7D2F\u79EF),\u518D\u5199\u5165\u65B0\u6761\u76EE\u3002\n */\nfunction syncDiffToolTexts(sessionId: string, chat: ChatLike): void {\n const found = new Map<string, string>()\n for (const key of chat.order) {\n const node = chat.nodes.get(key)\n if (node === undefined || node.kind !== 'tool-call') continue\n const root = readToolRoot(node.data)\n if (root === null || !isSettledToolResult(root)) continue\n if (root.call === null || root.call.name !== 'bash') continue\n const text = toolResultText(root)\n if (!isDiffText(text)) continue\n found.set(key, text)\n }\n for (const [key, entry] of pendingTexts) {\n if (entry.sessionId === sessionId && !found.has(key)) pendingTexts.delete(key)\n }\n for (const [key, text] of found) pendingTexts.set(key, { sessionId, text })\n}\n\n/** \u6309 data-chat-anchor-key \u627E\u6E32\u67D3\u884C(\u904D\u5386\u6BD4\u5BF9,\u907F\u514D key \u7279\u6B8A\u5B57\u7B26\u8F6C\u4E49\u95EE\u9898)\u3002 */\nfunction findRowByKey(key: string): HTMLElement | null {\n for (const el of document.querySelectorAll('[data-chat-anchor-key]')) {\n if (el instanceof HTMLElement && el.getAttribute('data-chat-anchor-key') === key) return el\n }\n return null\n}\n\n/** \u7ED9\u6570\u636E\u5C42\u6536\u96C6\u5230\u7684 bash \u5DE5\u5177\u884C\u52A0\u6309\u94AE(\u884C\u672A\u6E32\u67D3\u65F6\u7B49\u4E0B\u4E00\u8F6E\u626B\u63CF)\u3002 */\nfunction attachToolRowButtons(): void {\n for (const [key, entry] of pendingTexts) {\n const row = findRowByKey(key)\n if (row === null) continue\n const sample = row.querySelector('[data-sample=\"bash\"]')\n if (!(sample instanceof HTMLElement)) continue\n ensureButtons(sample, () => pendingTexts.get(key)?.text ?? entry.text, null)\n }\n}\n\n// \u2014\u2014 \u626B\u63CF\u5668 \u2014\u2014\n\n/** \u4E00\u4E2A\u5019\u9009\u5757\u7684\u8DDF\u8E2A\u72B6\u6001(DOM \u6765\u6E90)\u3002 */\ninterface BlockState {\n /** \u4E0A\u6B21\u68C0\u6D4B\u5230\u7684\u5185\u5BB9\u7B7E\u540D\u3002 */\n text: string\n /** \u7A33\u5B9A\u53BB\u6296\u5B9A\u65F6\u5668(null = \u6CA1\u6709\u5F85\u5904\u7406\u7684\u52A0\u6309\u94AE)\u3002 */\n timer: number | null\n /** \u4E0A\u6B21\u89E3\u6790\u5931\u8D25\u7684\u65F6\u95F4\u6233(\u91CD\u8BD5\u8282\u6D41)\u3002 */\n lastTry: number\n /** \u4E0A\u6B21\u89E3\u6790\u662F\u5426\u5931\u8D25(\u5931\u8D25\u65F6\u5185\u5BB9\u4E0D\u53D8\u4E5F\u4F1A\u6309\u8282\u6D41\u91CD\u8BD5)\u3002 */\n failed: boolean\n /** \u4EE3\u7801\u5757\u662F\u5426\u6298\u53E0\u539F\u59CB\u6587\u672C(\u9ED8\u8BA4\u6298\u53E0,\u804A\u5929\u6D41\u4FDD\u6301\u7D27\u51D1)\u3002 */\n folded: boolean\n}\n\n/**\n * \u5168\u6587\u6863\u626B\u63CF\u5668:\u53D1\u73B0\u5019\u9009\u5757\u3001\u53BB\u6296\u3001\u6CE8\u5165\u6309\u94AE\u3001\u5185\u5BB9\u53D8\u5316\u65F6\u56DE\u9000\u3002\n *\n * \u72B6\u6001\u673A(\u6BCF\u4E2A\u5757):\n * - \u5185\u5BB9\u53D8\u5316/\u65B0\u5143\u7D20 \u2192 \u6392 STABLE_MS \u53BB\u6296;\n * - \u53BB\u6296\u5230\u70B9\u4E14\u5185\u5BB9\u672A\u518D\u53D8 \u2192 \u89E3\u6790\u9A8C\u8BC1(\u80FD\u89E3\u6790\u51FA\u6587\u4EF6\u624D\u52A0\u6309\u94AE);\n * - \u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff / \u7ED3\u6784\u6D88\u5931 \u2192 \u79FB\u9664\u6309\u94AE\u3002\n */\nclass DiffScanner {\n private readonly states = new WeakMap<HTMLElement, BlockState>()\n private observer: MutationObserver | null = null\n private scanTimer: number | null = null\n private safetyTimer: number | null = null\n\n start(): void {\n if (this.observer !== null) return\n // \u4EFB\u4F55 DOM \u589E\u5220\u90FD\u89E6\u53D1\u4E00\u6B21\u53BB\u6296\u626B\u63CF:\u8986\u76D6\u6D41\u5F0F\u8FFD\u52A0\u3001\u865A\u62DF\u5316\u91CD\u6302\u8F7D\u3001\n // \u5DE5\u5177\u884C\u5C55\u5F00\u7B49\u6240\u6709\u51FA\u73B0\u8DEF\u5F84\u3002\n this.observer = new MutationObserver(() => this.scheduleScan())\n this.observer.observe(document.body, { childList: true, subtree: true })\n this.scheduleScan()\n // \u515C\u5E95\u5468\u671F\u626B\u63CF:\u5185\u5BB9\u53D8\u5316\u7406\u8BBA\u4E0A\u90FD\u4F1A\u4EA7\u751F childList mutation,\n // \u8FD9\u91CC\u515C\u4F4F\u9057\u6F0F\u8DEF\u5F84,\u987A\u5E26\u8BA9\u89E3\u6790\u5931\u8D25\u7684\u91CD\u8BD5\u6709\u673A\u4F1A\u89E6\u53D1\u3002\n this.safetyTimer = window.setInterval(() => this.scan(), SAFETY_SCAN_MS)\n }\n\n stop(): void {\n this.observer?.disconnect()\n this.observer = null\n if (this.scanTimer !== null) {\n clearTimeout(this.scanTimer)\n this.scanTimer = null\n }\n if (this.safetyTimer !== null) {\n clearInterval(this.safetyTimer)\n this.safetyTimer = null\n }\n // states \u91CC\u7684\u53BB\u6296\u5B9A\u65F6\u5668\u4E0D\u5FC5\u9010\u4E2A\u6E05\u7406:\u5230\u70B9\u540E probe \u590D\u67E5\u4F1A\u628A\u5DF2\u65AD\u5F00\n // \u5143\u7D20\u7684\u6587\u672C\u8BFB\u6210\u7A7A\u4E32\u3001candidate=false,\u81EA\u7136\u653E\u5F03;WeakMap \u968F\u5757 GC\u3002\n }\n\n /** \u5408\u5E76\u9AD8\u9891 mutation \u4E3A\u4E00\u6B21\u626B\u63CF\u3002 */\n private scheduleScan(): void {\n if (this.scanTimer !== null) return\n this.scanTimer = window.setTimeout(() => {\n this.scanTimer = null\n this.scan()\n }, SCAN_DEBOUNCE)\n }\n\n /** \u5168\u91CF\u626B\u63CF:\u6570\u636E\u5C42\u5DE5\u5177\u884C\u6309\u94AE + DOM \u5019\u9009\u5757\u3002 */\n private scan(): void {\n attachToolRowButtons()\n for (const el of document.querySelectorAll('.md-code-block, [data-terminal]')) {\n this.inspect(el as HTMLElement)\n }\n }\n\n /** \u68C0\u67E5\u4E00\u4E2A\u5019\u9009\u5757:\u5185\u5BB9\u672A\u53D8\u5219\u4E0D\u52A8,\u53D8\u4E86\u5219\u6392\u53BB\u6296,\u4E0D\u518D\u5F62\u4F3C diff \u5219\u79FB\u9664\u6309\u94AE\u3002 */\n private inspect(el: HTMLElement): void {\n const probe = probeFor(el)\n if (probe === null || probe.running || !probe.candidate || probe.text.trim() === '') {\n const host = buttonHostFor(el)\n if (host !== null) removeButtons(host)\n // \u6062\u590D\u5B98\u65B9\u539F\u59CB\u663E\u793A(\u6B64\u524D\u53EF\u80FD\u88AB\u6298\u53E0)\u3002\n const body = findCodeBlockBody(el)\n if (body !== null) body.style.display = ''\n this.states.delete(el)\n return\n }\n const st = this.states.get(el)\n if (st !== undefined && st.text === probe.text) {\n // \u5185\u5BB9\u672A\u53D8:\u6309\u94AE\u5DF2\u52A0/\u53BB\u6296\u4E2D \u2192 \u65E0\u4E8B;\u89E3\u6790\u5931\u8D25 \u2192 \u6309\u8282\u6D41\u91CD\u8BD5\u3002\n if (!st.failed) {\n // \u91CD\u65B0\u540C\u6B65\u6298\u53E0\u6001:React \u53EF\u80FD\u5728\u540E\u53F0\u66FF\u6362\u8FC7 body \u8282\u70B9(\u8BED\u6CD5\u9AD8\u4EAE\n // \u5C31\u7EEA\u65F6 pre \u2192 div),\u65B0\u8282\u70B9\u9700\u8981\u91CD\u65B0\u9690\u85CF/\u663E\u793A\u3002\n this.applyFoldState(el, st)\n return\n }\n if (Date.now() - st.lastTry < RETRY_MS) return\n // \u5931\u8D25\u91CD\u8BD5\u5230\u70B9:\u76F4\u63A5\u8D70\u4E0B\u65B9\u52A0\u6309\u94AE\u5206\u652F(\u5185\u5BB9\u7A33\u5B9A,\u4E0D\u5FC5\u518D\u53BB\u6296)\u3002\n }\n if (st !== undefined && st.timer !== null) clearTimeout(st.timer)\n const timer = window.setTimeout(() => {\n const cur = this.states.get(el)\n if (cur === undefined || cur.timer !== timer) return\n cur.timer = null\n // \u5230\u70B9\u590D\u67E5:\u5185\u5BB9\u53C8\u53D8\u4E86(\u6D41\u5F0F\u8FD8\u5728\u7EE7\u7EED)\u5219\u653E\u5F03\u672C\u6B21,\u7B49\u4E0B\u4E00\u8F6E\u3002\n const p2 = probeFor(el)\n if (p2 === null || p2.running || !p2.candidate || p2.text !== cur.text) return\n this.attachButton(el, cur)\n }, STABLE_MS)\n this.states.set(el, {\n text: probe.text,\n timer,\n lastTry: st?.lastTry ?? 0,\n failed: st?.failed ?? false,\n folded: st?.folded ?? true,\n })\n }\n\n /** \u5185\u5BB9\u7A33\u5B9A\u540E\u6267\u884C:\u89E3\u6790\u9A8C\u8BC1\u3001\u6298\u53E0\u539F\u59CB\u6587\u672C\u5E76\u6CE8\u5165\u6309\u94AE(\u5931\u8D25\u5219\u4FDD\u6301\u65E0\u6309\u94AE)\u3002 */\n private attachButton(el: HTMLElement, st: BlockState): void {\n const host = buttonHostFor(el)\n if (host === null) return\n if (parseGitDiff(st.text).length === 0) {\n st.failed = true\n st.lastTry = Date.now()\n removeButtons(host)\n return\n }\n st.failed = false\n const isCodeBlock = el.classList.contains('md-code-block')\n if (isCodeBlock) {\n // \u9ED8\u8BA4\u6298\u53E0:diff \u4EE3\u7801\u5757\u53EA\u7559\u6807\u9898\u884C + \u6309\u94AE,\u4E0D\u644A\u5F00\u6574\u6BB5\u539F\u59CB\u6587\u672C\u3002\n this.applyFoldState(el, st)\n }\n // \u70B9\u51FB\u65F6\u73B0\u8BFB\u6587\u672C(React \u91CD\u6E32\u67D3\u540E\u65E7\u5F15\u7528\u53EF\u80FD\u8FC7\u671F)\u3002\n ensureButtons(\n host,\n () => probeFor(el)?.text ?? st.text,\n isCodeBlock ? { folded: st.folded, onToggle: () => this.toggleFold(el) } : null,\n )\n }\n\n /** \u540C\u6B65\u4EE3\u7801\u5757\u7684\u6298\u53E0\u6001(\u5B98\u65B9 body \u53EA\u6539 display,React \u4E0D\u7BA1\u7406\u8FD9\u4E2A style)\u3002 */\n private applyFoldState(el: HTMLElement, st: BlockState): void {\n if (!el.classList.contains('md-code-block')) return\n const body = findCodeBlockBody(el)\n if (body !== null) body.style.display = st.folded ? 'none' : ''\n }\n\n /** \u5C55\u5F00/\u6536\u8D77\u4EE3\u7801\u5757\u539F\u59CB\u6587\u672C,\u5E76\u540C\u6B65\u6309\u94AE\u6587\u6848\u3002 */\n private toggleFold(el: HTMLElement): void {\n const st = this.states.get(el)\n if (st === undefined) return\n st.folded = !st.folded\n this.applyFoldState(el, st)\n const host = buttonHostFor(el)\n const btn = host?.querySelector('[data-dsh-cr-fold]')\n if (btn instanceof HTMLElement) {\n const texts = CR_TEXTS[detectLang()]\n btn.textContent = st.folded ? texts.expand : texts.collapse\n }\n }\n}\n\n// \u2014\u2014 \u63D2\u4EF6\u4E3B\u4F53 \u2014\u2014\n\n/**\n * \u9700\u8981\u7684 client \u670D\u52A1:slots(slot \u6CE8\u518C)\u3001locale(\u754C\u9762\u8BED\u8A00\u68C0\u6D4B)\u3002\n *\n * cordis \u7684\u670D\u52A1\u8BBF\u95EE\u5FC5\u987B\u5148\u5728\u8FD9\u91CC\u58F0\u660E\u6CE8\u5165,\u5426\u5219 apply \u91CC\u8BFB ctx.locale\n * \u4F1A\u629B \"cannot get property 'locale' without inject\" \u2014\u2014 loader entry\n * \u5E94\u7528\u5931\u8D25\u4F1A\u8BA9\u6574\u4E2A\u5BA2\u6237\u7AEF\u7EC4\u5408\u6811\u5D29\u6E83(\u767D\u5C4F)\u3002\u8FD9\u4E5F\u662F\u4E0A\u6B21\u4E8B\u6545\u7684\u6839\u56E0\u3002\n * \u5B98\u65B9\u88C5\u914D\u542B dsh-client-locale,\u670D\u52A1\u5B58\u5728;\u4E07\u4E00\u672A\u6765\u7F3A\u5931,inject \u8FD4\u56DE\n * undefined,detectLang \u91CC\u4F1A\u9000\u5230\u6D4F\u89C8\u5668\u8BED\u8A00,\u4E0D\u4F1A\u5D29\u3002\n */\nexport const inject = ['slots', 'locale']\n\n/** Client \u63D2\u4EF6 body:\u63A5\u7BA1\u5B98\u65B9 locale \u670D\u52A1,\u6CE8\u518C\u626B\u63CF\u5668\u5BBF\u4E3B\u4E0E\u6570\u636E\u5C42\u76D1\u542C\u3002 */\nexport function apply(ctx: ClientContext): void {\n // \u63A5\u7BA1\u5B98\u65B9 locale \u670D\u52A1(\u8FD0\u884C\u65F6\u5F62\u72B6\u6821\u9A8C;\u7F3A\u7701\u65F6 detectLang \u9000\u6D4F\u89C8\u5668\u8BED\u8A00)\u3002\n const locale = ctx.locale\n if (typeof locale === 'object' && locale !== null && typeof (locale as { getLocale?: unknown }).getLocale === 'function') {\n localeService = locale as LocaleService\n }\n ctx.slots.inject(\n 'shell.overlay',\n () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'code-review-enhancer',\n }, CodeReviewEnhancer),\n )\n ctx.slots.inject(\n 'conversation.session.header.actions',\n () => ctx.slots.register({\n name: 'conversation.session.header.actions',\n id: 'code-review-tool-watcher',\n // \u5B98\u65B9\u6761\u76EE:agent-preset=-10\u3001subagent-catalog=10\u3001job-list=20,\n // web-enhance=15\u3002\u53D6 25 \u6392\u5728\u6700\u540E,\u4E0D\u4E0E\u4ED6\u4EBA\u5E76\u5217\u3002\n order: 25,\n }, DiffToolButtonWatcher),\n )\n}\n\n/**\n * \u626B\u63CF\u5668\u5BBF\u4E3B\u7EC4\u4EF6(root \u4F5C\u7528\u57DF,\u5168\u5C40\u53EA\u6B64\u4E00\u4EFD):\u6302\u8F7D\u65F6\u6CE8\u5165\u6837\u5F0F\u3001\u542F\u52A8\n * \u5168\u6587\u6863\u626B\u63CF\u3001\u76D1\u542C\u5BA1\u67E5\u9875\u7684\u8BC4\u8BBA\u56DE\u4F20\u6D88\u606F,\u5378\u8F7D\u65F6\u5168\u90E8\u6E05\u7406\u3002\n * \u81EA\u8EAB\u6E32\u67D3 null,\u4E0D\u5360\u4EFB\u4F55\u754C\u9762\u7A7A\u95F4\u3002\n */\nfunction CodeReviewEnhancer() {\n useEffect(() => {\n ensureStyle()\n const scanner = new DiffScanner()\n scanner.start()\n const onMessage = handleReviewMessage\n window.addEventListener('message', onMessage)\n return () => {\n scanner.stop()\n window.removeEventListener('message', onMessage)\n }\n }, [])\n return null\n}\n\n/**\n * \u6570\u636E\u5C42\u76D1\u542C(session \u4F5C\u7528\u57DF,\u968F\u4F1A\u8BDD\u5207\u6362\u81EA\u52A8\u91CD\u8BA2\u9605):\n * 1. \u8BA2\u9605\u5F53\u524D\u4F1A\u8BDD chat \u5FEB\u7167,\u628A\u8F93\u51FA\u5F62\u4F3C git diff \u7684 bash \u5DE5\u5177\u7ED3\u679C\u540C\u6B65\n * \u8FDB\u6587\u672C\u8868,\u626B\u63CF\u5668\u636E\u6B64\u7ED9\u6298\u53E0\u7684\u5DE5\u5177\u884C\u52A0\u6309\u94AE;\n * 2. \u6CE8\u518C\u5F53\u524D\u4F1A\u8BDD\u7684 composer \u5199\u5165\u5668 \u2014\u2014 \u5BA1\u67E5\u9875\u56DE\u4F20\u7684\u8BC4\u8BBA\u7ECF\n * inputActions.setDraft(\u5B98\u65B9\u5355\u4E00\u5199\u5165\u53E3)\u5199\u5165\u5BF9\u8BDD\u6846\u3002\u81EA\u8EAB\u6E32\u67D3 null\u3002\n */\nfunction DiffToolButtonWatcher({ useSession, sessionId, inputActions }: PropsRuntime<'conversation.session.header.actions'>) {\n const chat = useSession((state) => state.chat)\n useEffect(() => {\n if (chat !== undefined) syncDiffToolTexts(String(sessionId), chat)\n }, [chat, sessionId])\n useEffect(() => {\n // \u53EA\u6E05\u7406\u81EA\u5DF1\u6CE8\u518C\u7684\u5199\u5165\u5668(\u591A\u7EC4\u4EF6\u5B9E\u4F8B\u5E76\u5B58\u65F6\u4E92\u4E0D\u8BEF\u5220)\u3002\n const writer = (text: string): void => inputActions.setDraft(text)\n composerWriter = writer\n return () => {\n if (composerWriter === writer) composerWriter = null\n }\n }, [inputActions])\n return null\n}\n", "/**\n * unified diff \u6587\u672C\u89E3\u6790\u5668(\u7EAF\u903B\u8F91,\u4E0D\u4F9D\u8D56 DOM,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u8F93\u5165\u662F `git diff` / `git show` / `git diff --no-index` \u7B49\u4EA7\u751F\u7684\n * unified diff \u6587\u672C,\u8F93\u51FA\u6309\u6587\u4EF6\u62C6\u5206\u7684\u7ED3\u6784\u5316\u6570\u636E,\u4F9B\u53CC\u5217\u89C6\u56FE\u6E32\u67D3\u3002\n *\n * \u884C\u6A21\u578B\u8BF4\u660E:\n * - ctx \u4E0A\u4E0B\u6587\u884C:\u65E7/\u65B0\u4E24\u4FA7\u90FD\u6709,\u884C\u53F7\u5404\u81EA\u9012\u589E;\n * - del \u5220\u9664\u884C:\u53EA\u5728\u65E7\u4FA7;\n * - add \u65B0\u589E\u884C:\u53EA\u5728\u65B0\u4FA7;\n * - change \u53D8\u66F4\u5BF9:\u8FDE\u7EED\u300C-\u300D\u5757\u540E\u7D27\u8DDF\u8FDE\u7EED\u300C+\u300D\u5757\u65F6,\u4E24\u4E24\u914D\u5BF9\u6210\u540C\u4E00\u884C\n * (GitHub split view \u7684\u5BF9\u9F50\u65B9\u5F0F),\u65E7\u884C\u53F7\u53D6 - \u884C\u3001\u65B0\u884C\u53F7\u53D6 + \u884C,\n * \u4E24\u4FA7\u6570\u91CF\u4E0D\u7B49\u65F6,\u591A\u51FA\u7684\u884C\u9000\u5316\u4E3A\u5355\u4FA7 del/add;\n * - gap \u95F4\u9699\u884C:@@ hunk \u5934,\u6216\u6298\u53E0\u540E\u7684\u300C\u22EF \u5C55\u5F00\u300D\u5360\u4F4D(\u6E32\u67D3\u5C42\u7528),\n * \u4E0D\u5360\u884C\u53F7\u3002\n *\n * \u5B9E\u73B0\u6CE8\u610F:\u4E0D\u628A\u300C\u5F53\u524D\u6587\u4EF6\u300D\u5B58\u6210\u88AB\u95ED\u5305\u6355\u83B7\u8D4B\u503C\u7684\u5C40\u90E8\u53D8\u91CF \u2014\u2014\n * TypeScript \u5BF9\u300C\u95ED\u5305\u5185\u8D4B\u503C\u7684 let\u300D\u4F1A\u505A\u4FDD\u5B88\u63A7\u5236\u6D41\u5206\u6790,\u5916\u5C42\u7A84\u5316\u4F1A\u5931\u6548\n * (\u751A\u81F3\u7A84\u5316\u6210 never);\u7EDF\u4E00\u7528 current() \u4ECE files \u6808\u9876\u53D6,\u907F\u514D\u8E29\u5751\u3002\n */\n\n/** \u53CC\u5217\u89C6\u56FE\u91CC\u7684\u4E00\u884C\u3002 */\nexport interface DiffRow {\n kind: 'ctx' | 'del' | 'add' | 'change' | 'gap'\n /** \u65E7\u4FA7\u884C\u53F7(ctx/del/change \u6709\u503C)\u3002 */\n oldNo?: number\n /** \u65B0\u4FA7\u884C\u53F7(ctx/add/change \u6709\u503C)\u3002 */\n newNo?: number\n /** \u65E7\u4FA7\u6587\u672C(\u5DF2\u53BB\u6389\u884C\u9996\u7684 - / \u7A7A\u683C\u524D\u7F00)\u3002 */\n oldText?: string\n /** \u65B0\u4FA7\u6587\u672C(\u5DF2\u53BB\u6389\u884C\u9996\u7684 + / \u7A7A\u683C\u524D\u7F00)\u3002 */\n newText?: string\n /** gap \u884C\u7684\u663E\u793A\u6587\u672C(@@ \u5934\u539F\u6587)\u3002 */\n gapText?: string\n /** \u65E7\u4FA7\u8BE5\u884C\u662F\u6587\u4EF6\u6700\u540E\u4E00\u884C\u4E14\u672B\u5C3E\u65E0\u6362\u884C\u7B26(\\ No newline \u6807\u8BB0)\u3002 */\n oldNoEol?: boolean\n /** \u65B0\u4FA7\u8BE5\u884C\u662F\u6587\u4EF6\u6700\u540E\u4E00\u884C\u4E14\u672B\u5C3E\u65E0\u6362\u884C\u7B26\u3002 */\n newNoEol?: boolean\n}\n\n/** \u4E00\u4E2A\u6587\u4EF6\u7684\u53D8\u66F4\u3002 */\nexport interface FileDiff {\n /** \u53D8\u66F4\u524D\u8DEF\u5F84(\u539F\u59CB\u6587\u672C,\u53EF\u80FD\u5E26 a/ \u524D\u7F00;/dev/null \u8868\u793A\u65B0\u5EFA)\u3002 */\n oldPath: string\n /** \u53D8\u66F4\u540E\u8DEF\u5F84(\u539F\u59CB\u6587\u672C,\u53EF\u80FD\u5E26 b/ \u524D\u7F00;/dev/null \u8868\u793A\u5220\u9664)\u3002 */\n newPath: string\n /** \u53D8\u66F4\u7C7B\u578B\u3002 */\n status: 'modified' | 'added' | 'deleted' | 'renamed' | 'binary' | 'mode'\n /** \u6587\u4EF6\u7EA7\u5143\u4FE1\u606F\u884C(new file mode / rename from / similarity index \u7B49)\u3002 */\n meta: string[]\n /** \u6E32\u67D3\u884C\u5E8F\u5217(\u542B hunk \u95F4\u9699\u884C)\u3002 */\n rows: DiffRow[]\n}\n\n/** \u5C55\u793A\u7528\u7684\u8DEF\u5F84:\u53BB\u6389 a/ b/ \u524D\u7F00(\u4EC5\u5F53\u786E\u5B9E\u5E26\u524D\u7F00\u65F6)\u3002 */\nexport function displayPath(path: string): string {\n if (path === '/dev/null') return path\n return path.replace(/^[ab]\\//, '')\n}\n\n/** \u6587\u672C\u662F\u5426\u5F62\u4F3C git diff(\u68C0\u6D4B\u7528,\u5148\u4E8E\u5B8C\u6574\u89E3\u6790)\u3002 */\nexport function isDiffText(text: string): boolean {\n const lines = text.split('\\n')\n // git \u6807\u51C6\u5934;\u6CA1\u6709\u5B83\u65F6,\u9000\u800C\u8981\u6C42\u300Chunk \u5934 + \u6587\u4EF6\u6807\u8BB0\u300D\u540C\u65F6\u51FA\u73B0,\n // \u907F\u514D\u628A\u666E\u901A\u5E26 @@ \u7684\u6587\u672C\u8BEF\u5224\u6210 diff\u3002\n if (lines.some((line) => line.startsWith('diff --git '))) return true\n const hasHunk = lines.some((line) => /^@@ -\\d+/.test(line))\n const hasFileMark = lines.some((line) => /^(---|\\+\\+\\+) \\S/.test(line))\n return hasHunk && hasFileMark\n}\n\n/** \u89E3\u6790\u6574\u6BB5 unified diff \u6587\u672C\u3002 */\nexport function parseGitDiff(text: string): FileDiff[] {\n const lines = text.split(/\\r?\\n/)\n const files: FileDiff[] = []\n // \u5F53\u524D\u662F\u5426\u5904\u4E8E\u67D0\u4E2A hunk \u7684\u6B63\u6587\u533A(\u8DEF\u5F84\u884C ---/+++ \u53EA\u5728 hunk \u5916\u51FA\u73B0)\u3002\n let inHunk = false\n // \u5F53\u524D hunk \u7684\u65E7/\u65B0\u884C\u53F7\u6E38\u6807(\u4ECE hunk \u5934\u58F0\u660E\u7684\u8D77\u70B9\u5F00\u59CB\u9012\u589E)\u3002\n let oldNo = 0\n let newNo = 0\n // \u7B49\u5F85\u914D\u5BF9\u7684\u5220\u9664\u884C:\u8FDE\u7EED\u300C-\u300D\u5757\u4F1A\u5148\u7F13\u5B58,\u9047\u5230\u300C+\u300D\u65F6\u9010\u5BF9\u914D\u5BF9\u6210\n // change \u884C;hunk \u7ED3\u675F\u6216\u9047\u5230\u4E0A\u4E0B\u6587\u884C\u65F6,\u5269\u4F59\u7F13\u5B58\u6309 del \u884C\u8F93\u51FA\u3002\n // noEol \u6807\u8BB0\u53EF\u80FD\u7D27\u8DDF\u300C-\u300D\u884C\u51FA\u73B0(\),\n // \u6240\u4EE5\u8981\u7F13\u5B58\u8FDB\u5F85\u914D\u5BF9\u6761\u76EE,\u914D\u5BF9\u65F6\u624D\u80FD\u5E26\u5230 change \u884C\u7684\u65E7\u4FA7\u3002\n let pendingDels: Array<{ text: string; no: number; noEol: boolean }> = []\n // \u6700\u8FD1\u4E00\u6761\u5185\u5BB9\u884C(\u975E gap)\u7684\u7C7B\u578B,\u7528\u4E8E\u628A \\ No newline \u6807\u8BB0\u6302\u5230\u6B63\u786E\u7684\u4E00\u4FA7\u3002\n let lastKind: 'ctx' | 'del' | 'add' | null = null\n // \u6700\u8FD1\u4E00\u6761\u5DF2\u8F93\u51FA(\u6216\u5DF2\u914D\u5BF9)\u7684\u3001\u5E26\u65B0\u4FA7\u7684\u884C,\u7528\u4E8E\u7ED9\u65B0\u4FA7\u6302 noEol\u3002\n let lastNewRow: DiffRow | null = null\n\n /** \u5F53\u524D\u6B63\u5728\u7D2F\u79EF\u7684\u6587\u4EF6(\u6808\u9876);\u8FD8\u6CA1\u6709 diff \u5934\u65F6\u4E3A null\u3002 */\n function current(): FileDiff | null {\n return files.length > 0 ? files[files.length - 1] : null\n }\n\n /** \u628A\u5F85\u914D\u5BF9\u7F13\u5B58\u6309 del \u884C\u5237\u51FA(\u9047\u4E0A\u4E0B\u6587\u3001\u65B0 hunk\u3001\u6587\u4EF6\u5207\u6362\u3001\u6587\u672C\u7ED3\u675F\u65F6\u8C03\u7528)\u3002 */\n function flushPending(): void {\n const file = current()\n if (file === null || pendingDels.length === 0) return\n for (const del of pendingDels) {\n file.rows.push({\n kind: 'del',\n oldNo: del.no,\n oldText: del.text,\n oldNoEol: del.noEol,\n })\n lastNewRow = null\n }\n pendingDels = []\n }\n\n /** \u65B0\u5EFA\u6587\u4EF6\u5E76\u63A5\u7BA1\u6E38\u6807(\u81EA\u52A8\u5237\u51FA\u4E0A\u4E00\u4E2A\u6587\u4EF6\u7684\u5F85\u914D\u5BF9\u7F13\u5B58)\u3002 */\n function startFile(oldPath: string, newPath: string): void {\n flushPending()\n inHunk = false\n lastKind = null\n lastNewRow = null\n files.push({\n oldPath,\n newPath,\n status: 'modified',\n meta: [],\n rows: [],\n })\n }\n\n /** \u89E3\u6790 @@ hunk \u5934,\u8FD4\u56DE\u662F\u5426\u6210\u529F(\u5931\u8D25\u884C\u539F\u6837\u5F53\u6B63\u6587\u5904\u7406)\u3002 */\n function openHunk(line: string): boolean {\n const match = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/.exec(line)\n if (match === null) return false\n flushPending()\n oldNo = Number(match[1])\n newNo = Number(match[3])\n inHunk = true\n lastKind = null\n lastNewRow = null\n // \u4FDD\u7559\u6574\u4E2A hunk \u5934\u539F\u6587(\u542B\u5C3E\u90E8 section \u540D),\u6E32\u67D3\u6210\u95F4\u9699\u884C\u3002\n current()?.rows.push({ kind: 'gap', gapText: line })\n return true\n }\n\n /** \u8F93\u51FA\u4E00\u6761 ctx \u884C(\u5E26\u4E24\u4FA7\u884C\u53F7\u4E0E\u6587\u672C)\u3002 */\n function emitContext(text: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = { kind: 'ctx', oldNo, newNo, oldText: text, newText: text }\n file.rows.push(row)\n oldNo += 1\n newNo += 1\n lastKind = 'ctx'\n lastNewRow = row\n }\n\n /** \u8F93\u51FA\u4E00\u6761 change \u884C(\u65E7\u4FA7 del \u4E0E \u65B0\u4FA7 add \u914D\u5BF9)\u3002 */\n function emitChange(del: { text: string; no: number; noEol: boolean }, addText: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = {\n kind: 'change',\n oldNo: del.no,\n newNo,\n oldText: del.text,\n newText: addText,\n oldNoEol: del.noEol,\n }\n file.rows.push(row)\n newNo += 1\n lastKind = 'add'\n lastNewRow = row\n }\n\n /** \u8F93\u51FA\u4E00\u6761 add \u884C\u3002 */\n function emitAdd(text: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = { kind: 'add', newNo, newText: text }\n file.rows.push(row)\n newNo += 1\n lastKind = 'add'\n lastNewRow = row\n }\n\n /** \u5904\u7406 \ \u6807\u8BB0:\u6302\u5230\u4E0A\u4E00\u884C\u5BF9\u5E94\u7684\u4E00\u4FA7\u3002 */\n function markNoEol(): void {\n if (lastKind === 'del') {\n // \u4E0A\u4E00\u4E2A\u300C-\u300D\u53EF\u80FD\u8FD8\u5728\u5F85\u914D\u5BF9\u7F13\u5B58\u91CC(\u8FD8\u6CA1\u9047\u5230\u914D\u5BF9/\u5237\u51FA)\u3002\n const last = pendingDels[pendingDels.length - 1]\n if (last !== undefined) {\n last.noEol = true\n } else {\n const file = current()\n if (file !== null && file.rows.length > 0) {\n // \u5DF2\u88AB\u5237\u6210 del \u884C:\u6807\u8BB0\u65E7\u4FA7\u3002\n const row = file.rows[file.rows.length - 1]\n if (row.kind === 'del') row.oldNoEol = true\n }\n }\n } else if (lastKind === 'add') {\n // \u4E0A\u4E00\u884C\u662F change \u6216 add:\u6807\u8BB0\u65B0\u4FA7\u3002\n if (lastNewRow !== null) lastNewRow.newNoEol = true\n } else if (lastKind === 'ctx') {\n const file = current()\n if (file !== null && file.rows.length > 0) {\n const row = file.rows[file.rows.length - 1]\n if (row.kind === 'ctx') {\n row.oldNoEol = true\n row.newNoEol = true\n }\n }\n }\n }\n\n for (const line of lines) {\n // \u7A7A\u884C:\u6587\u672C\u672B\u5C3E\u6362\u884C\u7B26\u7684\u62C6\u5206\u4EA7\u7269(unified diff \u7684\u5185\u5BB9\u884C\u90FD\u5E26\u524D\u7F00\n // \u5B57\u7B26,\u771F\u6B63\u7684\u7A7A\u5185\u5BB9\u884C\u662F \" \" \u800C\u4E0D\u662F \"\"),\u76F4\u63A5\u8DF3\u8FC7\u3002\n if (line === '') continue\n if (line.startsWith('diff --git ')) {\n // \u65B0\u6587\u4EF6\u5934:\u5C1D\u8BD5\u4ECE a/X b/Y \u63D0\u53D6\u8DEF\u5F84,\u63D0\u53D6\u5931\u8D25\u4E5F\u4E0D\u963B\u585E\u89E3\u6790\u3002\n const match = /^diff --git (?:a\\/)?(\\S+) (?:b\\/)?(\\S+)$/.exec(line)\n startFile(match?.[1] ?? '', match?.[2] ?? '')\n continue\n }\n if (line.startsWith('@@ ')) {\n openHunk(line)\n continue\n }\n const cur = current()\n if (cur === null) {\n // diff \u5934\u4E4B\u524D\u7684\u524D\u5BFC\u5185\u5BB9(\u63D0\u4EA4\u4FE1\u606F\u7B49)\u8DF3\u8FC7;\u88F8 patch(\u65E0 diff --git\n // \u5934,\u5982 git diff --no-index \u7684\u5408\u5E76\u683C\u5F0F)\u4EE5 --- \u884C\u76F4\u63A5\u5F00\u6587\u4EF6\u3002\n if (line.startsWith('--- ') && line.length > 4) startFile(line.slice(4), '')\n continue\n }\n if (!inHunk) {\n // \u2014\u2014 hunk \u5916\u7684\u5143\u4FE1\u606F\u884C \u2014\u2014\n if (line.startsWith('--- ') && line.length > 4) {\n cur.oldPath = line.slice(4)\n } else if (line.startsWith('+++ ') && line.length > 4) {\n cur.newPath = line.slice(4)\n } else if (line.startsWith('new file mode ')) {\n cur.status = 'added'\n cur.meta.push(line)\n } else if (line.startsWith('deleted file mode ')) {\n cur.status = 'deleted'\n cur.meta.push(line)\n } else if (line.startsWith('rename from ') || line.startsWith('rename to ')) {\n cur.status = 'renamed'\n cur.meta.push(line)\n } else if (\n line.startsWith('old mode ') ||\n line.startsWith('new mode ') ||\n line.startsWith('similarity index ') ||\n line.startsWith('dissimilarity index ') ||\n line.startsWith('copy from ') ||\n line.startsWith('copy to ')\n ) {\n cur.meta.push(line)\n } else if (line.startsWith('Binary files ') || line.startsWith('GIT binary patch')) {\n // \u4E8C\u8FDB\u5236\u6587\u4EF6:\u6CA1\u6709 hunk,\u6E32\u67D3\u6210\u4FE1\u606F\u884C\u3002\n cur.status = 'binary'\n cur.meta.push(line)\n } else if (line.startsWith('index ')) {\n // index \u884C(blob \u54C8\u5E0C)\u4FE1\u606F\u91CF\u4F4E,\u6536\u8FDB meta \u4F46\u6E32\u67D3\u5C42\u9009\u62E9\u5FFD\u7565\u3002\n cur.meta.push(line)\n }\n // \u5176\u4ED6(\u7A7A\u884C\u7B49)\u5FFD\u7565\u3002\n continue\n }\n // \u2014\u2014 hunk \u6B63\u6587 \u2014\u2014\n const kind = line[0]\n const body = line.slice(1)\n if (kind === ' ') {\n flushPending()\n emitContext(body)\n } else if (kind === '-') {\n pendingDels.push({ text: body, no: oldNo, noEol: false })\n oldNo += 1\n lastKind = 'del'\n } else if (kind === '+') {\n const del = pendingDels.shift()\n if (del !== undefined) {\n emitChange(del, body)\n } else {\n emitAdd(body)\n }\n } else if (kind === '\\\\' && line.startsWith('\\\')) {\n markNoEol()\n } else {\n // \u65E0\u6CD5\u8BC6\u522B\u7684\u884C(\u7578\u5F62 diff):\u6309\u4E0A\u4E0B\u6587\u5BBD\u5BB9\u5904\u7406,\u4FDD\u8BC1\u89C6\u56FE\u4E0D\u9519\u4F4D\u3002\n flushPending()\n emitContext(line)\n }\n }\n flushPending()\n return files\n}\n\n/**\n * \u7EDF\u8BA1\u6574\u4E2A\u89E3\u6790\u7ED3\u679C:\u65B0\u589E\u884C\u6570 / \u5220\u9664\u884C\u6570(change \u884C\u4E24\u4FA7\u5404\u8BA1\u4E00\u6B21)\u3002\n * \u4F9B\u5DE5\u5177\u680F\u300C+N \u2212M \u00B7 F \u4E2A\u6587\u4EF6\u300D\u4F7F\u7528\u3002\n */\nexport function countStats(files: FileDiff[]): { added: number; removed: number; files: number } {\n let added = 0\n let removed = 0\n for (const file of files) {\n for (const row of file.rows) {\n if (row.kind === 'add' || row.kind === 'change') added += 1\n if (row.kind === 'del' || row.kind === 'change') removed += 1\n }\n }\n return { added, removed, files: files.length }\n}\n", "/**\n * \u4EE3\u7801\u9AD8\u4EAE\u7684\u7EAF\u903B\u8F91(\u4E0D\u4F9D\u8D56 DOM,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u4E00\u3001\u57FA\u7840\u8BED\u6CD5\u9AD8\u4EAE:\u8F7B\u91CF\u5355\u884C tokenizer(\u4E0D\u5F15\u5165\u9AD8\u4EAE\u5E93,\u72EC\u7ACB\u9875\u96F6\u4F9D\u8D56),\n * \u5207\u51FA \u5173\u952E\u5B57/\u5B57\u7B26\u4E32/\u6CE8\u91CA/\u6570\u5B57 \u56DB\u7C7B token,\u6E32\u67D3\u5C42\u6620\u5C04\u6210\u5B98\u65B9\n * --shiki-token-* \u989C\u8272(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4)\u3002\n * \u652F\u6301\u6CE8\u91CA\u98CE\u683C:\u53CC\u659C\u6760\u3001\u659C\u6760\u661F\u53F7(\u884C\u5185)\u3001\u4E95\u53F7(Python/shell \u7CFB,\n * \u6309\u6269\u5C55\u540D\u5F00\u542F)\u3002\u5757\u6CE8\u91CA\u4E0D\u8DE8\u884C(\u57FA\u7840\u7248\u53D6\u820D,\u5355\u884C\u6E32\u67D3\u573A\u666F\u5F71\u54CD\u5F88\u5C0F)\u3002\n *\n * \u4E8C\u3001\u884C\u5185\u5B57\u7B26\u7EA7 diff:\u5BF9 change \u884C(\u540C\u4E00\u884C\u65E7/\u65B0\u914D\u5BF9)\u505A\u516C\u5171\u524D\u7F00/\u540E\u7F00\n * \u5265\u79BB,\u4E2D\u95F4\u4E0D\u540C\u7684\u5B57\u7B26\u6BB5\u5C31\u662F\u300C\u53EA\u6539\u4E86\u51E0\u4E2A\u5B57\u7B26\u300D\u7684\u7CBE\u786E\u8303\u56F4,\u6E32\u67D3\u5C42\u7528\n * \u52A0\u6DF1\u80CC\u666F\u8272\u9AD8\u4EAE\u3002\u7D22\u5F15\u6309 code point(Array.from)\u8BA1\u7B97,\u6E32\u67D3\u5C42\u540C\u6837\u6309\n * code point \u5207\u7247,\u4EE3\u7406\u5BF9\u4E0D\u4F1A\u4ECE\u4E2D\u95F4\u65AD\u5F00\u3002\n */\n\n/** \u8BED\u6CD5 token(\u7EAF\u6587\u672C\u5207\u5206,HTML \u8F6C\u4E49\u7531\u6E32\u67D3\u5C42\u505A)\u3002 */\nexport interface Token {\n text: string\n /** null = \u666E\u901A\u6587\u672C;\u5176\u4F59\u4E3A\u8BED\u6CD5\u7C7B\u522B,\u6620\u5C04\u5230 .tok-* \u6837\u5F0F\u3002 */\n cls: 'tok-kw' | 'tok-str' | 'tok-com' | 'tok-num' | null\n}\n\n/** \u884C\u5185\u5DEE\u5F02\u7684\u4E2D\u6BB5\u8303\u56F4(code point \u7D22\u5F15,\u524D\u95ED\u540E\u5F00;null = \u65E0\u5DEE\u5F02)\u3002 */\nexport interface DiffMidRange {\n oldStart: number\n oldEnd: number\n newStart: number\n newEnd: number\n}\n\n/** \u5355\u884C\u9AD8\u4EAE\u914D\u7F6E:\u662F\u5426\u652F\u6301 # \u6CE8\u91CA(Python/shell/YAML \u7B49)\u3002 */\nexport interface LineLang {\n hashComment: boolean\n}\n\n/**\n * \u5408\u5E76\u5E38\u89C1\u8BED\u8A00\u7684\u5173\u952E\u5B57\u8868(\u57FA\u7840\u9AD8\u4EAE\u4E0D\u6309\u8BED\u8A00\u7EC6\u5206,\u4E00\u5957\u901A\u7528\u8BCD\u8868\u8986\u76D6\n * JS/TS\u3001Python\u3001Go\u3001Rust\u3001Java/C \u7CFB\u7684\u7EDD\u5927\u591A\u6570\u5173\u952E\u5B57)\u3002\n */\nconst KEYWORDS = new Set([\n // JS/TS\n 'const', 'let', 'var', 'function', 'class', 'extends', 'super', 'new', 'delete',\n 'typeof', 'instanceof', 'in', 'of', 'this', 'static', 'get', 'set', 'async',\n 'await', 'yield', 'return', 'if', 'else', 'switch', 'case', 'default', 'break',\n 'continue', 'try', 'catch', 'finally', 'throw', 'for', 'while', 'do', 'import',\n 'export', 'from', 'as', 'interface', 'type', 'enum', 'namespace', 'module',\n 'implements', 'public', 'private', 'protected', 'readonly', 'abstract',\n 'declare', 'keyof', 'infer', 'never', 'unknown', 'any', 'void', 'null',\n 'undefined', 'true', 'false', 'NaN', 'Infinity', 'require',\n // Python\n 'def', 'lambda', 'pass', 'raise', 'elif', 'not', 'and', 'or', 'is', 'None',\n 'True', 'False', 'print', 'with', 'global', 'nonlocal', 'assert', 'del',\n // Go\n 'go', 'func', 'package', 'struct', 'map', 'chan', 'select', 'defer', 'range',\n 'nil', 'goto', 'fallthrough',\n // Rust\n 'fn', 'let', 'mut', 'pub', 'impl', 'trait', 'match', 'use', 'mod', 'self',\n 'crate', 'unsafe', 'where', 'loop', 'move', 'ref', 'dyn',\n // Java / C \u7CFB\n 'int', 'float', 'double', 'char', 'boolean', 'byte', 'short', 'long', 'signed',\n 'unsigned', 'auto', 'register', 'extern', 'volatile', 'constexpr', 'sizeof',\n 'template', 'typename',\n])\n\n/** \u505A\u8BED\u6CD5\u9AD8\u4EAE\u7684\u6269\u5C55\u540D\u96C6\u5408(\u5176\u4F59\u6269\u5C55\u540D\u4E0D\u505A\u8BED\u6CD5\u9AD8\u4EAE,\u53EA\u505A\u5B57\u7B26\u7EA7 diff)\u3002 */\nconst HIGHLIGHT_EXTS = new Set([\n 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'json', 'py', 'rb', 'go', 'rs',\n 'java', 'c', 'h', 'cpp', 'cc', 'cs', 'css', 'scss', 'less', 'html', 'htm',\n 'vue', 'xml', 'sql', 'md', 'yml', 'yaml', 'toml', 'sh', 'bash', 'zsh', 'ini',\n])\n\n/** # \u6CE8\u91CA\u8BED\u8A00\u7684\u6269\u5C55\u540D(Python/shell/YAML \u7CFB;JS \u7684 # \u662F\u79C1\u6709\u5B57\u6BB5,\u4E0D\u5F00)\u3002 */\nconst HASH_COMMENT_EXTS = new Set(['py', 'rb', 'sh', 'bash', 'zsh', 'yml', 'yaml', 'toml', 'ini'])\n\n/** \u6309\u6587\u4EF6\u8DEF\u5F84\u63A8\u65AD\u9AD8\u4EAE\u914D\u7F6E;\u65E0\u6CD5\u8BC6\u522B\u65F6\u8FD4\u56DE null(\u53EA\u505A\u5B57\u7B26\u7EA7 diff)\u3002 */\nexport function langFor(path: string): LineLang | null {\n const dot = path.lastIndexOf('.')\n const ext = dot >= 0 ? path.slice(dot + 1).toLowerCase() : ''\n if (ext === '' || !HIGHLIGHT_EXTS.has(ext)) return null\n return { hashComment: HASH_COMMENT_EXTS.has(ext) }\n}\n\n/** \u662F\u5426\u6807\u8BC6\u7B26\u8D77\u59CB\u5B57\u7B26\u3002 */\nfunction isIdentStart(ch: string): boolean {\n return /[A-Za-z_]/.test(ch)\n}\n\n/** \u662F\u5426\u6807\u8BC6\u7B26\u540E\u7EED\u5B57\u7B26\u3002 */\nfunction isIdentPart(ch: string): boolean {\n return /[A-Za-z0-9_]/.test(ch)\n}\n\n/** \u662F\u5426\u6570\u5B57\u8D77\u59CB\u5B57\u7B26\u3002 */\nfunction isDigitStart(ch: string): boolean {\n return /[0-9]/.test(ch)\n}\n\n/** \u662F\u5426\u6570\u5B57\u540E\u7EED\u5B57\u7B26(\u542B\u5341\u516D\u8FDB\u5236/\u4E8C\u8FDB\u5236\u524D\u7F00\u4E0E\u5C0F\u6570\u70B9)\u3002 */\nfunction isDigitPart(ch: string): boolean {\n return /[0-9a-fA-FxXbBoO._]/.test(ch)\n}\n\n/**\n * \u5355\u884C tokenize:\u6309 \u5B57\u7B26\u4E32 \u2192 \u6CE8\u91CA \u2192 \u6570\u5B57 \u2192 \u6807\u8BC6\u7B26 \u7684\u987A\u5E8F\u626B\u63CF,\n * \u5176\u4F59\u5B57\u7B26\u5F52\u5165\u666E\u901A\u6587\u672C\u3002\u884C\u5185 token \u72B6\u6001\u4E0D\u8DE8\u884C(\u57FA\u7840\u7248\u53D6\u820D)\u3002\n */\nexport function tokenizeLine(text: string, lang: LineLang): Token[] {\n const tokens: Token[] = []\n let plain = ''\n const flushPlain = (): void => {\n if (plain !== '') {\n tokens.push({ text: plain, cls: null })\n plain = ''\n }\n }\n let i = 0\n while (i < text.length) {\n const ch = text[i]\n // \u5B57\u7B26\u4E32:\u4E09\u79CD\u5F15\u53F7,\u53CD\u659C\u6760\u8F6C\u4E49\u8DF3\u4E24\u683C;\u884C\u5C3E\u672A\u95ED\u5408\u5219\u5230\u884C\u5C3E\u3002\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n let j = i + 1\n while (j < text.length) {\n if (text[j] === '\\\\') {\n j += 2\n continue\n }\n if (text[j] === ch) break\n j += 1\n }\n flushPlain()\n tokens.push({ text: text.slice(i, Math.min(j + 1, text.length)), cls: 'tok-str' })\n i = Math.min(j + 1, text.length)\n continue\n }\n // \u884C\u6CE8\u91CA // \u6216 /* */(\u884C\u5185;\u672A\u95ED\u5408\u5230\u884C\u5C3E)\n if (ch === '/' && text[i + 1] === '/') {\n flushPlain()\n tokens.push({ text: text.slice(i), cls: 'tok-com' })\n break\n }\n if (ch === '/' && text[i + 1] === '*') {\n const close = text.indexOf('*/', i + 2)\n const end = close === -1 ? text.length : close + 2\n flushPlain()\n tokens.push({ text: text.slice(i, end), cls: 'tok-com' })\n i = end\n continue\n }\n if (lang.hashComment && ch === '#') {\n flushPlain()\n tokens.push({ text: text.slice(i), cls: 'tok-com' })\n break\n }\n // \u6570\u5B57(\u524D\u7F00\u5B57\u6BCD/\u5C0F\u6570\u70B9/\u4E0B\u5212\u7EBF\u90FD\u7B97\u6570\u5B57\u7247\u6BB5)\n if (isDigitStart(ch)) {\n let j = i + 1\n while (j < text.length && isDigitPart(text[j])) j += 1\n flushPlain()\n tokens.push({ text: text.slice(i, j), cls: 'tok-num' })\n i = j\n continue\n }\n // \u6807\u8BC6\u7B26 \u2192 \u547D\u4E2D\u5173\u952E\u5B57\u8868\u5219\u6807 tok-kw\n if (isIdentStart(ch)) {\n let j = i + 1\n while (j < text.length && isIdentPart(text[j])) j += 1\n const word = text.slice(i, j)\n flushPlain()\n tokens.push({ text: word, cls: KEYWORDS.has(word) ? 'tok-kw' : null })\n i = j\n continue\n }\n plain += ch\n i += 1\n }\n flushPlain()\n return tokens\n}\n\n/**\n * \u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u8303\u56F4:\u516C\u5171\u524D\u7F00/\u540E\u7F00\u5265\u79BB,\u4E2D\u95F4\u5269\u4F59\u90E8\u5206\u5373\u5DEE\u5F02\u6BB5\u3002\n * \u8986\u76D6\u300C\u4E00\u884C\u53EA\u6539\u51E0\u4E2A\u5B57\u7B26\u300D\u7684\u7CBE\u786E\u9AD8\u4EAE;\u6574\u884C\u91CD\u5199\u65F6\u4E2D\u95F4\u6BB5\u63A5\u8FD1\u6574\u884C,\n * \u6548\u679C\u4E0E\u6574\u884C\u9AD8\u4EAE\u4E00\u81F4(\u53EF\u63A5\u53D7)\u3002\u76F8\u540C\u6587\u672C\u8FD4\u56DE null\u3002\n */\nexport function diffMidRange(oldText: string, newText: string): DiffMidRange | null {\n const a = Array.from(oldText)\n const b = Array.from(newText)\n // \u516C\u5171\u524D\u7F00\n let pre = 0\n const minLen = Math.min(a.length, b.length)\n while (pre < minLen && a[pre] === b[pre]) pre += 1\n // \u516C\u5171\u540E\u7F00(\u4E0D\u56DE\u9000\u8FDB\u524D\u7F00\u533A)\n let oldEnd = a.length\n let newEnd = b.length\n while (oldEnd > pre && newEnd > pre && a[oldEnd - 1] === b[newEnd - 1]) {\n oldEnd -= 1\n newEnd -= 1\n }\n if (pre === a.length && pre === b.length) return null\n return { oldStart: pre, oldEnd, newStart: pre, newEnd }\n}\n", "/**\n * \u72EC\u7ACB Code Review \u6807\u7B7E\u9875\u7684 HTML \u6784\u5EFA(\u7EAF\u5B57\u7B26\u4E32,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u804A\u5929\u7A97\u53E3\u5185\u4E0D\u518D\u505A\u53CC\u5217\u8F6C\u6362 \u2014\u2014 \u53EA\u5728\u68C0\u6D4B\u5230 git diff \u7684\u5757\u4E0A\u52A0\u4E00\u4E2A\n * \u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE;\u70B9\u51FB\u540E\u5728\u72EC\u7ACB\u6D4F\u89C8\u5668\u6807\u7B7E\u9875(Blob URL,\u81EA\u5305\u542B)\n * \u6E32\u67D3\u5B8C\u6574\u53CC\u5217\u89C6\u56FE:\u5DE6\u4FA7\u6587\u4EF6\u5BFC\u822A\u3001\u5168\u5BBD\u3001\u6574\u9875\u6EDA\u52A8\u3001\u65E0\u9AD8\u5EA6\u9650\u5236\u3002\n *\n * \u5E03\u5C40(\u4E0E\u5B98\u65B9 DiffBlock \u540C\u6E90\u7684\u53CC\u5217\u8BED\u4E49):\n * - ctx \u4E0A\u4E0B\u6587\u884C:\u4E24\u4FA7\u540C\u6587\u3001\u65E0\u5E95\u8272;\n * - del \u5220\u9664\u884C:\u4EC5\u65E7\u4FA7,\u7EA2\u5E95;\n * - add \u65B0\u589E\u884C:\u4EC5\u65B0\u4FA7,\u7EFF\u5E95;\n * - change \u53D8\u66F4\u5BF9:\u65E7\u4FA7\u7EA2\u5E95 + \u65B0\u4FA7\u7EFF\u5E95(\u8FDE\u7EED - \u4E0E + \u7684\u914D\u5BF9,\u89C1 diff-parse);\n * - gap @@ hunk \u5934,\u6A2A\u8DE8\u56DB\u5217\u3002\n */\n\nimport type { DiffRow, FileDiff } from './diff-parse'\nimport { countStats, displayPath } from './diff-parse'\nimport { diffMidRange, langFor, tokenizeLine } from './diff-highlight'\nimport type { DiffMidRange, LineLang, Token } from './diff-highlight'\n\n/** HTML \u8F6C\u4E49:diff \u884C\u5185\u5BB9\u662F\u4E0D\u53EF\u4FE1\u6587\u672C,\u76F4\u63A5\u62FC HTML \u5FC5\u987B\u5148\u8F6C\u4E49\u3002 */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\n/** \u72EC\u7ACB\u9875\u7684 CSS:\u81EA\u5305\u542B,\u989C\u8272\u5168\u90E8\u8D70\u6CE8\u5165\u7684\u4E3B\u9898\u53D8\u91CF(\u4E0E DSH \u4E3B\u9898\u4E00\u81F4)\u3002 */\nconst STANDALONE_CSS = `\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 13px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n}\n.topbar {\n position: sticky;\n top: 0;\n z-index: 10;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 0 16px;\n background: var(--dsw-alias-markdown-code-block);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.stats { font-size: 13px; color: var(--dsw-alias-label-secondary); }\n.topbar-actions { display: flex; gap: 8px; }\n.btn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n background: transparent;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 2px 10px;\n cursor: pointer;\n font-family: inherit;\n}\n.btn:hover { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-border-l3); }\n.sidenav {\n position: fixed;\n left: 0;\n top: 36px;\n bottom: 0;\n width: 220px;\n overflow-y: auto;\n padding: 12px 8px;\n background: var(--dsw-alias-markdown-code-block);\n border-right: 1px solid var(--dsw-alias-border-l2);\n}\n.navitem {\n display: block;\n padding: 4px 12px;\n font-size: 12px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n text-decoration: none;\n border-radius: 6px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.navitem:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); }\nmain { margin-left: 220px; padding: 16px 24px 60px; }\n.file { margin-bottom: 24px; background: var(--dsw-alias-markdown-code-block); border-radius: 12px; overflow: hidden; }\n.filehead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 8px 12px 4px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n.badge {\n flex: none;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 0 6px;\n}\n.path { overflow-wrap: anywhere; }\n.meta { flex-basis: 100%; font-size: 11px; color: var(--dsw-alias-label-tertiary); }\n.dsh-cr-grid {\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);\n}\n.dsh-cr-row { display: contents; }\n.dsh-cr-num {\n padding: 0 10px 0 4px;\n text-align: right;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: nowrap;\n}\n.dsh-cr-cell {\n padding: 0 12px 0 4px;\n min-width: 0;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.dsh-cr-del .dsh-cr-num-old, .dsh-cr-del .dsh-cr-cell-old,\n.dsh-cr-change .dsh-cr-num-old, .dsh-cr-change .dsh-cr-cell-old {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n}\n.dsh-cr-add .dsh-cr-num-new, .dsh-cr-add .dsh-cr-cell-new,\n.dsh-cr-change .dsh-cr-num-new, .dsh-cr-change .dsh-cr-cell-new {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n}\n.dsh-cr-gaprow {\n grid-column: 1 / -1;\n padding: 2px 12px;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 4%, transparent);\n user-select: none;\n}\n.dsh-cr-nonl { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n/* \u8BED\u6CD5\u9AD8\u4EAE:\u989C\u8272\u590D\u7528\u5B98\u65B9 shiki \u53D8\u91CF(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4,\u53D6\u4E0D\u5230\u65F6\u515C\u5E95) */\n.tok-kw { color: var(--shiki-token-keyword, #7c3aed); }\n.tok-str { color: var(--shiki-token-string, #16a34a); }\n.tok-com { color: var(--shiki-token-comment, #9aa0a6); font-style: italic; }\n.tok-num { color: var(--shiki-token-constant, #d97706); }\n/* \u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02:change \u884C\u4E2D\u6BB5\u52A0\u6DF1\u80CC\u666F(\u65E7\u4FA7\u7EA2/\u65B0\u4FA7\u7EFF) */\n.dihl-mid-old {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 32%, transparent);\n border-radius: 2px;\n}\n.dihl-mid-new {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 32%, transparent);\n border-radius: 2px;\n}\n/* \u884C\u5185\u8BC4\u8BBA:\u884C\u53F7\u683C\u53EF\u70B9\u51FB,hover \u51FA\u73B0 + \u53F7 */\n.dsh-cr-num[data-cr-line] {\n cursor: pointer;\n position: relative;\n}\n.dsh-cr-num[data-cr-line]:hover {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cr-num[data-cr-line]:hover::after {\n content: '+';\n position: absolute;\n left: 2px;\n top: 0;\n color: var(--dsw-alias-state-business-primary);\n font-weight: 700;\n}\n.cc {\n margin-left: 5px;\n font-size: 10px;\n line-height: 15px;\n color: #fff;\n background: var(--dsw-alias-state-business-primary);\n border-radius: 8px;\n padding: 0 5px;\n user-select: none;\n}\n.comment-draft {\n grid-column: 1 / -1;\n padding: 8px 12px;\n background: var(--dsw-alias-bg-layer-1);\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.comment-list { margin-bottom: 8px; }\n.comment-item {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n margin-bottom: 4px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.comment-input {\n width: 100%;\n min-height: 60px;\n resize: vertical;\n box-sizing: border-box;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n}\n.comment-actions { display: flex; gap: 8px; margin-top: 8px; }\n.submit-panel {\n position: fixed;\n top: 44px;\n right: 16px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n}\n.submit-hint { font-size: 12px; color: var(--dsw-alias-label-secondary); }\n.rawwrap { margin: 16px 24px; }\n.rawsummary { font-size: 12px; color: var(--dsw-alias-label-secondary); cursor: pointer; }\n.rawpre { white-space: pre-wrap; word-break: break-word; font: inherit; margin: 8px 0 0; }\n`\n\n/** \u53D8\u66F4\u7C7B\u578B\u7684\u5FBD\u7AE0\u6587\u6848(\u4E2D\u6587\u9ED8\u8BA4,\u7531 CR_TEXTS \u8986\u76D6)\u3002 */\nconst STANDALONE_STATUS_LABEL: Record<FileDiff['status'], string> = {\n modified: '\u4FEE\u6539',\n added: '\u65B0\u589E\u6587\u4EF6',\n deleted: '\u5220\u9664\u6587\u4EF6',\n renamed: '\u91CD\u547D\u540D',\n binary: '\u4E8C\u8FDB\u5236',\n mode: '\u6743\u9650\u53D8\u66F4',\n}\n\n/** \u754C\u9762\u8BED\u8A00(DSH \u9875\u9762 lang \u4EE5 zh \u5F00\u5934\u7B97\u4E2D\u6587,\u5176\u4F59\u4E00\u5F8B\u82F1\u6587)\u3002 */\nexport type Lang = 'zh' | 'en'\n\n/**\n * \u5168\u90E8\u7528\u6237\u53EF\u89C1\u6587\u6848\u7684\u4E2D/\u82F1\u8BCD\u5178\u3002\u804A\u5929\u5185\u6309\u94AE\u3001\u72EC\u7ACB\u6807\u7B7E\u9875\u3001\u8BC4\u8BBA\u62A5\u544A\n * \u4E09\u5904\u5171\u7528;\u72EC\u7ACB\u6807\u7B7E\u9875\u8131\u79BB DSH \u9875\u9762\u62FF\u4E0D\u5230\u5B98\u65B9 locale,\u7531\u751F\u6210\u65F6\u628A\n * \u8BED\u8A00\u8FDE\u540C\u8BCD\u5178\u4E00\u8D77\u4F20\u5165\u3002\u51FD\u6570\u5B57\u6BB5\u4EC5\u4F9B TS \u4FA7\u6C42\u503C,\u6CE8\u5165\u6807\u7B7E\u9875\u811A\u672C\u65F6\n * \u53EA\u6311\u5B57\u7B26\u4E32\u5B57\u6BB5(JSON \u5E8F\u5217\u5316\u4F1A\u4E22\u51FD\u6570)\u3002\n */\nexport interface CrTexts {\n // \u2014\u2014 \u804A\u5929\u5185\u6309\u94AE \u2014\u2014\n openTab: string\n openTabTitle: string\n expand: string\n collapse: string\n expandTitle: string\n collapseTitle: string\n // \u2014\u2014 \u8BC4\u8BBA\u62A5\u544A(\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846)\u2014\u2014\n reportLgtmHeader: (count: number) => string\n reportCommentHeader: (count: number) => string\n // \u2014\u2014 \u72EC\u7ACB\u6807\u7B7E\u9875 \u2014\u2014\n stats: (added: number, removed: number, files: number) => string\n submitComments: string\n copyRaw: string\n copied: string\n sent: string\n submitHint: string\n submitLgtm: string\n submitPlain: string\n rawSummary: string\n commentPlaceholder: string\n addComment: string\n cancel: string\n noNewlineTitle: string\n openerClosed: string\n noSession: string\n status: Record<FileDiff['status'], string>\n}\n\nexport const CR_TEXTS: Record<Lang, CrTexts> = {\n zh: {\n openTab: '\u29C9 \u65B0\u6807\u7B7E\u9875\u6253\u5F00',\n openTabTitle: '\u5728\u72EC\u7ACB\u7684\u6D4F\u89C8\u5668\u6807\u7B7E\u9875\u4E2D\u6253\u5F00 Code Review \u89C6\u56FE',\n expand: '\u5C55\u5F00',\n collapse: '\u6536\u8D77',\n expandTitle: '\u5C55\u5F00\u539F\u59CB diff \u6587\u672C',\n collapseTitle: '\u6536\u8D77\u539F\u59CB diff \u6587\u672C',\n reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review \u5171 ${count} \u6761\u8BC4\u8BBA:`,\n reportCommentHeader: (count) => `Code Review \u610F\u89C1,\u5171 ${count} \u6761:`,\n stats: (added, removed, files) => `+${added} \u2212${removed} \u00B7 ${files} \u4E2A\u6587\u4EF6`,\n submitComments: '\u63D0\u4EA4\u8BC4\u8BBA',\n copyRaw: '\u590D\u5236\u539F\u59CB diff',\n copied: '\u5DF2\u590D\u5236',\n sent: '\u5DF2\u53D1\u9001 \u2713',\n submitHint: '\u628A\u5168\u90E8\u8BC4\u8BBA\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846:',\n submitLgtm: 'Looks Good To Me \u2713',\n submitPlain: '\u4EC5\u63D0\u4EA4\u8BC4\u8BBA',\n rawSummary: '\u539F\u59CB diff \u6587\u672C',\n commentPlaceholder: '\u5728\u6B64\u884C\u6DFB\u52A0\u8BC4\u8BBA\u2026',\n addComment: '\u6DFB\u52A0\u8BC4\u8BBA',\n cancel: '\u53D6\u6D88',\n noNewlineTitle: '\u8BE5\u4FA7\u6587\u4EF6\u672B\u5C3E\u65E0\u6362\u884C\u7B26',\n openerClosed: '\u539F\u6765\u7684 DSH \u9875\u9762\u5DF2\u5173\u95ED,\u65E0\u6CD5\u56DE\u4F20\u8BC4\u8BBA',\n noSession: '\u672A\u80FD\u5199\u5165 DSH \u5BF9\u8BDD\u6846:\u5F53\u524D\u6CA1\u6709\u6253\u5F00\u4E2D\u7684\u4F1A\u8BDD',\n status: STANDALONE_STATUS_LABEL,\n },\n en: {\n openTab: '\u29C9 Open in new tab',\n openTabTitle: 'Open the code review view in a separate browser tab',\n expand: 'Expand',\n collapse: 'Collapse',\n expandTitle: 'Show raw diff text',\n collapseTitle: 'Hide raw diff text',\n reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review with ${count} comment${count === 1 ? '' : 's'}:`,\n reportCommentHeader: (count) => `Code Review comments (${count}):`,\n stats: (added, removed, files) => `+${added} \u2212${removed} \u00B7 ${files} file${files === 1 ? '' : 's'}`,\n submitComments: 'Submit comments',\n copyRaw: 'Copy raw diff',\n copied: 'Copied',\n sent: 'Sent \u2713',\n submitHint: 'Submit all comments to the DSH composer:',\n submitLgtm: 'Looks Good To Me \u2713',\n submitPlain: 'Comments only',\n rawSummary: 'Raw diff text',\n commentPlaceholder: 'Add a comment on this line\u2026',\n addComment: 'Add comment',\n cancel: 'Cancel',\n noNewlineTitle: 'No newline at end of file',\n openerClosed: 'The original DSH page was closed; comments cannot be sent back',\n noSession: 'Could not write to the DSH composer: no open session',\n status: {\n modified: 'modified',\n added: 'new file',\n deleted: 'deleted',\n renamed: 'renamed',\n binary: 'binary',\n mode: 'mode change',\n },\n },\n}\n\n/** \u53EA\u6311\u5B57\u7B26\u4E32\u5B57\u6BB5,\u5E8F\u5217\u5316\u8FDB\u72EC\u7ACB\u9875\u811A\u672C(JSON \u4F1A\u4E22\u51FD\u6570\u5B57\u6BB5)\u3002 */\nfunction stringTexts(texts: CrTexts): Record<string, string> {\n return {\n submitComments: texts.submitComments,\n copyRaw: texts.copyRaw,\n copied: texts.copied,\n sent: texts.sent,\n submitHint: texts.submitHint,\n submitLgtm: texts.submitLgtm,\n submitPlain: texts.submitPlain,\n rawSummary: texts.rawSummary,\n commentPlaceholder: texts.commentPlaceholder,\n addComment: texts.addComment,\n cancel: texts.cancel,\n noNewlineTitle: texts.noNewlineTitle,\n openerClosed: texts.openerClosed,\n noSession: texts.noSession,\n }\n}\n\n/** \u6807\u7B7E\u9875\u56DE\u4F20\u7684\u4E00\u6761\u884C\u5185\u8BC4\u8BBA(\u7ECF postMessage,\u5F62\u72B6\u7531\u63D2\u4EF6\u4FA7\u6821\u9A8C)\u3002 */\nexport interface ReviewComment {\n path: string\n lineNo: number\n side?: string\n text: string\n}\n\n/**\n * \u7EC4\u88C5\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846\u7684 Code Review \u62A5\u544A\u6587\u672C(\u7EAF\u51FD\u6570,\u5355\u6D4B\u8986\u76D6)\u3002\n * \u683C\u5F0F:\u6587\u4EF6\u540D:\u884C\u53F7 \u2014 \u8BC4\u8BBA,\u9010\u6761\u7F16\u53F7;LGTM \u65F6\u5934\u90E8\u52A0\u6279\u51C6\u58F0\u660E\u3002\n * \u8BC4\u8BBA\u6587\u672C\u542B\u6362\u884C\u65F6,\u7EED\u884C\u7F29\u8FDB\u4E09\u683C\u4E0E\u6761\u76EE\u5185\u5BB9\u5BF9\u9F50,\u4E0D\u7834\u574F\u7F16\u53F7\u7ED3\u6784\u3002\n * @param comments \u884C\u5185\u8BC4\u8BBA\u5217\u8868\u3002\n * @param kind \u63D0\u4EA4\u7C7B\u578B:lgtm(\u6279\u51C6)/ comment(\u7EAF\u8BC4\u8BBA)\u3002\n * @param lang \u62A5\u544A\u8BED\u8A00(\u9ED8\u8BA4\u4E2D\u6587)\u3002\n */\nexport function buildCommentReport(\n comments: readonly ReviewComment[],\n kind: 'lgtm' | 'comment',\n lang: Lang = 'zh',\n): string {\n const texts = CR_TEXTS[lang]\n const header = kind === 'lgtm' ? texts.reportLgtmHeader(comments.length) : texts.reportCommentHeader(comments.length)\n const items = comments.map((comment, index) => {\n const lines = comment.text.split('\\n')\n const first = `${index + 1}. ${comment.path}:${comment.lineNo} \u2014 ${lines[0]}`\n // \u7EED\u884C\u7F29\u8FDB\u4E09\u683C(\u4E0E \"N. \" \u524D\u7F00\u7B49\u5BBD),\u907F\u514D\u4E0E\u4E0B\u4E00\u6761\u7F16\u53F7\u6DF7\u6DC6\u3002\n const rest = lines.slice(1).map((line) => ` ${line}`)\n return [first, ...rest].join('\\n')\n })\n return [header, '', ...items].join('\\n')\n}\n\n/** \u628A token \u5E8F\u5217\u6E32\u67D3\u6210 HTML(\u8F6C\u4E49\u5728 token \u7EA7\u505A,\u4FDD\u8BC1\u5B89\u5168)\u3002 */\nfunction tokensToHtml(tokens: Token[]): string {\n let out = ''\n for (const token of tokens) {\n const escaped = escapeHtml(token.text)\n out += token.cls === null ? escaped : `<span class=\"${token.cls}\">${escaped}</span>`\n }\n return out\n}\n\n/**\n * \u6E32\u67D3\u4E00\u4E2A\u5185\u5BB9\u683C:\u57FA\u7840\u8BED\u6CD5\u9AD8\u4EAE + \u53EF\u9009\u7684\u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u9AD8\u4EAE\u3002\n * @param text \u5185\u5BB9\u6587\u672C(\u53EF\u80FD\u4E3A\u7A7A)\u3002\n * @param side \u65E7/\u65B0\u4FA7(\u51B3\u5B9A\u4E2D\u6BB5\u80CC\u666F\u7528\u7EA2\u8FD8\u662F\u7EFF)\u3002\n * @param noEol \u672B\u5C3E\u65E0\u6362\u884C\u6807\u8BB0\u3002\n * @param diffRange \u884C\u5185\u5DEE\u5F02\u8303\u56F4(\u4EC5 change \u884C;null = \u4E0D\u505A\u5B57\u7B26\u7EA7\u9AD8\u4EAE)\u3002\n * @param lang \u8BED\u6CD5\u9AD8\u4EAE\u914D\u7F6E(\u65E0\u6CD5\u8BC6\u522B\u8BED\u8A00\u65F6 null)\u3002\n */\nfunction cellHtml(\n text: string | undefined,\n side: 'old' | 'new',\n noEol: boolean | undefined,\n diffRange: DiffMidRange | null,\n lang: LineLang | null,\n noNewlineTitle: string,\n): string {\n if (text === undefined) return `<div class=\"dsh-cr-cell dsh-cr-cell-${side}\"></div>`\n const mark = noEol === true ? `<span class=\"dsh-cr-nonl\" title=\"${escapeHtml(noNewlineTitle)}\">\u23CE</span>` : ''\n let body: string\n if (diffRange === null) {\n // \u65E0\u5B57\u7B26\u7EA7\u9AD8\u4EAE:\u6574\u884C\u4E00\u4E2A\u8BED\u6CD5\u6BB5\u3002\n body = lang === null ? escapeHtml(text) : tokensToHtml(tokenizeLine(text, lang))\n } else {\n // \u6309 code point \u5207\u524D/\u4E2D/\u540E\u4E09\u6BB5(\u7D22\u5F15\u6765\u81EA diffMidRange \u7684\n // Array.from \u8BA1\u7B97,\u5207\u7247\u540C\u6837\u6309 code point,\u4EE3\u7406\u5BF9\u4E0D\u4F1A\u65AD\u88C2)\u3002\n const chars = Array.from(text)\n const start = side === 'old' ? diffRange.oldStart : diffRange.newStart\n const end = side === 'old' ? diffRange.oldEnd : diffRange.newEnd\n const prefix = chars.slice(0, start).join('')\n const mid = chars.slice(start, end).join('')\n const suffix = chars.slice(end).join('')\n const seg = (segText: string): string =>\n lang === null ? escapeHtml(segText) : tokensToHtml(tokenizeLine(segText, lang))\n body = seg(prefix)\n + (mid !== '' ? `<span class=\"dihl-mid-${side}\">${seg(mid)}</span>` : '')\n + seg(suffix)\n }\n return `<div class=\"dsh-cr-cell dsh-cr-cell-${side}\">${body}${mark}</div>`\n}\n\n/** \u6E32\u67D3\u4E00\u884C(\u53CC\u5217 4 \u683C\u7ED3\u6784,\u76F4\u63A5\u4EA7 HTML \u5B57\u7B26\u4E32)\u3002 */\nfunction rowHtml(row: DiffRow, lang: LineLang | null, path: string, noNewlineTitle: string): string {\n if (row.kind === 'gap') {\n return `<div class=\"dsh-cr-gaprow\">${escapeHtml(row.gapText ?? '')}</div>`\n }\n // \u884C\u53F7\u683C\u5E26\u8BC4\u8BBA\u5B9A\u4F4D\u4FE1\u606F(\u6587\u4EF6\u8DEF\u5F84/\u884C\u53F7/\u4FA7\u522B),\u6807\u7B7E\u9875\u811A\u672C\u636E\u6B64\u6302\u884C\u5185\u8BC4\u8BBA\u3002\n const num = (no: number | undefined, side: string): string => {\n if (no === undefined) return `<div class=\"dsh-cr-num dsh-cr-num-${side}\"></div>`\n return `<div class=\"dsh-cr-num dsh-cr-num-${side}\" data-cr-path=\"${escapeHtml(path)}\" data-cr-line=\"${no}\" data-cr-side=\"${side}\">${no}</div>`\n }\n const line = `<div class=\"dsh-cr-row dsh-cr-${row.kind}\">`\n if (row.kind === 'ctx') {\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, null, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, null, lang, noNewlineTitle) + '</div>'\n }\n if (row.kind === 'del') {\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, null, lang, noNewlineTitle)\n + num(undefined, 'new') + cellHtml(undefined, 'new', undefined, null, lang, noNewlineTitle) + '</div>'\n }\n if (row.kind === 'add') {\n return line + num(undefined, 'old') + cellHtml(undefined, 'old', undefined, null, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, null, lang, noNewlineTitle) + '</div>'\n }\n // change \u884C:\u7B97\u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u8303\u56F4,\u4E24\u4FA7\u4E2D\u6BB5\u5404\u81EA\u52A0\u6DF1\u80CC\u666F\u3002\n const range = diffMidRange(row.oldText ?? '', row.newText ?? '')\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, range, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, range, lang, noNewlineTitle) + '</div>'\n}\n\n/**\n * \u6784\u5EFA\u72EC\u7ACB\u6807\u7B7E\u9875\u7684\u5B8C\u6574 HTML(\u81EA\u5305\u542B:\u6837\u5F0F + \u4E3B\u9898\u53D8\u91CF + \u53CC\u5217\u5185\u5BB9)\u3002\n * \u5FC5\u987B\u7531\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE\u7684\u70B9\u51FB\u5904\u7406\u5668\u5728\u7528\u6237\u624B\u52BF\u5185\u540C\u6B65\u8C03\u7528\n * (window.open \u624D\u4E0D\u4F1A\u88AB\u6D4F\u89C8\u5668\u62E6\u622A)\u3002\n * @param files \u89E3\u6790\u7ED3\u679C\u3002\n * @param theme \u4ECE\u5BBF\u4E3B\u9875\u9762\u6536\u96C6\u7684 DSW \u4E3B\u9898\u53D8\u91CF(name \u2192 value)\u3002\n * @param rawText \u539F\u59CB diff \u6587\u672C(\u590D\u5236/\u4FDD\u5E95\u67E5\u770B\u7528)\u3002\n */\nexport function buildStandaloneHtml(\n files: FileDiff[],\n theme: Record<string, string>,\n rawText: string,\n lang: Lang = 'zh',\n): string {\n const texts = CR_TEXTS[lang]\n const stats = countStats(files)\n const statsText = texts.stats(stats.added, stats.removed, stats.files)\n const rootVars = Object.entries(theme)\n .map(([name, value]) => `${name}: ${value};`)\n .join(' ')\n // \u539F\u59CB\u6587\u672C\u8981\u5185\u5D4C\u8FDB <script> \u7684 JSON \u5B57\u7B26\u4E32:JSON.stringify \u4E0D\u8F6C\u4E49 `<`,\n // diff \u5185\u5BB9\u91CC\u4E00\u65E6\u51FA\u73B0 `</script>` \u4F1A\u63D0\u524D\u7EC8\u6B62\u6807\u7B7E(\u7ECF\u5178\u89E3\u6790\u9677\u9631),\n // \u7EDF\u4E00\u628A `<` \u5199\u6210 \\u003c(JSON \u8BED\u4E49\u7B49\u4EF7,\u89E3\u6790\u5B89\u5168)\u3002\n const rawJson = JSON.stringify(rawText).replace(/</g, '\\\\u003c')\n const nav = files\n .map((file, index) => `<a class=\"navitem\" href=\"#file-${index}\">${escapeHtml(displayPath(file.newPath) || displayPath(file.oldPath))}</a>`)\n .join('')\n const sections = files.map((file, index) => {\n const oldPath = displayPath(file.oldPath)\n const newPath = displayPath(file.newPath)\n const path = oldPath !== '' && oldPath !== newPath ? `${oldPath} \u2192 ${newPath}` : (newPath === '' ? oldPath : newPath)\n const meta = file.meta.filter((m) => !m.startsWith('index '))\n // \u8BED\u8A00\u63A8\u65AD:\u65B0\u8DEF\u5F84\u4F18\u5148(\u65B0\u589E\u6587\u4EF6\u6CA1\u6709\u65E7\u8DEF\u5F84);/dev/null \u65F6\u56DE\u9000\u65E7\u8DEF\u5F84\u3002\n const lang = langFor(newPath) ?? langFor(oldPath)\n const commentPath = newPath !== '' && newPath !== '/dev/null' ? newPath : oldPath\n const rows = file.rows.length === 0\n ? `<div class=\"dsh-cr-gaprow\">${escapeHtml(file.meta.join(' \u00B7 ') || file.status)}</div>`\n : file.rows.map((row) => rowHtml(row, lang, commentPath, texts.noNewlineTitle)).join('')\n const head = `<header class=\"filehead\"><span class=\"badge\">${texts.status[file.status]}</span><span class=\"path\">${escapeHtml(path)}</span>`\n + (meta.length > 0 ? `<span class=\"meta\">${escapeHtml(meta.join(' \u00B7 '))}</span>` : '')\n + '</header>'\n return `<section class=\"file\" id=\"file-${index}\">${head}<div class=\"dsh-cr-grid\">${rows}</div></section>`\n }).join('')\n return `<!doctype html>\n<html lang=\"${lang === 'zh' ? 'zh-CN' : 'en'}\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Code Review \u00B7 ${statsText}</title>\n<style>\n:root { ${rootVars} }\n${STANDALONE_CSS}\n</style>\n</head>\n<body>\n<header class=\"topbar\">\n <span class=\"stats\">${statsText}</span>\n <span class=\"topbar-actions\">\n <button class=\"btn\" id=\"submit-comments\" type=\"button\">${texts.submitComments}</button>\n <button class=\"btn\" id=\"copy-raw\" type=\"button\">${texts.copyRaw}</button>\n </span>\n</header>\n<div class=\"submit-panel\" id=\"submit-panel\" hidden>\n <span class=\"submit-hint\">${texts.submitHint}</span>\n <button class=\"btn\" id=\"submit-lgtm\" type=\"button\">${texts.submitLgtm}</button>\n <button class=\"btn\" id=\"submit-plain\" type=\"button\">${texts.submitPlain}</button>\n</div>\n<nav class=\"sidenav\">${nav}</nav>\n<main>${sections}</main>\n<div class=\"rawwrap\">\n <details><summary class=\"rawsummary\">${texts.rawSummary}</summary>\n <pre class=\"rawpre\">${escapeHtml(rawText)}</pre>\n </details>\n</div>\n<script>\ndocument.getElementById('copy-raw').addEventListener('click', function (event) {\n var raw = ${rawJson}\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(raw).then(function () {\n var btn = event.currentTarget\n btn.textContent = T.copied\n setTimeout(function () { btn.textContent = T.copyRaw }, 1200)\n })\n }\n})\n</script>\n<script>\n/* \u884C\u5185\u8BC4\u8BBA:\u70B9\u51FB\u884C\u53F7\u683C\u63D0\u8BC4\u8BBA,\u6512\u9F50\u540E\u63D0\u4EA4\u56DE DSH \u5BF9\u8BDD\u6846(postMessage)\u3002 */\n(function () {\n var T = ${JSON.stringify(stringTexts(texts))}\n var comments = []\n var draft = null\n var submitBtn = document.getElementById('submit-comments')\n var panel = document.getElementById('submit-panel')\n\n function esc(text) {\n return String(text)\n .replace(/&/g, '&amp;').replace(/</g, '&lt;')\n .replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n }\n function syncSubmit() {\n if (!submitBtn) return\n submitBtn.textContent = comments.length === 0 ? T.submitComments : T.submitComments + ' (' + comments.length + ')'\n }\n function sameLine(a, b) {\n return a.path === b.path && a.lineNo === b.lineNo && a.side === b.side\n }\n function lineOf(num) {\n return {\n path: num.getAttribute('data-cr-path'),\n lineNo: Number(num.getAttribute('data-cr-line')),\n side: num.getAttribute('data-cr-side'),\n }\n }\n function commentsOn(line) {\n return comments.filter(function (c) { return sameLine(c, line) })\n }\n function closeDraft() {\n if (draft && draft.parentNode) draft.parentNode.removeChild(draft)\n draft = null\n }\n function findNum(line) {\n var nums = document.querySelectorAll('.dsh-cr-num[data-cr-line]')\n for (var i = 0; i < nums.length; i += 1) {\n if (sameLine(lineOf(nums[i]), line)) return nums[i]\n }\n return null\n }\n function updateBadge(num) {\n var badge = num.querySelector('.cc')\n if (badge) badge.parentNode.removeChild(badge)\n var n = commentsOn(lineOf(num)).length\n if (n > 0) {\n var span = document.createElement('span')\n span.className = 'cc'\n span.textContent = String(n)\n num.appendChild(span)\n }\n }\n function openDraft(num) {\n closeDraft()\n var line = lineOf(num)\n var existing = commentsOn(line)\n draft = document.createElement('div')\n draft.className = 'comment-draft'\n var html = ''\n if (existing.length > 0) {\n html += '<div class=\"comment-list\">'\n existing.forEach(function (c) { html += '<div class=\"comment-item\">' + esc(c.text) + '</div>' })\n html += '</div>'\n }\n html += '<textarea class=\"comment-input\" placeholder=\"' + T.commentPlaceholder + '\"></textarea>'\n html += '<div class=\"comment-actions\">'\n + '<button class=\"btn\" type=\"button\" data-act=\"save\">' + T.addComment + '</button>'\n + '<button class=\"btn\" type=\"button\" data-act=\"cancel\">' + T.cancel + '</button>'\n + '</div>'\n draft.innerHTML = html\n num.parentElement.insertAdjacentElement('afterend', draft)\n var ta = draft.querySelector('.comment-input')\n ta.focus()\n ta.addEventListener('keydown', function (event) {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) saveDraft(line)\n })\n draft.querySelector('[data-act=\"save\"]').addEventListener('click', function () { saveDraft(line) })\n draft.querySelector('[data-act=\"cancel\"]').addEventListener('click', closeDraft)\n }\n function saveDraft(line) {\n if (!draft) return\n var ta = draft.querySelector('.comment-input')\n var text = ta.value.trim()\n closeDraft()\n if (text === '') return\n comments.push({ path: line.path, lineNo: line.lineNo, side: line.side, text: text })\n var num = findNum(line)\n if (num) updateBadge(num)\n syncSubmit()\n }\n function send(kind) {\n if (comments.length === 0) return\n var target = window.opener\n if (!target) {\n alert(T.openerClosed)\n return\n }\n target.postMessage({\n type: 'dsh-code-review-comments',\n kind: kind,\n comments: comments.map(function (c) {\n return { path: c.path, lineNo: c.lineNo, side: c.side, text: c.text }\n }),\n }, '*')\n panel.hidden = true\n }\n document.addEventListener('click', function (event) {\n var target = event.target\n var num = target && target.closest ? target.closest('.dsh-cr-num[data-cr-line]') : null\n if (num) {\n openDraft(num)\n return\n }\n if (draft && !draft.contains(target)) closeDraft()\n if (panel && target && target.closest && !panel.contains(target) && !target.closest('#submit-comments')) {\n panel.hidden = true\n }\n })\n submitBtn.addEventListener('click', function () {\n if (comments.length === 0) return\n panel.hidden = !panel.hidden\n })\n document.getElementById('submit-lgtm').addEventListener('click', function () { send('lgtm') })\n document.getElementById('submit-plain').addEventListener('click', function () { send('comment') })\n window.addEventListener('message', function (event) {\n if (event.source !== window.opener) return\n var data = event.data\n if (!data || data.type !== 'dsh-code-review-ack') return\n if (data.ok === false) {\n alert(T.noSession)\n } else {\n submitBtn.textContent = T.sent\n setTimeout(syncSubmit, 3000)\n }\n })\n syncSubmit()\n})()\n</script>\n</body>\n</html>\n`\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @yangzhe1991/dsh-code-review \u63D2\u4EF6,\u6D4F\u89C8\u5668\u534A \u2014\u2014 Git Diff \u72EC\u7ACB\u5BA1\u67E5\u6807\u7B7E\u9875\u3002\n *\n * \u4EA4\u4E92:\u804A\u5929\u7A97\u53E3\u5185\u4FDD\u6301\u5B98\u65B9\u539F\u59CB\u663E\u793A(\u4E0D\u5C55\u5F00\u3001\u4E0D\u8F6C\u6362),\u68C0\u6D4B\u5230 git diff\n * \u8F93\u51FA\u7684\u5757\u4E0A\u81EA\u52A8\u52A0\u4E00\u4E2A\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE;\u70B9\u51FB\u540E\u5728\u72EC\u7ACB\u6D4F\u89C8\u5668\u6807\u7B7E\n * \u9875\u6E32\u67D3\u5B8C\u6574\u53CC\u5217 Code Review \u89C6\u56FE(\u89C1 diff-view.ts)\u3002\u5F39\u7A97\u5FC5\u987B\u7531\u7528\u6237\n * \u624B\u52BF\u89E6\u53D1,\u6240\u4EE5\u6807\u7B7E\u9875\u53EA\u80FD\u505A\u6210\u6309\u94AE\u5F62\u6001\u3002\n *\n * \u4E24\u4E2A\u6309\u94AE\u6CE8\u5165\u6765\u6E90(\u4E92\u8865,\u8986\u76D6 diff \u51FA\u73B0\u7684\u4E24\u4E2A\u9762):\n * 1. DOM \u63A2\u6D4B:markdown \u4EE3\u7801\u5757(.md-code-block,```diff/```patch \u6216\u5185\u5BB9\n * \u5F62\u4F3C diff)\u4E0E\u8BE6\u60C5\u9762\u677F\u91CC\u7684\u7EC8\u7AEF\u5361\u7247([data-terminal])\u3002\u6D41\u5F0F\u671F\u95F4\u5185\u5BB9\n * \u6301\u7EED\u53D8\u5316,\u53BB\u6296\u7A33\u5B9A\u540E\u624D\u52A0\u6309\u94AE;\u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff \u65F6\u79FB\u9664\u6309\u94AE\u3002\n * 2. \u6570\u636E\u5C42:bash \u5DE5\u5177\u7ED3\u679C\u8F93\u51FA\u5F62\u4F3C diff \u65F6,\u6298\u53E0\u7684\u5DE5\u5177\u884C\u5728 DOM \u91CC\u6CA1\u6709\n * \u8F93\u51FA\u5185\u5BB9(\u968F\u5C55\u5F00\u624D\u6302\u8F7D),\u7EAF DOM \u63A2\u6D4B\u770B\u4E0D\u5230 \u2014\u2014 \u7531 session \u4F5C\u7528\u57DF\n * \u7EC4\u4EF6\u8BA2\u9605 chat \u5FEB\u7167,\u628A\u7ED3\u679C\u6587\u672C\u6309\u8282\u70B9 key \u5B58\u8868,\u626B\u63CF\u5668\u7ED9\u5BF9\u5E94\u7684\n * \u5DE5\u5177\u884C\u672C\u4F53([data-sample=\"bash\"])\u52A0\u6309\u94AE\u3002\u5DE5\u5177\u884C\u5185\u7684\u7EC8\u7AEF\u5361\u7247\u63A2\u6D4B\n * \u4F1A\u8DF3\u8FC7(\u907F\u514D\u540C\u4E00\u5757\u51FA\u73B0\u4E24\u4E2A\u6309\u94AE)\u3002\n */\nimport { useEffect } from 'react'\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// \u89E6\u53D1 SlotMap \u58F0\u660E\u5408\u5E76:shell.overlay \u7531 layout\u3001header.actions \u7531 conversation \u58F0\u660E\u3002\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// \u58F0\u660E\u5408\u5E76:\u5B98\u65B9 locale \u670D\u52A1\u6302\u8F7D\u5728 cordis Context \u4E0A(\u7531 dsh-client-locale\n// \u63D0\u4F9B;\u7C7B\u578B\u4E3A unknown,\u8FD0\u884C\u65F6\u505A\u5F62\u72B6\u6821\u9A8C \u2014\u2014 \u4E0D\u76F4\u63A5\u4F9D\u8D56\u8BE5\u5305\u7684\u7C7B\u578B)\u3002\nimport type {} from '@deepseek-ai/cordis'\nimport { isDiffText, parseGitDiff } from './diff-parse'\nimport { buildCommentReport, buildStandaloneHtml, CR_TEXTS } from './diff-view'\nimport type { Lang, ReviewComment } from './diff-view'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /** \u5B98\u65B9 locale \u670D\u52A1(\u53EF\u9009:\u5B98\u65B9\u88C5\u914D\u4E0D\u542B locale \u65F6\u4E3A undefined)\u3002 */\n locale?: unknown\n }\n}\n\n// \u2014\u2014 \u53EF\u8C03\u53C2\u6570 \u2014\u2014\n\n/** \u7A33\u5B9A\u53BB\u6296\u7A97\u53E3:\u5185\u5BB9\u5728\u8FD9\u4E2A\u7A97\u53E3\u5185\u4E0D\u518D\u53D8\u5316\u624D\u52A0\u6309\u94AE(\u6D41\u5F0F\u671F\u95F4\u4E0D\u65AD\u91CD\u7F6E)\u3002 */\nconst STABLE_MS = 1000\n/** \u5168\u91CF\u626B\u63CF\u53BB\u6296(MutationObserver \u89E6\u53D1\u9891\u7E41,\u5408\u5E76\u6210\u4E00\u6B21\u626B\u63CF)\u3002 */\nconst SCAN_DEBOUNCE = 300\n/** \u89E3\u6790\u5931\u8D25\u540E\u7684\u91CD\u8BD5\u95F4\u9694(\u7578\u5F62 diff \u5185\u5BB9\u7A33\u5B9A\u540E\u53EF\u80FD\u53D8\u5408\u6CD5)\u3002 */\nconst RETRY_MS = 3000\n/** \u515C\u5E95\u5168\u91CF\u626B\u63CF\u95F4\u9694:\u8986\u76D6\u4EFB\u4F55\u6F0F\u6389\u7684 mutation \u8DEF\u5F84(\u7406\u8BBA\u4E0D\u8BE5\u6709)\u3002 */\nconst SAFETY_SCAN_MS = 10000\n\n/** \u6CE8\u5165\u7684 <style> \u662F\u5426\u5DF2\u5B58\u5728,\u907F\u514D\u91CD\u590D\u6CE8\u5165\u3002 */\nlet styleInjected = false\n\n// \u2014\u2014 \u6309\u94AE\u6837\u5F0F(\u5B98\u65B9\u8BED\u4E49\u53D8\u91CF,\u6DF1\u6D45\u4E3B\u9898\u81EA\u52A8\u9002\u914D)\u2014\u2014\n\nconst BUTTON_CSS = `\n.dsh-cr-openbtn {\n margin-left: auto;\n flex: none;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n background: transparent;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 1px 10px;\n cursor: pointer;\n white-space: nowrap;\n}\n.dsh-cr-openbtn:hover {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-border-l3);\n}\n`\n\n/** \u5168\u5C40\u6CE8\u5165\u4E00\u6B21\u6309\u94AE\u6837\u5F0F(\u6D4F\u89C8\u5668\u7AEF bundle \u7684\u6A21\u5757\u7EA7\u526F\u4F5C\u7528)\u3002 */\nfunction ensureStyle(): void {\n if (styleInjected || typeof document === 'undefined') return\n styleInjected = true\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-code-review'\n tag.textContent = BUTTON_CSS\n document.head.appendChild(tag)\n}\n\n// \u2014\u2014 \u72EC\u7ACB\u6807\u7B7E\u9875\u6253\u5F00 \u2014\u2014\n\n/** \u72EC\u7ACB\u6807\u7B7E\u9875\u9700\u8981\u7EE7\u627F\u7684 DSW \u4E3B\u9898\u53D8\u91CF(\u53D6\u4E0D\u5230\u65F6\u7ED9\u6D45\u8272\u8FD1\u4F3C\u503C\u515C\u5E95)\u3002 */\nconst THEME_VAR_DEFAULTS: Record<string, string> = {\n '--dsw-alias-bg-base': '#ffffff',\n '--dsw-alias-bg-layer-1': '#f7f7f8',\n '--dsw-alias-markdown-code-block': '#f4f4f5',\n '--dsw-alias-label-primary': '#1a1a1a',\n '--dsw-alias-label-secondary': '#555555',\n '--dsw-alias-label-tertiary': '#888888',\n '--dsw-alias-border-l2': '#e5e5e7',\n '--dsw-alias-border-l3': '#cfcfd2',\n '--dsw-alias-state-error-primary': '#d92d20',\n '--dsw-alias-state-success-primary': '#12b76a',\n '--dsw-alias-state-business-primary': '#3b6fe0',\n '--ds-font-family-code': 'ui-monospace, SFMono-Regular, Menlo, monospace',\n // \u8BED\u6CD5\u9AD8\u4EAE token \u989C\u8272:\u5B98\u65B9 shiki \u4E3B\u9898\u53D8\u91CF(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4)\u3002\n '--shiki-token-keyword': '#7c3aed',\n '--shiki-token-string': '#16a34a',\n '--shiki-token-comment': '#9aa0a6',\n '--shiki-token-constant': '#d97706',\n '--shiki-token-function': '#2563eb',\n}\n\n/** \u4ECE\u5BBF\u4E3B\u9875\u9762\u6536\u96C6\u4E3B\u9898\u53D8\u91CF\u7684\u5B9E\u9645\u503C(\u72EC\u7ACB\u6807\u7B7E\u9875\u4E0E DSH \u6DF1\u6D45\u8272\u4FDD\u6301\u4E00\u81F4)\u3002 */\nfunction collectThemeVars(): Record<string, string> {\n const out: Record<string, string> = {}\n const styles = getComputedStyle(document.body)\n for (const name of Object.keys(THEME_VAR_DEFAULTS)) {\n const value = styles.getPropertyValue(name).trim()\n out[name] = value !== '' ? value : THEME_VAR_DEFAULTS[name]\n }\n return out\n}\n\n/**\n * \u6253\u5F00\u72EC\u7ACB\u5BA1\u67E5\u6807\u7B7E\u9875\u3002\u5FC5\u987B\u5728\u7528\u6237\u70B9\u51FB\u624B\u52BF\u5185\u540C\u6B65\u6267\u884C(Blob URL \u751F\u6210 +\n * window.open \u5168\u540C\u6B65),\u5426\u5219\u4F1A\u88AB\u6D4F\u89C8\u5668\u5F39\u7A97\u62E6\u622A\u3002URL \u5EF6\u8FDF\u91CA\u653E\u3002\n * \u8FD4\u56DE\u7684\u5B50\u7A97\u53E3\u5F15\u7528\u767B\u8BB0\u8FDB reviewWindows,\u8BC4\u8BBA\u56DE\u4F20\u65F6\u6821\u9A8C\u6D88\u606F\u6765\u6E90\u3002\n */\n/** \u5B98\u65B9 locale \u670D\u52A1\u7684\u6700\u5C0F\u5F62\u72B6(\u8FD0\u884C\u65F6\u5F62\u72B6\u6821\u9A8C,\u4E0D\u4F9D\u8D56\u5B98\u65B9\u5305\u7C7B\u578B)\u3002 */\ninterface LocaleService {\n getLocale(): { active: string }\n}\n\n/** \u5B98\u65B9 locale \u670D\u52A1(apply \u65F6\u63A5\u7BA1;\u5B98\u65B9\u88C5\u914D\u4E0D\u542B locale \u65F6\u7559 null)\u3002 */\nlet localeService: LocaleService | null = null\n\n/**\n * \u68C0\u6D4B DSH \u754C\u9762\u8BED\u8A00:\u4F18\u5148\u5B98\u65B9 locale \u670D\u52A1(\u7528\u6237\u8BBE\u7F6E > \u6D4F\u89C8\u5668\u8BED\u8A00),\n * \u62FF\u4E0D\u5230\u65F6\u9000\u56DE\u6D4F\u89C8\u5668\u8BED\u8A00(navigator),\u975E zh \u4E00\u5F8B\u6309\u82F1\u6587\u3002\n * \u6BCF\u6B21\u73B0\u8BFB \u2014\u2014 \u7528\u6237\u5728\u8BBE\u7F6E\u91CC\u5207\u6362\u8BED\u8A00\u540E,\u4E0B\u4E00\u6B21\u751F\u6210\u6309\u94AE/\u6807\u7B7E\u9875\u5373\u751F\u6548,\n * \u65E0\u9700\u8BA2\u9605 locale/change \u4E8B\u4EF6\u3002\n */\nfunction detectLang(): Lang {\n if (localeService !== null) {\n return localeService.getLocale().active === 'en' ? 'en' : 'zh'\n }\n const nav = navigator.languages?.[0] ?? navigator.language ?? ''\n return nav.toLowerCase().startsWith('zh') ? 'zh' : 'en'\n}\n\nfunction openReviewTab(text: string): void {\n const files = parseGitDiff(text)\n if (files.length === 0) return\n const html = buildStandaloneHtml(files, collectThemeVars(), text, detectLang())\n const url = URL.createObjectURL(new Blob([html], { type: 'text/html' }))\n // \u987A\u5E26\u6E05\u6389\u5DF2\u5173\u95ED\u7684\u65E7\u5B50\u7A97\u53E3\u5F15\u7528\u3002\n for (const win of reviewWindows) {\n if (win === null || win.closed) reviewWindows.delete(win)\n }\n const win = window.open(url, '_blank')\n if (win !== null) reviewWindows.add(win)\n window.setTimeout(() => URL.revokeObjectURL(url), 60000)\n}\n\n// \u2014\u2014 \u8BC4\u8BBA\u56DE\u4F20(\u72EC\u7ACB\u6807\u7B7E\u9875 \u2192 DSH \u5BF9\u8BDD\u6846)\u2014\u2014\n\n/** \u672C\u63D2\u4EF6\u6253\u5F00\u7684\u5BA1\u67E5\u5B50\u7A97\u53E3(\u6D88\u606F\u6765\u6E90\u6821\u9A8C\u7528)\u3002 */\nconst reviewWindows = new Set<Window | null>()\n\n/** \u5F53\u524D\u4F1A\u8BDD\u7684 composer \u5199\u5165\u5668(header.actions \u7EC4\u4EF6\u6302\u8F7D\u65F6\u6CE8\u518C)\u3002 */\nlet composerWriter: ((text: string) => void) | null = null\n\n/** \u6821\u9A8C\u56DE\u4F20\u8BC4\u8BBA\u7684\u5F62\u72B6,\u8FC7\u6EE4\u975E\u6CD5\u6761\u76EE(\u8DE8\u7A97\u53E3\u6570\u636E\u4E0D\u53EF\u4FE1)\u3002 */\nfunction sanitizeComments(raw: unknown): ReviewComment[] {\n if (!Array.isArray(raw)) return []\n const out: ReviewComment[] = []\n for (const item of raw) {\n if (typeof item !== 'object' || item === null) continue\n const c = item as { path?: unknown; lineNo?: unknown; text?: unknown }\n if (typeof c.path !== 'string' || typeof c.lineNo !== 'number' || typeof c.text !== 'string') continue\n if (c.text.trim() === '') continue\n out.push({ path: c.path, lineNo: Math.trunc(c.lineNo), text: c.text })\n }\n return out\n}\n\n/**\n * \u5904\u7406\u5BA1\u67E5\u9875\u56DE\u4F20\u7684\u8BC4\u8BBA:\u7EC4\u88C5\u300C\u6587\u4EF6\u540D:\u884C\u53F7 \u2014 \u8BC4\u8BBA\u300D\u62A5\u544A\u6587\u672C,\u5199\u5165\n * \u5F53\u524D\u4F1A\u8BDD\u7684 composer,\u5E76\u5411\u5B50\u7A97\u53E3\u56DE\u6267\u3002\u6D88\u606F\u6765\u6E90\u6821\u9A8C:\u5FC5\u987B\u662F\u672C\u63D2\u4EF6\n * \u6253\u5F00\u7684\u7A97\u53E3(\u4E0E origin \u76F8\u6BD4\u66F4\u53EF\u9760,blob \u5B50\u7A97\u53E3\u7684 origin \u8BED\u4E49\u6709\u5DEE\u5F02)\u3002\n */\nfunction handleReviewMessage(event: MessageEvent): void {\n const data = event.data\n if (typeof data !== 'object' || data === null) return\n if (data.type !== 'dsh-code-review-comments') return\n if (!reviewWindows.has(event.source as Window | null)) return\n const comments = sanitizeComments(data.comments)\n if (comments.length === 0) return\n const kind = data.kind === 'lgtm' ? 'lgtm' : 'comment'\n const report = buildCommentReport(comments, kind, detectLang())\n const source = event.source as Window\n if (composerWriter !== null) {\n composerWriter(report)\n source.postMessage({ type: 'dsh-code-review-ack', ok: true }, '*')\n } else {\n source.postMessage({ type: 'dsh-code-review-ack', ok: false }, '*')\n }\n}\n\n// \u2014\u2014 \u6309\u94AE\u6CE8\u5165(\u7EAF DOM,\u4E0D\u4F9D\u8D56 React)\u2014\u2014\n\n/**\n * \u7ED9\u5BBF\u4E3B\u5143\u7D20(\u4EE3\u7801\u5757 banner / \u7EC8\u7AEF\u5361\u7247 header / bash \u5DE5\u5177\u884C\u672C\u4F53)\u52A0\u6309\u94AE:\n * \u2460\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D;\u2461 \u4EE3\u7801\u5757\u573A\u666F\u518D\u52A0\u300C\u5C55\u5F00/\u6536\u8D77\u300D\u5F00\u5173(\u9ED8\u8BA4\u6298\u53E0\n * \u539F\u59CB diff \u6587\u672C,\u804A\u5929\u6D41\u4FDD\u6301\u7D27\u51D1)\u3002\u5DF2\u5B58\u5728\u7684\u6309\u94AE\u8DF3\u8FC7\u3002\n * @param host \u6309\u94AE\u6302\u8F7D\u70B9(React \u4E0D\u8FFD\u8E2A\u6211\u4EEC\u7684\u8282\u70B9,\u91CD\u6E32\u67D3\u4E00\u822C\u4E0D\u52A8\u5B83)\u3002\n * @param getText \u70B9\u51FB\u65F6\u83B7\u53D6 diff \u539F\u59CB\u6587\u672C(\u73B0\u8BFB,\u907F\u514D\u8FC7\u671F\u5F15\u7528)\u3002\n * @param fold \u4EE3\u7801\u5757\u6298\u53E0\u5F00\u5173(\u975E\u4EE3\u7801\u5757\u573A\u666F\u4F20 null)\u3002\n */\nfunction ensureButtons(\n host: HTMLElement,\n getText: () => string | null,\n fold: { folded: boolean; onToggle: () => void } | null,\n): void {\n const texts = CR_TEXTS[detectLang()]\n if (host.querySelector('[data-dsh-cr-btn]') === null) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'dsh-cr-openbtn'\n btn.dataset.dshCrBtn = ''\n btn.textContent = texts.openTab\n btn.title = texts.openTabTitle\n btn.addEventListener('click', (event) => {\n // bash \u5DE5\u5177\u884C\u672C\u4F53\u6574\u884C\u53EF\u70B9(\u5C55\u5F00),\u5FC5\u987B\u963B\u6B62\u4E8B\u4EF6\u4F20\u64AD\u5230\u5B98\u65B9\u70B9\u51FB\u3002\n event.stopPropagation()\n event.preventDefault()\n const text = getText()\n if (text !== null) openReviewTab(text)\n })\n host.appendChild(btn)\n }\n if (fold !== null && host.querySelector('[data-dsh-cr-fold]') === null) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'dsh-cr-openbtn'\n btn.dataset.dshCrFold = ''\n btn.textContent = fold.folded ? texts.expand : texts.collapse\n btn.title = fold.folded ? texts.expandTitle : texts.collapseTitle\n btn.addEventListener('click', (event) => {\n event.stopPropagation()\n event.preventDefault()\n fold.onToggle()\n })\n host.appendChild(btn)\n }\n}\n\n/** \u79FB\u9664\u5BBF\u4E3B\u4E0A\u7684\u6240\u6709\u6309\u94AE(\u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff / \u89E3\u6790\u5931\u8D25\u65F6)\u3002 */\nfunction removeButtons(host: HTMLElement): void {\n for (const btn of host.querySelectorAll('[data-dsh-cr-btn], [data-dsh-cr-fold]')) {\n btn.remove()\n }\n}\n\n// \u2014\u2014 \u5019\u9009\u5757\u63A2\u6D4B(DOM \u6765\u6E90)\u2014\u2014\n\n/** \u63A2\u6D4B\u7ED3\u679C:\u539F\u59CB\u5185\u5BB9 + \u662F\u5426\u503C\u5F97\u52A0\u6309\u94AE + \u662F\u5426\u8FD8\u5728\u6D41\u5F0F/\u8FD0\u884C\u3002 */\ninterface Probe {\n /** \u5B98\u65B9\u539F\u59CB\u5185\u5BB9\u6587\u672C(\u5185\u5BB9\u7B7E\u540D,\u7528\u4E8E\u5224\u65AD\u300C\u5185\u5BB9\u662F\u5426\u53D8\u5316\u300D)\u3002 */\n text: string\n /** \u662F\u5426\u503C\u5F97\u52A0\u6309\u94AE(\u8BED\u8A00\u6807\u7B7E\u662F diff/patch,\u6216\u6587\u672C\u5F62\u4F3C git diff)\u3002 */\n candidate: boolean\n /** \u7EC8\u7AEF\u5361\u7247\u4ECD\u5728\u8FD0\u884C(\u8F93\u51FA\u672A\u5B8C\u6210)\u3002 */\n running: boolean\n}\n\n/**\n * \u5B9A\u4F4D markdown \u4EE3\u7801\u5757\u7684\u5B98\u65B9\u539F\u59CB\u5185\u5BB9\u5BB9\u5668(body)\u3002\n *\n * \u4E0D\u80FD\u6309\u300C\u7B2C 2 \u4E2A\u5B50\u8282\u70B9\u300D\u53D6:React \u5728\u8BED\u6CD5\u9AD8\u4EAE\u5668\u5C31\u7EEA\u65F6\u4F1A\u628A body \u4ECE\n * `<pre>` \u6362\u6210\u9AD8\u4EAE `<div>`,\u800C\u65B0\u8282\u70B9\u4F1A append \u5230\u7236\u7EA7\u672B\u5C3E(React \u53EA\u8FFD\u8E2A\n * \u81EA\u5DF1\u7684\u5B50\u8282\u70B9,\u6211\u4EEC\u7684\u6309\u94AE\u662F\u672A\u8FFD\u8E2A\u8282\u70B9,\u4F1A\u88AB\u6324\u5230\u524D\u9762)\u3002\n * \u56E0\u6B64\u6309\u7ED3\u6784\u627E:\u8DF3\u8FC7\u6211\u4EEC\u7684\u8282\u70B9(data-dsh-cr-btn),\u7B2C\u4E00\u4E2A PRE \u5373 body;\n * \u6CA1\u6709 PRE \u65F6,\u7B2C\u4E00\u4E2A\u300C\u975E banner\u300D\u7684 DIV \u5373\u9AD8\u4EAE body\u3002\n */\nfunction findCodeBlockBody(el: HTMLElement): HTMLElement | null {\n const first = el.firstElementChild\n for (const child of el.children) {\n if (!(child instanceof HTMLElement)) continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (child.tagName === 'PRE') return child\n }\n for (const child of el.children) {\n if (!(child instanceof HTMLElement)) continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (child === first) continue // banner\n if (child.tagName === 'DIV') return child\n }\n return null\n}\n\n/** \u63A2\u6D4B markdown \u4EE3\u7801\u5757:\u7ED3\u6784\u4E3A [banner(\u8BED\u8A00\u6807\u7B7E+\u590D\u5236\u6309\u94AE), body]\u3002 */\nfunction probeCodeBlock(el: HTMLElement): Probe | null {\n const banner = el.firstElementChild\n const body = findCodeBlockBody(el)\n if (!(banner instanceof HTMLElement) || body === null) return null\n const lang = (banner.firstElementChild?.textContent ?? '').trim().toLowerCase()\n const text = body.textContent ?? ''\n const candidate = lang === 'diff' || lang === 'patch' || isDiffText(text)\n return { text, candidate, running: false }\n}\n\n/**\n * \u5728\u7EC8\u7AEF\u5361\u7247\u91CC\u627E\u8F93\u51FA\u5BB9\u5668:\u8DF3\u8FC7\u7B2C 0 \u4E2A\u5B50\u8282\u70B9(\u5934\u90E8:\u547D\u4EE4/\u72B6\u6001/\u590D\u5236\u6309\u94AE),\n * \u7B2C\u4E00\u4E2A\u5185\u5BB9\u5F62\u4F3C git diff \u7684 div \u5373\u8F93\u51FA\u5BB9\u5668(\u7A7A\u6001/\u5934\u90E8\u90FD\u4E0D\u4F1A\u5F62\u4F3C diff,\n * \u5929\u7136\u6392\u9664);\u6211\u4EEC\u7684\u6309\u94AE\u5E26 data-dsh-cr-btn,\u4E00\u5E76\u8DF3\u8FC7\u3002\n */\nfunction findOutputContainer(wrapper: HTMLElement): HTMLElement | null {\n const children = wrapper.children\n for (let i = 1; i < children.length; i += 1) {\n const child = children[i]\n if (!(child instanceof HTMLElement) || child.tagName !== 'DIV') continue\n if (child.hasAttribute('data-dsh-cr-btn') || child.hasAttribute('data-dsh-cr-fold')) continue\n if (isDiffText(child.textContent ?? '')) return child\n }\n return null\n}\n\n/**\n * \u8F93\u51FA\u5BB9\u5668\u9010\u884C\u53D6\u6587\u672C:\u6BCF\u884C\u662F div.line(ANSI \u5DF2\u6E32\u67D3\u6210 span,textContent \u5373\n * \u51C0\u5316\u540E\u7684\u7EAF\u6587\u672C);\u6298\u53E0\u6309\u94AE\u7B49\u975E\u884C\u5143\u7D20\u8DF3\u8FC7\u3002\n */\nfunction joinOutputLines(container: HTMLElement): string {\n const parts: string[] = []\n for (const child of container.children) {\n if (child instanceof HTMLElement && child.tagName === 'DIV') {\n parts.push(child.textContent ?? '')\n }\n }\n return parts.join('\\n')\n}\n\n/** \u63A2\u6D4B\u7EC8\u7AEF\u5361\u7247:\u8FD0\u884C\u4E2D\u76F4\u63A5\u6807\u8BB0 running,\u7531\u8C03\u7528\u65B9\u8DF3\u8FC7\u3002 */\nfunction probeTerminal(el: HTMLElement): Probe | null {\n if (el.hasAttribute('data-running')) {\n return { text: '', candidate: false, running: true }\n }\n const output = findOutputContainer(el)\n if (output === null) return null\n const text = joinOutputLines(output)\n return { text, candidate: isDiffText(text), running: false }\n}\n\n/** \u6309\u5143\u7D20\u7C7B\u578B\u5206\u53D1\u63A2\u6D4B\u3002 */\nfunction probeFor(el: HTMLElement): Probe | null {\n if (el.classList.contains('md-code-block')) return probeCodeBlock(el)\n if (el.hasAttribute('data-terminal')) {\n // \u5DE5\u5177\u884C\u5185\u7684\u7EC8\u7AEF\u5361\u7247:\u6309\u94AE\u52A0\u5728\u884C\u672C\u4F53\u4E0A(\u7531\u6570\u636E\u5C42\u6CE8\u5165,\u6298\u53E0\u65F6\u4E5F\n // \u53EF\u89C1),\u8FD9\u91CC\u8DF3\u8FC7,\u907F\u514D\u540C\u4E00\u5757\u51FA\u73B0\u4E24\u4E2A\u6309\u94AE\u3002\n if (el.closest('[data-sample=\"bash\"]') !== null) return null\n return probeTerminal(el)\n }\n return null\n}\n\n/** \u6309\u94AE\u6302\u8F7D\u70B9:\u4EE3\u7801\u5757 = banner,\u7EC8\u7AEF\u5361\u7247 = header(\u90FD\u662F\u5B98\u65B9\u7B2C\u4E00\u4E2A\u5B50\u8282\u70B9)\u3002 */\nfunction buttonHostFor(el: HTMLElement): HTMLElement | null {\n const first = el.firstElementChild\n return first instanceof HTMLElement ? first : null\n}\n\n// \u2014\u2014 \u6570\u636E\u5C42(\u6298\u53E0\u7684 bash \u5DE5\u5177\u884C)\u2014\u2014\n\n/**\n * \u6570\u636E\u5C42\u6587\u672C\u8868:node key \u2192 { sessionId, diff \u6587\u672C }\u3002\n *\n * \u6298\u53E0\u7684 bash \u5DE5\u5177\u884C\u5728 DOM \u91CC\u53EA\u6709\u4E00\u884C\u6458\u8981,\u8F93\u51FA\u5185\u5BB9\u968F\u5C55\u5F00\u624D\u6302\u8F7D,\n * DOM \u63A2\u6D4B\u770B\u4E0D\u5230 \u2014\u2014 \u53EA\u80FD\u4ECE chat \u5FEB\u7167(\u6570\u636E\u5C42)\u62FF\u5230\u7ED3\u679C\u6587\u672C\u3002\n */\nconst pendingTexts = new Map<string, { sessionId: string; text: string }>()\n\n/** chat \u5FEB\u7167\u7684\u6700\u5C0F\u5F62\u72B6(\u4E0E dsh-web-enhance \u540C\u53E3\u5F84,\u53EA\u53D6\u6240\u9700\u5B57\u6BB5)\u3002 */\ninterface ChatLike {\n order: readonly string[]\n nodes: { get(key: string): { kind: string; data: unknown } | undefined }\n}\n\n/** \u4ECE tool-call \u8282\u70B9 data \u91CC\u53D6\u5DE5\u5177\u7ED3\u679C\u6839(root: RunningToolCall | ToolResultNode)\u3002 */\nfunction readToolRoot(data: unknown): unknown {\n if (typeof data !== 'object' || data === null) return null\n return (data as { root?: unknown }).root ?? null\n}\n\n/** \u5DE5\u5177\u7ED3\u679C\u662F\u5426\u5DF2\u843D\u5730(settled ToolResultNode:\u5E26 content \u6570\u7EC4)\u3002 */\nfunction isSettledToolResult(root: unknown): root is { call: { name: string } | null; content: readonly unknown[] } {\n if (typeof root !== 'object' || root === null) return false\n const r = root as { call?: unknown; content?: unknown }\n if (!Array.isArray(r.content)) return false\n // call \u5934\u53EF\u80FD\u88AB\u7A97\u53E3\u622A\u65AD(call: null),\u4FDD\u5B88\u8D77\u89C1\u53EA\u8BA4\u5F97\u5230\u5DE5\u5177\u540D\u7684\u3002\n if (r.call !== null && (typeof r.call !== 'object' || r.call === null || typeof (r.call as { name?: unknown }).name !== 'string')) {\n return false\n }\n return true\n}\n\n/** \u62FC\u63A5\u5DE5\u5177\u7ED3\u679C content \u91CC\u7684\u6587\u672C\u5757(\u5DE5\u5177\u8F93\u51FA\u901A\u5E38\u662F\u4E00\u6BB5 text \u5757)\u3002 */\nfunction toolResultText(root: unknown): string {\n if (!isSettledToolResult(root)) return ''\n const parts: string[] = []\n for (const block of root.content) {\n if (typeof block !== 'object' || block === null) continue\n const b = block as { type?: unknown; text?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text)\n }\n return parts.join('\\n')\n}\n\n/**\n * \u628A\u5F53\u524D\u4F1A\u8BDD chat \u5FEB\u7167\u91CC\u300C\u8F93\u51FA\u5F62\u4F3C git diff \u7684 bash \u5DE5\u5177\u7ED3\u679C\u300D\u540C\u6B65\u8FDB\n * \u6587\u672C\u8868:\u5148\u6E05\u6389\u672C\u4F1A\u8BDD\u65E7\u6761\u76EE(\u9632\u5DE8\u578B\u6587\u672C\u8DE8\u4F1A\u8BDD\u7D2F\u79EF),\u518D\u5199\u5165\u65B0\u6761\u76EE\u3002\n */\nfunction syncDiffToolTexts(sessionId: string, chat: ChatLike): void {\n const found = new Map<string, string>()\n for (const key of chat.order) {\n const node = chat.nodes.get(key)\n if (node === undefined || node.kind !== 'tool-call') continue\n const root = readToolRoot(node.data)\n if (root === null || !isSettledToolResult(root)) continue\n if (root.call === null || root.call.name !== 'bash') continue\n const text = toolResultText(root)\n if (!isDiffText(text)) continue\n found.set(key, text)\n }\n for (const [key, entry] of pendingTexts) {\n if (entry.sessionId === sessionId && !found.has(key)) pendingTexts.delete(key)\n }\n for (const [key, text] of found) pendingTexts.set(key, { sessionId, text })\n}\n\n/** \u6309 data-chat-anchor-key \u627E\u6E32\u67D3\u884C(\u904D\u5386\u6BD4\u5BF9,\u907F\u514D key \u7279\u6B8A\u5B57\u7B26\u8F6C\u4E49\u95EE\u9898)\u3002 */\nfunction findRowByKey(key: string): HTMLElement | null {\n for (const el of document.querySelectorAll('[data-chat-anchor-key]')) {\n if (el instanceof HTMLElement && el.getAttribute('data-chat-anchor-key') === key) return el\n }\n return null\n}\n\n/** \u7ED9\u6570\u636E\u5C42\u6536\u96C6\u5230\u7684 bash \u5DE5\u5177\u884C\u52A0\u6309\u94AE(\u884C\u672A\u6E32\u67D3\u65F6\u7B49\u4E0B\u4E00\u8F6E\u626B\u63CF)\u3002 */\nfunction attachToolRowButtons(): void {\n for (const [key, entry] of pendingTexts) {\n const row = findRowByKey(key)\n if (row === null) continue\n const sample = row.querySelector('[data-sample=\"bash\"]')\n if (!(sample instanceof HTMLElement)) continue\n ensureButtons(sample, () => pendingTexts.get(key)?.text ?? entry.text, null)\n }\n}\n\n// \u2014\u2014 \u626B\u63CF\u5668 \u2014\u2014\n\n/** \u4E00\u4E2A\u5019\u9009\u5757\u7684\u8DDF\u8E2A\u72B6\u6001(DOM \u6765\u6E90)\u3002 */\ninterface BlockState {\n /** \u4E0A\u6B21\u68C0\u6D4B\u5230\u7684\u5185\u5BB9\u7B7E\u540D\u3002 */\n text: string\n /** \u7A33\u5B9A\u53BB\u6296\u5B9A\u65F6\u5668(null = \u6CA1\u6709\u5F85\u5904\u7406\u7684\u52A0\u6309\u94AE)\u3002 */\n timer: number | null\n /** \u4E0A\u6B21\u89E3\u6790\u5931\u8D25\u7684\u65F6\u95F4\u6233(\u91CD\u8BD5\u8282\u6D41)\u3002 */\n lastTry: number\n /** \u4E0A\u6B21\u89E3\u6790\u662F\u5426\u5931\u8D25(\u5931\u8D25\u65F6\u5185\u5BB9\u4E0D\u53D8\u4E5F\u4F1A\u6309\u8282\u6D41\u91CD\u8BD5)\u3002 */\n failed: boolean\n /** \u4EE3\u7801\u5757\u662F\u5426\u6298\u53E0\u539F\u59CB\u6587\u672C(\u9ED8\u8BA4\u6298\u53E0,\u804A\u5929\u6D41\u4FDD\u6301\u7D27\u51D1)\u3002 */\n folded: boolean\n}\n\n/**\n * \u5168\u6587\u6863\u626B\u63CF\u5668:\u53D1\u73B0\u5019\u9009\u5757\u3001\u53BB\u6296\u3001\u6CE8\u5165\u6309\u94AE\u3001\u5185\u5BB9\u53D8\u5316\u65F6\u56DE\u9000\u3002\n *\n * \u72B6\u6001\u673A(\u6BCF\u4E2A\u5757):\n * - \u5185\u5BB9\u53D8\u5316/\u65B0\u5143\u7D20 \u2192 \u6392 STABLE_MS \u53BB\u6296;\n * - \u53BB\u6296\u5230\u70B9\u4E14\u5185\u5BB9\u672A\u518D\u53D8 \u2192 \u89E3\u6790\u9A8C\u8BC1(\u80FD\u89E3\u6790\u51FA\u6587\u4EF6\u624D\u52A0\u6309\u94AE);\n * - \u5185\u5BB9\u4E0D\u518D\u5F62\u4F3C diff / \u7ED3\u6784\u6D88\u5931 \u2192 \u79FB\u9664\u6309\u94AE\u3002\n */\nclass DiffScanner {\n private readonly states = new WeakMap<HTMLElement, BlockState>()\n private observer: MutationObserver | null = null\n private scanTimer: number | null = null\n private safetyTimer: number | null = null\n\n start(): void {\n if (this.observer !== null) return\n // \u4EFB\u4F55 DOM \u589E\u5220\u90FD\u89E6\u53D1\u4E00\u6B21\u53BB\u6296\u626B\u63CF:\u8986\u76D6\u6D41\u5F0F\u8FFD\u52A0\u3001\u865A\u62DF\u5316\u91CD\u6302\u8F7D\u3001\n // \u5DE5\u5177\u884C\u5C55\u5F00\u7B49\u6240\u6709\u51FA\u73B0\u8DEF\u5F84\u3002\n this.observer = new MutationObserver(() => this.scheduleScan())\n this.observer.observe(document.body, { childList: true, subtree: true })\n this.scheduleScan()\n // \u515C\u5E95\u5468\u671F\u626B\u63CF:\u5185\u5BB9\u53D8\u5316\u7406\u8BBA\u4E0A\u90FD\u4F1A\u4EA7\u751F childList mutation,\n // \u8FD9\u91CC\u515C\u4F4F\u9057\u6F0F\u8DEF\u5F84,\u987A\u5E26\u8BA9\u89E3\u6790\u5931\u8D25\u7684\u91CD\u8BD5\u6709\u673A\u4F1A\u89E6\u53D1\u3002\n this.safetyTimer = window.setInterval(() => this.scan(), SAFETY_SCAN_MS)\n }\n\n stop(): void {\n this.observer?.disconnect()\n this.observer = null\n if (this.scanTimer !== null) {\n clearTimeout(this.scanTimer)\n this.scanTimer = null\n }\n if (this.safetyTimer !== null) {\n clearInterval(this.safetyTimer)\n this.safetyTimer = null\n }\n // states \u91CC\u7684\u53BB\u6296\u5B9A\u65F6\u5668\u4E0D\u5FC5\u9010\u4E2A\u6E05\u7406:\u5230\u70B9\u540E probe \u590D\u67E5\u4F1A\u628A\u5DF2\u65AD\u5F00\n // \u5143\u7D20\u7684\u6587\u672C\u8BFB\u6210\u7A7A\u4E32\u3001candidate=false,\u81EA\u7136\u653E\u5F03;WeakMap \u968F\u5757 GC\u3002\n }\n\n /** \u5408\u5E76\u9AD8\u9891 mutation \u4E3A\u4E00\u6B21\u626B\u63CF\u3002 */\n private scheduleScan(): void {\n if (this.scanTimer !== null) return\n this.scanTimer = window.setTimeout(() => {\n this.scanTimer = null\n this.scan()\n }, SCAN_DEBOUNCE)\n }\n\n /** \u5168\u91CF\u626B\u63CF:\u6570\u636E\u5C42\u5DE5\u5177\u884C\u6309\u94AE + DOM \u5019\u9009\u5757\u3002 */\n private scan(): void {\n attachToolRowButtons()\n for (const el of document.querySelectorAll('.md-code-block, [data-terminal]')) {\n this.inspect(el as HTMLElement)\n }\n }\n\n /** \u68C0\u67E5\u4E00\u4E2A\u5019\u9009\u5757:\u5185\u5BB9\u672A\u53D8\u5219\u4E0D\u52A8,\u53D8\u4E86\u5219\u6392\u53BB\u6296,\u4E0D\u518D\u5F62\u4F3C diff \u5219\u79FB\u9664\u6309\u94AE\u3002 */\n private inspect(el: HTMLElement): void {\n const probe = probeFor(el)\n if (probe === null || probe.running || !probe.candidate || probe.text.trim() === '') {\n const host = buttonHostFor(el)\n if (host !== null) removeButtons(host)\n // \u6062\u590D\u5B98\u65B9\u539F\u59CB\u663E\u793A(\u6B64\u524D\u53EF\u80FD\u88AB\u6298\u53E0)\u3002\n const body = findCodeBlockBody(el)\n if (body !== null) body.style.display = ''\n this.states.delete(el)\n return\n }\n const st = this.states.get(el)\n if (st !== undefined && st.text === probe.text) {\n // \u5185\u5BB9\u672A\u53D8:\u6309\u94AE\u5DF2\u52A0/\u53BB\u6296\u4E2D \u2192 \u65E0\u4E8B;\u89E3\u6790\u5931\u8D25 \u2192 \u6309\u8282\u6D41\u91CD\u8BD5\u3002\n if (!st.failed) {\n // \u91CD\u65B0\u540C\u6B65\u6298\u53E0\u6001:React \u53EF\u80FD\u5728\u540E\u53F0\u66FF\u6362\u8FC7 body \u8282\u70B9(\u8BED\u6CD5\u9AD8\u4EAE\n // \u5C31\u7EEA\u65F6 pre \u2192 div),\u65B0\u8282\u70B9\u9700\u8981\u91CD\u65B0\u9690\u85CF/\u663E\u793A\u3002\n this.applyFoldState(el, st)\n return\n }\n if (Date.now() - st.lastTry < RETRY_MS) return\n // \u5931\u8D25\u91CD\u8BD5\u5230\u70B9:\u76F4\u63A5\u8D70\u4E0B\u65B9\u52A0\u6309\u94AE\u5206\u652F(\u5185\u5BB9\u7A33\u5B9A,\u4E0D\u5FC5\u518D\u53BB\u6296)\u3002\n }\n if (st !== undefined && st.timer !== null) clearTimeout(st.timer)\n const timer = window.setTimeout(() => {\n const cur = this.states.get(el)\n if (cur === undefined || cur.timer !== timer) return\n cur.timer = null\n // \u5230\u70B9\u590D\u67E5:\u5185\u5BB9\u53C8\u53D8\u4E86(\u6D41\u5F0F\u8FD8\u5728\u7EE7\u7EED)\u5219\u653E\u5F03\u672C\u6B21,\u7B49\u4E0B\u4E00\u8F6E\u3002\n const p2 = probeFor(el)\n if (p2 === null || p2.running || !p2.candidate || p2.text !== cur.text) return\n this.attachButton(el, cur)\n }, STABLE_MS)\n this.states.set(el, {\n text: probe.text,\n timer,\n lastTry: st?.lastTry ?? 0,\n failed: st?.failed ?? false,\n folded: st?.folded ?? true,\n })\n }\n\n /** \u5185\u5BB9\u7A33\u5B9A\u540E\u6267\u884C:\u89E3\u6790\u9A8C\u8BC1\u3001\u6298\u53E0\u539F\u59CB\u6587\u672C\u5E76\u6CE8\u5165\u6309\u94AE(\u5931\u8D25\u5219\u4FDD\u6301\u65E0\u6309\u94AE)\u3002 */\n private attachButton(el: HTMLElement, st: BlockState): void {\n const host = buttonHostFor(el)\n if (host === null) return\n if (parseGitDiff(st.text).length === 0) {\n st.failed = true\n st.lastTry = Date.now()\n removeButtons(host)\n return\n }\n st.failed = false\n const isCodeBlock = el.classList.contains('md-code-block')\n if (isCodeBlock) {\n // \u9ED8\u8BA4\u6298\u53E0:diff \u4EE3\u7801\u5757\u53EA\u7559\u6807\u9898\u884C + \u6309\u94AE,\u4E0D\u644A\u5F00\u6574\u6BB5\u539F\u59CB\u6587\u672C\u3002\n this.applyFoldState(el, st)\n }\n // \u70B9\u51FB\u65F6\u73B0\u8BFB\u6587\u672C(React \u91CD\u6E32\u67D3\u540E\u65E7\u5F15\u7528\u53EF\u80FD\u8FC7\u671F)\u3002\n ensureButtons(\n host,\n () => probeFor(el)?.text ?? st.text,\n isCodeBlock ? { folded: st.folded, onToggle: () => this.toggleFold(el) } : null,\n )\n }\n\n /** \u540C\u6B65\u4EE3\u7801\u5757\u7684\u6298\u53E0\u6001(\u5B98\u65B9 body \u53EA\u6539 display,React \u4E0D\u7BA1\u7406\u8FD9\u4E2A style)\u3002 */\n private applyFoldState(el: HTMLElement, st: BlockState): void {\n if (!el.classList.contains('md-code-block')) return\n const body = findCodeBlockBody(el)\n if (body !== null) body.style.display = st.folded ? 'none' : ''\n }\n\n /** \u5C55\u5F00/\u6536\u8D77\u4EE3\u7801\u5757\u539F\u59CB\u6587\u672C,\u5E76\u540C\u6B65\u6309\u94AE\u6587\u6848\u3002 */\n private toggleFold(el: HTMLElement): void {\n const st = this.states.get(el)\n if (st === undefined) return\n st.folded = !st.folded\n this.applyFoldState(el, st)\n const host = buttonHostFor(el)\n const btn = host?.querySelector('[data-dsh-cr-fold]')\n if (btn instanceof HTMLElement) {\n const texts = CR_TEXTS[detectLang()]\n btn.textContent = st.folded ? texts.expand : texts.collapse\n }\n }\n}\n\n// \u2014\u2014 \u63D2\u4EF6\u4E3B\u4F53 \u2014\u2014\n\n/**\n * \u9700\u8981\u7684 client \u670D\u52A1:slots(slot \u6CE8\u518C)\u3001locale(\u754C\u9762\u8BED\u8A00\u68C0\u6D4B)\u3002\n *\n * cordis \u7684\u670D\u52A1\u8BBF\u95EE\u5FC5\u987B\u5148\u5728\u8FD9\u91CC\u58F0\u660E\u6CE8\u5165,\u5426\u5219 apply \u91CC\u8BFB ctx.locale\n * \u4F1A\u629B \"cannot get property 'locale' without inject\" \u2014\u2014 loader entry\n * \u5E94\u7528\u5931\u8D25\u4F1A\u8BA9\u6574\u4E2A\u5BA2\u6237\u7AEF\u7EC4\u5408\u6811\u5D29\u6E83(\u767D\u5C4F)\u3002\u8FD9\u4E5F\u662F\u4E0A\u6B21\u4E8B\u6545\u7684\u6839\u56E0\u3002\n * \u5B98\u65B9\u88C5\u914D\u542B dsh-client-locale,\u670D\u52A1\u5B58\u5728;\u4E07\u4E00\u672A\u6765\u7F3A\u5931,inject \u8FD4\u56DE\n * undefined,detectLang \u91CC\u4F1A\u9000\u5230\u6D4F\u89C8\u5668\u8BED\u8A00,\u4E0D\u4F1A\u5D29\u3002\n */\nexport const inject = ['slots', 'locale']\n\n/** Client \u63D2\u4EF6 body:\u63A5\u7BA1\u5B98\u65B9 locale \u670D\u52A1,\u6CE8\u518C\u626B\u63CF\u5668\u5BBF\u4E3B\u4E0E\u6570\u636E\u5C42\u76D1\u542C\u3002 */\nexport function apply(ctx: ClientContext): void {\n // \u63A5\u7BA1\u5B98\u65B9 locale \u670D\u52A1(\u8FD0\u884C\u65F6\u5F62\u72B6\u6821\u9A8C;\u7F3A\u7701\u65F6 detectLang \u9000\u6D4F\u89C8\u5668\u8BED\u8A00)\u3002\n const locale = ctx.locale\n if (typeof locale === 'object' && locale !== null && typeof (locale as { getLocale?: unknown }).getLocale === 'function') {\n localeService = locale as LocaleService\n }\n ctx.slots.inject(\n 'shell.overlay',\n () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'code-review-enhancer',\n }, CodeReviewEnhancer),\n )\n ctx.slots.inject(\n 'conversation.session.header.actions',\n () => ctx.slots.register({\n name: 'conversation.session.header.actions',\n id: 'code-review-tool-watcher',\n // \u5B98\u65B9\u6761\u76EE:agent-preset=-10\u3001subagent-catalog=10\u3001job-list=20,\n // web-enhance=15\u3002\u53D6 25 \u6392\u5728\u6700\u540E,\u4E0D\u4E0E\u4ED6\u4EBA\u5E76\u5217\u3002\n order: 25,\n }, DiffToolButtonWatcher),\n )\n}\n\n/**\n * \u626B\u63CF\u5668\u5BBF\u4E3B\u7EC4\u4EF6(root \u4F5C\u7528\u57DF,\u5168\u5C40\u53EA\u6B64\u4E00\u4EFD):\u6302\u8F7D\u65F6\u6CE8\u5165\u6837\u5F0F\u3001\u542F\u52A8\n * \u5168\u6587\u6863\u626B\u63CF\u3001\u76D1\u542C\u5BA1\u67E5\u9875\u7684\u8BC4\u8BBA\u56DE\u4F20\u6D88\u606F,\u5378\u8F7D\u65F6\u5168\u90E8\u6E05\u7406\u3002\n * \u81EA\u8EAB\u6E32\u67D3 null,\u4E0D\u5360\u4EFB\u4F55\u754C\u9762\u7A7A\u95F4\u3002\n */\nfunction CodeReviewEnhancer() {\n useEffect(() => {\n ensureStyle()\n const scanner = new DiffScanner()\n scanner.start()\n const onMessage = handleReviewMessage\n window.addEventListener('message', onMessage)\n return () => {\n scanner.stop()\n window.removeEventListener('message', onMessage)\n }\n }, [])\n return null\n}\n\n/**\n * \u6570\u636E\u5C42\u76D1\u542C(session \u4F5C\u7528\u57DF,\u968F\u4F1A\u8BDD\u5207\u6362\u81EA\u52A8\u91CD\u8BA2\u9605):\n * 1. \u8BA2\u9605\u5F53\u524D\u4F1A\u8BDD chat \u5FEB\u7167,\u628A\u8F93\u51FA\u5F62\u4F3C git diff \u7684 bash \u5DE5\u5177\u7ED3\u679C\u540C\u6B65\n * \u8FDB\u6587\u672C\u8868,\u626B\u63CF\u5668\u636E\u6B64\u7ED9\u6298\u53E0\u7684\u5DE5\u5177\u884C\u52A0\u6309\u94AE;\n * 2. \u6CE8\u518C\u5F53\u524D\u4F1A\u8BDD\u7684 composer \u5199\u5165\u5668 \u2014\u2014 \u5BA1\u67E5\u9875\u56DE\u4F20\u7684\u8BC4\u8BBA\u7ECF\n * inputActions.setDraft(\u5B98\u65B9\u5355\u4E00\u5199\u5165\u53E3)\u5199\u5165\u5BF9\u8BDD\u6846\u3002\u81EA\u8EAB\u6E32\u67D3 null\u3002\n */\nfunction DiffToolButtonWatcher({ useSession, sessionId, inputActions }: PropsRuntime<'conversation.session.header.actions'>) {\n const chat = useSession((state) => state.chat)\n useEffect(() => {\n if (chat !== undefined) syncDiffToolTexts(String(sessionId), chat)\n }, [chat, sessionId])\n useEffect(() => {\n // \u53EA\u6E05\u7406\u81EA\u5DF1\u6CE8\u518C\u7684\u5199\u5165\u5668(\u591A\u7EC4\u4EF6\u5B9E\u4F8B\u5E76\u5B58\u65F6\u4E92\u4E0D\u8BEF\u5220)\u3002\n const writer = (text: string): void => inputActions.setDraft(text)\n composerWriter = writer\n return () => {\n if (composerWriter === writer) composerWriter = null\n }\n }, [inputActions])\n return null\n}\n", "/**\n * unified diff \u6587\u672C\u89E3\u6790\u5668(\u7EAF\u903B\u8F91,\u4E0D\u4F9D\u8D56 DOM,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u8F93\u5165\u662F `git diff` / `git show` / `git diff --no-index` \u7B49\u4EA7\u751F\u7684\n * unified diff \u6587\u672C,\u8F93\u51FA\u6309\u6587\u4EF6\u62C6\u5206\u7684\u7ED3\u6784\u5316\u6570\u636E,\u4F9B\u53CC\u5217\u89C6\u56FE\u6E32\u67D3\u3002\n *\n * \u884C\u6A21\u578B\u8BF4\u660E:\n * - ctx \u4E0A\u4E0B\u6587\u884C:\u65E7/\u65B0\u4E24\u4FA7\u90FD\u6709,\u884C\u53F7\u5404\u81EA\u9012\u589E;\n * - del \u5220\u9664\u884C:\u53EA\u5728\u65E7\u4FA7;\n * - add \u65B0\u589E\u884C:\u53EA\u5728\u65B0\u4FA7;\n * - change \u53D8\u66F4\u5BF9:\u8FDE\u7EED\u300C-\u300D\u5757\u540E\u7D27\u8DDF\u8FDE\u7EED\u300C+\u300D\u5757\u65F6,\u4E24\u4E24\u914D\u5BF9\u6210\u540C\u4E00\u884C\n * (GitHub split view \u7684\u5BF9\u9F50\u65B9\u5F0F),\u65E7\u884C\u53F7\u53D6 - \u884C\u3001\u65B0\u884C\u53F7\u53D6 + \u884C,\n * \u4E24\u4FA7\u6570\u91CF\u4E0D\u7B49\u65F6,\u591A\u51FA\u7684\u884C\u9000\u5316\u4E3A\u5355\u4FA7 del/add;\n * - gap \u95F4\u9699\u884C:@@ hunk \u5934,\u6216\u6298\u53E0\u540E\u7684\u300C\u22EF \u5C55\u5F00\u300D\u5360\u4F4D(\u6E32\u67D3\u5C42\u7528),\n * \u4E0D\u5360\u884C\u53F7\u3002\n *\n * \u5B9E\u73B0\u6CE8\u610F:\u4E0D\u628A\u300C\u5F53\u524D\u6587\u4EF6\u300D\u5B58\u6210\u88AB\u95ED\u5305\u6355\u83B7\u8D4B\u503C\u7684\u5C40\u90E8\u53D8\u91CF \u2014\u2014\n * TypeScript \u5BF9\u300C\u95ED\u5305\u5185\u8D4B\u503C\u7684 let\u300D\u4F1A\u505A\u4FDD\u5B88\u63A7\u5236\u6D41\u5206\u6790,\u5916\u5C42\u7A84\u5316\u4F1A\u5931\u6548\n * (\u751A\u81F3\u7A84\u5316\u6210 never);\u7EDF\u4E00\u7528 current() \u4ECE files \u6808\u9876\u53D6,\u907F\u514D\u8E29\u5751\u3002\n */\n\n/** \u53CC\u5217\u89C6\u56FE\u91CC\u7684\u4E00\u884C\u3002 */\nexport interface DiffRow {\n kind: 'ctx' | 'del' | 'add' | 'change' | 'gap'\n /** \u65E7\u4FA7\u884C\u53F7(ctx/del/change \u6709\u503C)\u3002 */\n oldNo?: number\n /** \u65B0\u4FA7\u884C\u53F7(ctx/add/change \u6709\u503C)\u3002 */\n newNo?: number\n /** \u65E7\u4FA7\u6587\u672C(\u5DF2\u53BB\u6389\u884C\u9996\u7684 - / \u7A7A\u683C\u524D\u7F00)\u3002 */\n oldText?: string\n /** \u65B0\u4FA7\u6587\u672C(\u5DF2\u53BB\u6389\u884C\u9996\u7684 + / \u7A7A\u683C\u524D\u7F00)\u3002 */\n newText?: string\n /** gap \u884C\u7684\u663E\u793A\u6587\u672C(@@ \u5934\u539F\u6587)\u3002 */\n gapText?: string\n /** \u65E7\u4FA7\u8BE5\u884C\u662F\u6587\u4EF6\u6700\u540E\u4E00\u884C\u4E14\u672B\u5C3E\u65E0\u6362\u884C\u7B26(\\ No newline \u6807\u8BB0)\u3002 */\n oldNoEol?: boolean\n /** \u65B0\u4FA7\u8BE5\u884C\u662F\u6587\u4EF6\u6700\u540E\u4E00\u884C\u4E14\u672B\u5C3E\u65E0\u6362\u884C\u7B26\u3002 */\n newNoEol?: boolean\n}\n\n/** \u4E00\u4E2A\u6587\u4EF6\u7684\u53D8\u66F4\u3002 */\nexport interface FileDiff {\n /** \u53D8\u66F4\u524D\u8DEF\u5F84(\u539F\u59CB\u6587\u672C,\u53EF\u80FD\u5E26 a/ \u524D\u7F00;/dev/null \u8868\u793A\u65B0\u5EFA)\u3002 */\n oldPath: string\n /** \u53D8\u66F4\u540E\u8DEF\u5F84(\u539F\u59CB\u6587\u672C,\u53EF\u80FD\u5E26 b/ \u524D\u7F00;/dev/null \u8868\u793A\u5220\u9664)\u3002 */\n newPath: string\n /** \u53D8\u66F4\u7C7B\u578B\u3002 */\n status: 'modified' | 'added' | 'deleted' | 'renamed' | 'binary' | 'mode'\n /** \u6587\u4EF6\u7EA7\u5143\u4FE1\u606F\u884C(new file mode / rename from / similarity index \u7B49)\u3002 */\n meta: string[]\n /** \u6E32\u67D3\u884C\u5E8F\u5217(\u542B hunk \u95F4\u9699\u884C)\u3002 */\n rows: DiffRow[]\n}\n\n/** \u5C55\u793A\u7528\u7684\u8DEF\u5F84:\u53BB\u6389 a/ b/ \u524D\u7F00(\u4EC5\u5F53\u786E\u5B9E\u5E26\u524D\u7F00\u65F6)\u3002 */\nexport function displayPath(path: string): string {\n if (path === '/dev/null') return path\n return path.replace(/^[ab]\\//, '')\n}\n\n/** \u6587\u672C\u662F\u5426\u5F62\u4F3C git diff(\u68C0\u6D4B\u7528,\u5148\u4E8E\u5B8C\u6574\u89E3\u6790)\u3002 */\nexport function isDiffText(text: string): boolean {\n const lines = text.split('\\n')\n // git \u6807\u51C6\u5934;\u6CA1\u6709\u5B83\u65F6,\u9000\u800C\u8981\u6C42\u300Chunk \u5934 + \u6587\u4EF6\u6807\u8BB0\u300D\u540C\u65F6\u51FA\u73B0,\n // \u907F\u514D\u628A\u666E\u901A\u5E26 @@ \u7684\u6587\u672C\u8BEF\u5224\u6210 diff\u3002\n if (lines.some((line) => line.startsWith('diff --git '))) return true\n const hasHunk = lines.some((line) => /^@@ -\\d+/.test(line))\n const hasFileMark = lines.some((line) => /^(---|\\+\\+\\+) \\S/.test(line))\n return hasHunk && hasFileMark\n}\n\n/** \u89E3\u6790\u6574\u6BB5 unified diff \u6587\u672C\u3002 */\nexport function parseGitDiff(text: string): FileDiff[] {\n const lines = text.split(/\\r?\\n/)\n const files: FileDiff[] = []\n // \u5F53\u524D\u662F\u5426\u5904\u4E8E\u67D0\u4E2A hunk \u7684\u6B63\u6587\u533A(\u8DEF\u5F84\u884C ---/+++ \u53EA\u5728 hunk \u5916\u51FA\u73B0)\u3002\n let inHunk = false\n // \u5F53\u524D hunk \u7684\u65E7/\u65B0\u884C\u53F7\u6E38\u6807(\u4ECE hunk \u5934\u58F0\u660E\u7684\u8D77\u70B9\u5F00\u59CB\u9012\u589E)\u3002\n let oldNo = 0\n let newNo = 0\n // \u7B49\u5F85\u914D\u5BF9\u7684\u5220\u9664\u884C:\u8FDE\u7EED\u300C-\u300D\u5757\u4F1A\u5148\u7F13\u5B58,\u9047\u5230\u300C+\u300D\u65F6\u9010\u5BF9\u914D\u5BF9\u6210\n // change \u884C;hunk \u7ED3\u675F\u6216\u9047\u5230\u4E0A\u4E0B\u6587\u884C\u65F6,\u5269\u4F59\u7F13\u5B58\u6309 del \u884C\u8F93\u51FA\u3002\n // noEol \u6807\u8BB0\u53EF\u80FD\u7D27\u8DDF\u300C-\u300D\u884C\u51FA\u73B0(\),\n // \u6240\u4EE5\u8981\u7F13\u5B58\u8FDB\u5F85\u914D\u5BF9\u6761\u76EE,\u914D\u5BF9\u65F6\u624D\u80FD\u5E26\u5230 change \u884C\u7684\u65E7\u4FA7\u3002\n let pendingDels: Array<{ text: string; no: number; noEol: boolean }> = []\n // \u6700\u8FD1\u4E00\u6761\u5185\u5BB9\u884C(\u975E gap)\u7684\u7C7B\u578B,\u7528\u4E8E\u628A \\ No newline \u6807\u8BB0\u6302\u5230\u6B63\u786E\u7684\u4E00\u4FA7\u3002\n let lastKind: 'ctx' | 'del' | 'add' | null = null\n // \u6700\u8FD1\u4E00\u6761\u5DF2\u8F93\u51FA(\u6216\u5DF2\u914D\u5BF9)\u7684\u3001\u5E26\u65B0\u4FA7\u7684\u884C,\u7528\u4E8E\u7ED9\u65B0\u4FA7\u6302 noEol\u3002\n let lastNewRow: DiffRow | null = null\n\n /** \u5F53\u524D\u6B63\u5728\u7D2F\u79EF\u7684\u6587\u4EF6(\u6808\u9876);\u8FD8\u6CA1\u6709 diff \u5934\u65F6\u4E3A null\u3002 */\n function current(): FileDiff | null {\n return files.length > 0 ? files[files.length - 1] : null\n }\n\n /** \u628A\u5F85\u914D\u5BF9\u7F13\u5B58\u6309 del \u884C\u5237\u51FA(\u9047\u4E0A\u4E0B\u6587\u3001\u65B0 hunk\u3001\u6587\u4EF6\u5207\u6362\u3001\u6587\u672C\u7ED3\u675F\u65F6\u8C03\u7528)\u3002 */\n function flushPending(): void {\n const file = current()\n if (file === null || pendingDels.length === 0) return\n for (const del of pendingDels) {\n file.rows.push({\n kind: 'del',\n oldNo: del.no,\n oldText: del.text,\n oldNoEol: del.noEol,\n })\n lastNewRow = null\n }\n pendingDels = []\n }\n\n /** \u65B0\u5EFA\u6587\u4EF6\u5E76\u63A5\u7BA1\u6E38\u6807(\u81EA\u52A8\u5237\u51FA\u4E0A\u4E00\u4E2A\u6587\u4EF6\u7684\u5F85\u914D\u5BF9\u7F13\u5B58)\u3002 */\n function startFile(oldPath: string, newPath: string): void {\n flushPending()\n inHunk = false\n lastKind = null\n lastNewRow = null\n files.push({\n oldPath,\n newPath,\n status: 'modified',\n meta: [],\n rows: [],\n })\n }\n\n /** \u89E3\u6790 @@ hunk \u5934,\u8FD4\u56DE\u662F\u5426\u6210\u529F(\u5931\u8D25\u884C\u539F\u6837\u5F53\u6B63\u6587\u5904\u7406)\u3002 */\n function openHunk(line: string): boolean {\n const match = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/.exec(line)\n if (match === null) return false\n flushPending()\n oldNo = Number(match[1])\n newNo = Number(match[3])\n inHunk = true\n lastKind = null\n lastNewRow = null\n // \u4FDD\u7559\u6574\u4E2A hunk \u5934\u539F\u6587(\u542B\u5C3E\u90E8 section \u540D),\u6E32\u67D3\u6210\u95F4\u9699\u884C\u3002\n current()?.rows.push({ kind: 'gap', gapText: line })\n return true\n }\n\n /** \u8F93\u51FA\u4E00\u6761 ctx \u884C(\u5E26\u4E24\u4FA7\u884C\u53F7\u4E0E\u6587\u672C)\u3002 */\n function emitContext(text: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = { kind: 'ctx', oldNo, newNo, oldText: text, newText: text }\n file.rows.push(row)\n oldNo += 1\n newNo += 1\n lastKind = 'ctx'\n lastNewRow = row\n }\n\n /** \u8F93\u51FA\u4E00\u6761 change \u884C(\u65E7\u4FA7 del \u4E0E \u65B0\u4FA7 add \u914D\u5BF9)\u3002 */\n function emitChange(del: { text: string; no: number; noEol: boolean }, addText: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = {\n kind: 'change',\n oldNo: del.no,\n newNo,\n oldText: del.text,\n newText: addText,\n oldNoEol: del.noEol,\n }\n file.rows.push(row)\n newNo += 1\n lastKind = 'add'\n lastNewRow = row\n }\n\n /** \u8F93\u51FA\u4E00\u6761 add \u884C\u3002 */\n function emitAdd(text: string): void {\n const file = current()\n if (file === null) return\n const row: DiffRow = { kind: 'add', newNo, newText: text }\n file.rows.push(row)\n newNo += 1\n lastKind = 'add'\n lastNewRow = row\n }\n\n /** \u5904\u7406 \ \u6807\u8BB0:\u6302\u5230\u4E0A\u4E00\u884C\u5BF9\u5E94\u7684\u4E00\u4FA7\u3002 */\n function markNoEol(): void {\n if (lastKind === 'del') {\n // \u4E0A\u4E00\u4E2A\u300C-\u300D\u53EF\u80FD\u8FD8\u5728\u5F85\u914D\u5BF9\u7F13\u5B58\u91CC(\u8FD8\u6CA1\u9047\u5230\u914D\u5BF9/\u5237\u51FA)\u3002\n const last = pendingDels[pendingDels.length - 1]\n if (last !== undefined) {\n last.noEol = true\n } else {\n const file = current()\n if (file !== null && file.rows.length > 0) {\n // \u5DF2\u88AB\u5237\u6210 del \u884C:\u6807\u8BB0\u65E7\u4FA7\u3002\n const row = file.rows[file.rows.length - 1]\n if (row.kind === 'del') row.oldNoEol = true\n }\n }\n } else if (lastKind === 'add') {\n // \u4E0A\u4E00\u884C\u662F change \u6216 add:\u6807\u8BB0\u65B0\u4FA7\u3002\n if (lastNewRow !== null) lastNewRow.newNoEol = true\n } else if (lastKind === 'ctx') {\n const file = current()\n if (file !== null && file.rows.length > 0) {\n const row = file.rows[file.rows.length - 1]\n if (row.kind === 'ctx') {\n row.oldNoEol = true\n row.newNoEol = true\n }\n }\n }\n }\n\n for (const line of lines) {\n // \u7A7A\u884C:\u6587\u672C\u672B\u5C3E\u6362\u884C\u7B26\u7684\u62C6\u5206\u4EA7\u7269(unified diff \u7684\u5185\u5BB9\u884C\u90FD\u5E26\u524D\u7F00\n // \u5B57\u7B26,\u771F\u6B63\u7684\u7A7A\u5185\u5BB9\u884C\u662F \" \" \u800C\u4E0D\u662F \"\"),\u76F4\u63A5\u8DF3\u8FC7\u3002\n if (line === '') continue\n if (line.startsWith('diff --git ')) {\n // \u65B0\u6587\u4EF6\u5934:\u5C1D\u8BD5\u4ECE a/X b/Y \u63D0\u53D6\u8DEF\u5F84,\u63D0\u53D6\u5931\u8D25\u4E5F\u4E0D\u963B\u585E\u89E3\u6790\u3002\n const match = /^diff --git (?:a\\/)?(\\S+) (?:b\\/)?(\\S+)$/.exec(line)\n startFile(match?.[1] ?? '', match?.[2] ?? '')\n continue\n }\n if (line.startsWith('@@ ')) {\n openHunk(line)\n continue\n }\n const cur = current()\n if (cur === null) {\n // diff \u5934\u4E4B\u524D\u7684\u524D\u5BFC\u5185\u5BB9(\u63D0\u4EA4\u4FE1\u606F\u7B49)\u8DF3\u8FC7;\u88F8 patch(\u65E0 diff --git\n // \u5934,\u5982 git diff --no-index \u7684\u5408\u5E76\u683C\u5F0F)\u4EE5 --- \u884C\u76F4\u63A5\u5F00\u6587\u4EF6\u3002\n if (line.startsWith('--- ') && line.length > 4) startFile(line.slice(4), '')\n continue\n }\n if (!inHunk) {\n // \u2014\u2014 hunk \u5916\u7684\u5143\u4FE1\u606F\u884C \u2014\u2014\n if (line.startsWith('--- ') && line.length > 4) {\n cur.oldPath = line.slice(4)\n } else if (line.startsWith('+++ ') && line.length > 4) {\n cur.newPath = line.slice(4)\n } else if (line.startsWith('new file mode ')) {\n cur.status = 'added'\n cur.meta.push(line)\n } else if (line.startsWith('deleted file mode ')) {\n cur.status = 'deleted'\n cur.meta.push(line)\n } else if (line.startsWith('rename from ') || line.startsWith('rename to ')) {\n cur.status = 'renamed'\n cur.meta.push(line)\n } else if (\n line.startsWith('old mode ') ||\n line.startsWith('new mode ') ||\n line.startsWith('similarity index ') ||\n line.startsWith('dissimilarity index ') ||\n line.startsWith('copy from ') ||\n line.startsWith('copy to ')\n ) {\n cur.meta.push(line)\n } else if (line.startsWith('Binary files ') || line.startsWith('GIT binary patch')) {\n // \u4E8C\u8FDB\u5236\u6587\u4EF6:\u6CA1\u6709 hunk,\u6E32\u67D3\u6210\u4FE1\u606F\u884C\u3002\n cur.status = 'binary'\n cur.meta.push(line)\n } else if (line.startsWith('index ')) {\n // index \u884C(blob \u54C8\u5E0C)\u4FE1\u606F\u91CF\u4F4E,\u6536\u8FDB meta \u4F46\u6E32\u67D3\u5C42\u9009\u62E9\u5FFD\u7565\u3002\n cur.meta.push(line)\n }\n // \u5176\u4ED6(\u7A7A\u884C\u7B49)\u5FFD\u7565\u3002\n continue\n }\n // \u2014\u2014 hunk \u6B63\u6587 \u2014\u2014\n const kind = line[0]\n const body = line.slice(1)\n if (kind === ' ') {\n flushPending()\n emitContext(body)\n } else if (kind === '-') {\n pendingDels.push({ text: body, no: oldNo, noEol: false })\n oldNo += 1\n lastKind = 'del'\n } else if (kind === '+') {\n const del = pendingDels.shift()\n if (del !== undefined) {\n emitChange(del, body)\n } else {\n emitAdd(body)\n }\n } else if (kind === '\\\\' && line.startsWith('\\\')) {\n markNoEol()\n } else {\n // \u65E0\u6CD5\u8BC6\u522B\u7684\u884C(\u7578\u5F62 diff):\u6309\u4E0A\u4E0B\u6587\u5BBD\u5BB9\u5904\u7406,\u4FDD\u8BC1\u89C6\u56FE\u4E0D\u9519\u4F4D\u3002\n flushPending()\n emitContext(line)\n }\n }\n flushPending()\n return files\n}\n\n/**\n * \u7EDF\u8BA1\u6574\u4E2A\u89E3\u6790\u7ED3\u679C:\u65B0\u589E\u884C\u6570 / \u5220\u9664\u884C\u6570(change \u884C\u4E24\u4FA7\u5404\u8BA1\u4E00\u6B21)\u3002\n * \u4F9B\u5DE5\u5177\u680F\u300C+N \u2212M \u00B7 F \u4E2A\u6587\u4EF6\u300D\u4F7F\u7528\u3002\n */\nexport function countStats(files: FileDiff[]): { added: number; removed: number; files: number } {\n let added = 0\n let removed = 0\n for (const file of files) {\n for (const row of file.rows) {\n if (row.kind === 'add' || row.kind === 'change') added += 1\n if (row.kind === 'del' || row.kind === 'change') removed += 1\n }\n }\n return { added, removed, files: files.length }\n}\n", "/**\n * \u4EE3\u7801\u9AD8\u4EAE\u7684\u7EAF\u903B\u8F91(\u4E0D\u4F9D\u8D56 DOM,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u4E00\u3001\u57FA\u7840\u8BED\u6CD5\u9AD8\u4EAE:\u8F7B\u91CF\u5355\u884C tokenizer(\u4E0D\u5F15\u5165\u9AD8\u4EAE\u5E93,\u72EC\u7ACB\u9875\u96F6\u4F9D\u8D56),\n * \u5207\u51FA \u5173\u952E\u5B57/\u5B57\u7B26\u4E32/\u6CE8\u91CA/\u6570\u5B57 \u56DB\u7C7B token,\u6E32\u67D3\u5C42\u6620\u5C04\u6210\u5B98\u65B9\n * --shiki-token-* \u989C\u8272(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4)\u3002\n * \u652F\u6301\u6CE8\u91CA\u98CE\u683C:\u53CC\u659C\u6760\u3001\u659C\u6760\u661F\u53F7(\u884C\u5185)\u3001\u4E95\u53F7(Python/shell \u7CFB,\n * \u6309\u6269\u5C55\u540D\u5F00\u542F)\u3002\u5757\u6CE8\u91CA\u4E0D\u8DE8\u884C(\u57FA\u7840\u7248\u53D6\u820D,\u5355\u884C\u6E32\u67D3\u573A\u666F\u5F71\u54CD\u5F88\u5C0F)\u3002\n *\n * \u4E8C\u3001\u884C\u5185\u5B57\u7B26\u7EA7 diff:\u5BF9 change \u884C(\u540C\u4E00\u884C\u65E7/\u65B0\u914D\u5BF9)\u505A\u516C\u5171\u524D\u7F00/\u540E\u7F00\n * \u5265\u79BB,\u4E2D\u95F4\u4E0D\u540C\u7684\u5B57\u7B26\u6BB5\u5C31\u662F\u300C\u53EA\u6539\u4E86\u51E0\u4E2A\u5B57\u7B26\u300D\u7684\u7CBE\u786E\u8303\u56F4,\u6E32\u67D3\u5C42\u7528\n * \u52A0\u6DF1\u80CC\u666F\u8272\u9AD8\u4EAE\u3002\u7D22\u5F15\u6309 code point(Array.from)\u8BA1\u7B97,\u6E32\u67D3\u5C42\u540C\u6837\u6309\n * code point \u5207\u7247,\u4EE3\u7406\u5BF9\u4E0D\u4F1A\u4ECE\u4E2D\u95F4\u65AD\u5F00\u3002\n */\n\n/** \u8BED\u6CD5 token(\u7EAF\u6587\u672C\u5207\u5206,HTML \u8F6C\u4E49\u7531\u6E32\u67D3\u5C42\u505A)\u3002 */\nexport interface Token {\n text: string\n /** null = \u666E\u901A\u6587\u672C;\u5176\u4F59\u4E3A\u8BED\u6CD5\u7C7B\u522B,\u6620\u5C04\u5230 .tok-* \u6837\u5F0F\u3002 */\n cls: 'tok-kw' | 'tok-str' | 'tok-com' | 'tok-num' | null\n}\n\n/** \u884C\u5185\u5DEE\u5F02\u7684\u4E2D\u6BB5\u8303\u56F4(code point \u7D22\u5F15,\u524D\u95ED\u540E\u5F00;null = \u65E0\u5DEE\u5F02)\u3002 */\nexport interface DiffMidRange {\n oldStart: number\n oldEnd: number\n newStart: number\n newEnd: number\n}\n\n/** \u5355\u884C\u9AD8\u4EAE\u914D\u7F6E:\u662F\u5426\u652F\u6301 # \u6CE8\u91CA(Python/shell/YAML \u7B49)\u3002 */\nexport interface LineLang {\n hashComment: boolean\n}\n\n/**\n * \u5408\u5E76\u5E38\u89C1\u8BED\u8A00\u7684\u5173\u952E\u5B57\u8868(\u57FA\u7840\u9AD8\u4EAE\u4E0D\u6309\u8BED\u8A00\u7EC6\u5206,\u4E00\u5957\u901A\u7528\u8BCD\u8868\u8986\u76D6\n * JS/TS\u3001Python\u3001Go\u3001Rust\u3001Java/C \u7CFB\u7684\u7EDD\u5927\u591A\u6570\u5173\u952E\u5B57)\u3002\n */\nconst KEYWORDS = new Set([\n // JS/TS\n 'const', 'let', 'var', 'function', 'class', 'extends', 'super', 'new', 'delete',\n 'typeof', 'instanceof', 'in', 'of', 'this', 'static', 'get', 'set', 'async',\n 'await', 'yield', 'return', 'if', 'else', 'switch', 'case', 'default', 'break',\n 'continue', 'try', 'catch', 'finally', 'throw', 'for', 'while', 'do', 'import',\n 'export', 'from', 'as', 'interface', 'type', 'enum', 'namespace', 'module',\n 'implements', 'public', 'private', 'protected', 'readonly', 'abstract',\n 'declare', 'keyof', 'infer', 'never', 'unknown', 'any', 'void', 'null',\n 'undefined', 'true', 'false', 'NaN', 'Infinity', 'require',\n // Python\n 'def', 'lambda', 'pass', 'raise', 'elif', 'not', 'and', 'or', 'is', 'None',\n 'True', 'False', 'print', 'with', 'global', 'nonlocal', 'assert', 'del',\n // Go\n 'go', 'func', 'package', 'struct', 'map', 'chan', 'select', 'defer', 'range',\n 'nil', 'goto', 'fallthrough',\n // Rust\n 'fn', 'let', 'mut', 'pub', 'impl', 'trait', 'match', 'use', 'mod', 'self',\n 'crate', 'unsafe', 'where', 'loop', 'move', 'ref', 'dyn',\n // Java / C \u7CFB\n 'int', 'float', 'double', 'char', 'boolean', 'byte', 'short', 'long', 'signed',\n 'unsigned', 'auto', 'register', 'extern', 'volatile', 'constexpr', 'sizeof',\n 'template', 'typename',\n])\n\n/** \u505A\u8BED\u6CD5\u9AD8\u4EAE\u7684\u6269\u5C55\u540D\u96C6\u5408(\u5176\u4F59\u6269\u5C55\u540D\u4E0D\u505A\u8BED\u6CD5\u9AD8\u4EAE,\u53EA\u505A\u5B57\u7B26\u7EA7 diff)\u3002 */\nconst HIGHLIGHT_EXTS = new Set([\n 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'json', 'py', 'rb', 'go', 'rs',\n 'java', 'c', 'h', 'cpp', 'cc', 'cs', 'css', 'scss', 'less', 'html', 'htm',\n 'vue', 'xml', 'sql', 'md', 'yml', 'yaml', 'toml', 'sh', 'bash', 'zsh', 'ini',\n])\n\n/** # \u6CE8\u91CA\u8BED\u8A00\u7684\u6269\u5C55\u540D(Python/shell/YAML \u7CFB;JS \u7684 # \u662F\u79C1\u6709\u5B57\u6BB5,\u4E0D\u5F00)\u3002 */\nconst HASH_COMMENT_EXTS = new Set(['py', 'rb', 'sh', 'bash', 'zsh', 'yml', 'yaml', 'toml', 'ini'])\n\n/** \u6309\u6587\u4EF6\u8DEF\u5F84\u63A8\u65AD\u9AD8\u4EAE\u914D\u7F6E;\u65E0\u6CD5\u8BC6\u522B\u65F6\u8FD4\u56DE null(\u53EA\u505A\u5B57\u7B26\u7EA7 diff)\u3002 */\nexport function langFor(path: string): LineLang | null {\n const dot = path.lastIndexOf('.')\n const ext = dot >= 0 ? path.slice(dot + 1).toLowerCase() : ''\n if (ext === '' || !HIGHLIGHT_EXTS.has(ext)) return null\n return { hashComment: HASH_COMMENT_EXTS.has(ext) }\n}\n\n/** \u662F\u5426\u6807\u8BC6\u7B26\u8D77\u59CB\u5B57\u7B26\u3002 */\nfunction isIdentStart(ch: string): boolean {\n return /[A-Za-z_]/.test(ch)\n}\n\n/** \u662F\u5426\u6807\u8BC6\u7B26\u540E\u7EED\u5B57\u7B26\u3002 */\nfunction isIdentPart(ch: string): boolean {\n return /[A-Za-z0-9_]/.test(ch)\n}\n\n/** \u662F\u5426\u6570\u5B57\u8D77\u59CB\u5B57\u7B26\u3002 */\nfunction isDigitStart(ch: string): boolean {\n return /[0-9]/.test(ch)\n}\n\n/** \u662F\u5426\u6570\u5B57\u540E\u7EED\u5B57\u7B26(\u542B\u5341\u516D\u8FDB\u5236/\u4E8C\u8FDB\u5236\u524D\u7F00\u4E0E\u5C0F\u6570\u70B9)\u3002 */\nfunction isDigitPart(ch: string): boolean {\n return /[0-9a-fA-FxXbBoO._]/.test(ch)\n}\n\n/**\n * \u5355\u884C tokenize:\u6309 \u5B57\u7B26\u4E32 \u2192 \u6CE8\u91CA \u2192 \u6570\u5B57 \u2192 \u6807\u8BC6\u7B26 \u7684\u987A\u5E8F\u626B\u63CF,\n * \u5176\u4F59\u5B57\u7B26\u5F52\u5165\u666E\u901A\u6587\u672C\u3002\u884C\u5185 token \u72B6\u6001\u4E0D\u8DE8\u884C(\u57FA\u7840\u7248\u53D6\u820D)\u3002\n */\nexport function tokenizeLine(text: string, lang: LineLang): Token[] {\n const tokens: Token[] = []\n let plain = ''\n const flushPlain = (): void => {\n if (plain !== '') {\n tokens.push({ text: plain, cls: null })\n plain = ''\n }\n }\n let i = 0\n while (i < text.length) {\n const ch = text[i]\n // \u5B57\u7B26\u4E32:\u4E09\u79CD\u5F15\u53F7,\u53CD\u659C\u6760\u8F6C\u4E49\u8DF3\u4E24\u683C;\u884C\u5C3E\u672A\u95ED\u5408\u5219\u5230\u884C\u5C3E\u3002\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n let j = i + 1\n while (j < text.length) {\n if (text[j] === '\\\\') {\n j += 2\n continue\n }\n if (text[j] === ch) break\n j += 1\n }\n flushPlain()\n tokens.push({ text: text.slice(i, Math.min(j + 1, text.length)), cls: 'tok-str' })\n i = Math.min(j + 1, text.length)\n continue\n }\n // \u884C\u6CE8\u91CA // \u6216 /* */(\u884C\u5185;\u672A\u95ED\u5408\u5230\u884C\u5C3E)\n if (ch === '/' && text[i + 1] === '/') {\n flushPlain()\n tokens.push({ text: text.slice(i), cls: 'tok-com' })\n break\n }\n if (ch === '/' && text[i + 1] === '*') {\n const close = text.indexOf('*/', i + 2)\n const end = close === -1 ? text.length : close + 2\n flushPlain()\n tokens.push({ text: text.slice(i, end), cls: 'tok-com' })\n i = end\n continue\n }\n if (lang.hashComment && ch === '#') {\n flushPlain()\n tokens.push({ text: text.slice(i), cls: 'tok-com' })\n break\n }\n // \u6570\u5B57(\u524D\u7F00\u5B57\u6BCD/\u5C0F\u6570\u70B9/\u4E0B\u5212\u7EBF\u90FD\u7B97\u6570\u5B57\u7247\u6BB5)\n if (isDigitStart(ch)) {\n let j = i + 1\n while (j < text.length && isDigitPart(text[j])) j += 1\n flushPlain()\n tokens.push({ text: text.slice(i, j), cls: 'tok-num' })\n i = j\n continue\n }\n // \u6807\u8BC6\u7B26 \u2192 \u547D\u4E2D\u5173\u952E\u5B57\u8868\u5219\u6807 tok-kw\n if (isIdentStart(ch)) {\n let j = i + 1\n while (j < text.length && isIdentPart(text[j])) j += 1\n const word = text.slice(i, j)\n flushPlain()\n tokens.push({ text: word, cls: KEYWORDS.has(word) ? 'tok-kw' : null })\n i = j\n continue\n }\n plain += ch\n i += 1\n }\n flushPlain()\n return tokens\n}\n\n/**\n * \u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u8303\u56F4:\u516C\u5171\u524D\u7F00/\u540E\u7F00\u5265\u79BB,\u4E2D\u95F4\u5269\u4F59\u90E8\u5206\u5373\u5DEE\u5F02\u6BB5\u3002\n * \u8986\u76D6\u300C\u4E00\u884C\u53EA\u6539\u51E0\u4E2A\u5B57\u7B26\u300D\u7684\u7CBE\u786E\u9AD8\u4EAE;\u6574\u884C\u91CD\u5199\u65F6\u4E2D\u95F4\u6BB5\u63A5\u8FD1\u6574\u884C,\n * \u6548\u679C\u4E0E\u6574\u884C\u9AD8\u4EAE\u4E00\u81F4(\u53EF\u63A5\u53D7)\u3002\u76F8\u540C\u6587\u672C\u8FD4\u56DE null\u3002\n */\nexport function diffMidRange(oldText: string, newText: string): DiffMidRange | null {\n const a = Array.from(oldText)\n const b = Array.from(newText)\n // \u516C\u5171\u524D\u7F00\n let pre = 0\n const minLen = Math.min(a.length, b.length)\n while (pre < minLen && a[pre] === b[pre]) pre += 1\n // \u516C\u5171\u540E\u7F00(\u4E0D\u56DE\u9000\u8FDB\u524D\u7F00\u533A)\n let oldEnd = a.length\n let newEnd = b.length\n while (oldEnd > pre && newEnd > pre && a[oldEnd - 1] === b[newEnd - 1]) {\n oldEnd -= 1\n newEnd -= 1\n }\n if (pre === a.length && pre === b.length) return null\n return { oldStart: pre, oldEnd, newStart: pre, newEnd }\n}\n", "/**\n * \u72EC\u7ACB Code Review \u6807\u7B7E\u9875\u7684 HTML \u6784\u5EFA(\u7EAF\u5B57\u7B26\u4E32,\u53EF\u5728 Node \u91CC\u5355\u6D4B)\u3002\n *\n * \u804A\u5929\u7A97\u53E3\u5185\u4E0D\u518D\u505A\u53CC\u5217\u8F6C\u6362 \u2014\u2014 \u53EA\u5728\u68C0\u6D4B\u5230 git diff \u7684\u5757\u4E0A\u52A0\u4E00\u4E2A\n * \u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE;\u70B9\u51FB\u540E\u5728\u72EC\u7ACB\u6D4F\u89C8\u5668\u6807\u7B7E\u9875(Blob URL,\u81EA\u5305\u542B)\n * \u6E32\u67D3\u5B8C\u6574\u53CC\u5217\u89C6\u56FE:\u5DE6\u4FA7\u6587\u4EF6\u5BFC\u822A\u3001\u5168\u5BBD\u3001\u6574\u9875\u6EDA\u52A8\u3001\u65E0\u9AD8\u5EA6\u9650\u5236\u3002\n *\n * \u5E03\u5C40(\u4E0E\u5B98\u65B9 DiffBlock \u540C\u6E90\u7684\u53CC\u5217\u8BED\u4E49):\n * - ctx \u4E0A\u4E0B\u6587\u884C:\u4E24\u4FA7\u540C\u6587\u3001\u65E0\u5E95\u8272;\n * - del \u5220\u9664\u884C:\u4EC5\u65E7\u4FA7,\u7EA2\u5E95;\n * - add \u65B0\u589E\u884C:\u4EC5\u65B0\u4FA7,\u7EFF\u5E95;\n * - change \u53D8\u66F4\u5BF9:\u65E7\u4FA7\u7EA2\u5E95 + \u65B0\u4FA7\u7EFF\u5E95(\u8FDE\u7EED - \u4E0E + \u7684\u914D\u5BF9,\u89C1 diff-parse);\n * - gap @@ hunk \u5934,\u6A2A\u8DE8\u56DB\u5217\u3002\n */\n\nimport type { DiffRow, FileDiff } from './diff-parse'\nimport { countStats, displayPath } from './diff-parse'\nimport { diffMidRange, langFor, tokenizeLine } from './diff-highlight'\nimport type { DiffMidRange, LineLang, Token } from './diff-highlight'\n\n/** HTML \u8F6C\u4E49:diff \u884C\u5185\u5BB9\u662F\u4E0D\u53EF\u4FE1\u6587\u672C,\u76F4\u63A5\u62FC HTML \u5FC5\u987B\u5148\u8F6C\u4E49\u3002 */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\n/** \u72EC\u7ACB\u9875\u7684 CSS:\u81EA\u5305\u542B,\u989C\u8272\u5168\u90E8\u8D70\u6CE8\u5165\u7684\u4E3B\u9898\u53D8\u91CF(\u4E0E DSH \u4E3B\u9898\u4E00\u81F4)\u3002 */\nconst STANDALONE_CSS = `\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 13px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n}\n.topbar {\n position: sticky;\n top: 0;\n z-index: 10;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 0 16px;\n background: var(--dsw-alias-markdown-code-block);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.stats { font-size: 13px; color: var(--dsw-alias-label-secondary); }\n.topbar-actions { display: flex; gap: 8px; }\n.btn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n background: transparent;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 2px 10px;\n cursor: pointer;\n font-family: inherit;\n}\n.btn:hover { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-border-l3); }\n.sidenav {\n position: fixed;\n left: 0;\n top: 36px;\n bottom: 0;\n width: 220px;\n overflow-y: auto;\n padding: 12px 8px;\n background: var(--dsw-alias-markdown-code-block);\n border-right: 1px solid var(--dsw-alias-border-l2);\n}\n.navitem {\n display: block;\n padding: 4px 12px;\n font-size: 12px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n text-decoration: none;\n border-radius: 6px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.navitem:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); }\nmain { margin-left: 220px; padding: 16px 24px 60px; }\n.file { margin-bottom: 24px; background: var(--dsw-alias-markdown-code-block); border-radius: 12px; overflow: hidden; }\n.filehead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 8px 12px 4px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n.badge {\n flex: none;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 0 6px;\n}\n.path { overflow-wrap: anywhere; }\n.meta { flex-basis: 100%; font-size: 11px; color: var(--dsw-alias-label-tertiary); }\n.dsh-cr-grid {\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);\n}\n.dsh-cr-row { display: contents; }\n.dsh-cr-num {\n padding: 0 10px 0 4px;\n text-align: right;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: nowrap;\n}\n.dsh-cr-cell {\n padding: 0 12px 0 4px;\n min-width: 0;\n white-space: pre-wrap;\n word-break: break-word;\n /* \u5236\u8868\u7B26\u6309 4 \u4E2A\u5B57\u7B26\u5BBD\u5EA6\u6E32\u67D3(\u6D4F\u89C8\u5668\u9ED8\u8BA4 8,\u4EE3\u7801\u9605\u8BFB\u592A\u677E) */\n tab-size: 4;\n}\n.dsh-cr-del .dsh-cr-num-old, .dsh-cr-del .dsh-cr-cell-old,\n.dsh-cr-change .dsh-cr-num-old, .dsh-cr-change .dsh-cr-cell-old {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n}\n.dsh-cr-add .dsh-cr-num-new, .dsh-cr-add .dsh-cr-cell-new,\n.dsh-cr-change .dsh-cr-num-new, .dsh-cr-change .dsh-cr-cell-new {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n}\n.dsh-cr-gaprow {\n grid-column: 1 / -1;\n padding: 2px 12px;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 4%, transparent);\n user-select: none;\n}\n.dsh-cr-nonl { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n/* \u8BED\u6CD5\u9AD8\u4EAE:\u989C\u8272\u590D\u7528\u5B98\u65B9 shiki \u53D8\u91CF(\u4E0E DSH \u6DF1\u6D45\u4E3B\u9898\u4E00\u81F4,\u53D6\u4E0D\u5230\u65F6\u515C\u5E95) */\n.tok-kw { color: var(--shiki-token-keyword, #7c3aed); }\n.tok-str { color: var(--shiki-token-string, #16a34a); }\n.tok-com { color: var(--shiki-token-comment, #9aa0a6); font-style: italic; }\n.tok-num { color: var(--shiki-token-constant, #d97706); }\n/* \u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02:change \u884C\u4E2D\u6BB5\u52A0\u6DF1\u80CC\u666F(\u65E7\u4FA7\u7EA2/\u65B0\u4FA7\u7EFF) */\n.dihl-mid-old {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 32%, transparent);\n border-radius: 2px;\n}\n.dihl-mid-new {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 32%, transparent);\n border-radius: 2px;\n}\n/* \u884C\u5185\u8BC4\u8BBA:\u884C\u53F7\u683C\u53EF\u70B9\u51FB,hover \u51FA\u73B0 + \u53F7 */\n.dsh-cr-num[data-cr-line] {\n cursor: pointer;\n position: relative;\n}\n.dsh-cr-num[data-cr-line]:hover {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cr-num[data-cr-line]:hover::after {\n content: '+';\n position: absolute;\n left: 2px;\n top: 0;\n color: var(--dsw-alias-state-business-primary);\n font-weight: 700;\n}\n.cc {\n margin-left: 5px;\n font-size: 10px;\n line-height: 15px;\n color: #fff;\n background: var(--dsw-alias-state-business-primary);\n border-radius: 8px;\n padding: 0 5px;\n user-select: none;\n}\n.comment-draft {\n grid-column: 1 / -1;\n padding: 8px 12px;\n background: var(--dsw-alias-bg-layer-1);\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.comment-list { margin-bottom: 8px; }\n.comment-item {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n margin-bottom: 4px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.comment-input {\n width: 100%;\n min-height: 60px;\n resize: vertical;\n box-sizing: border-box;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n}\n.comment-actions { display: flex; gap: 8px; margin-top: 8px; }\n.submit-panel {\n position: fixed;\n top: 44px;\n right: 16px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--dsw-alias-markdown-code-block);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n}\n.submit-hint { font-size: 12px; color: var(--dsw-alias-label-secondary); }\n.rawwrap { margin: 16px 24px; }\n.rawsummary { font-size: 12px; color: var(--dsw-alias-label-secondary); cursor: pointer; }\n.rawpre { white-space: pre-wrap; word-break: break-word; font: inherit; margin: 8px 0 0; tab-size: 4; }\n`\n\n/** \u53D8\u66F4\u7C7B\u578B\u7684\u5FBD\u7AE0\u6587\u6848(\u4E2D\u6587\u9ED8\u8BA4,\u7531 CR_TEXTS \u8986\u76D6)\u3002 */\nconst STANDALONE_STATUS_LABEL: Record<FileDiff['status'], string> = {\n modified: '\u4FEE\u6539',\n added: '\u65B0\u589E\u6587\u4EF6',\n deleted: '\u5220\u9664\u6587\u4EF6',\n renamed: '\u91CD\u547D\u540D',\n binary: '\u4E8C\u8FDB\u5236',\n mode: '\u6743\u9650\u53D8\u66F4',\n}\n\n/** \u754C\u9762\u8BED\u8A00(DSH \u9875\u9762 lang \u4EE5 zh \u5F00\u5934\u7B97\u4E2D\u6587,\u5176\u4F59\u4E00\u5F8B\u82F1\u6587)\u3002 */\nexport type Lang = 'zh' | 'en'\n\n/**\n * \u5168\u90E8\u7528\u6237\u53EF\u89C1\u6587\u6848\u7684\u4E2D/\u82F1\u8BCD\u5178\u3002\u804A\u5929\u5185\u6309\u94AE\u3001\u72EC\u7ACB\u6807\u7B7E\u9875\u3001\u8BC4\u8BBA\u62A5\u544A\n * \u4E09\u5904\u5171\u7528;\u72EC\u7ACB\u6807\u7B7E\u9875\u8131\u79BB DSH \u9875\u9762\u62FF\u4E0D\u5230\u5B98\u65B9 locale,\u7531\u751F\u6210\u65F6\u628A\n * \u8BED\u8A00\u8FDE\u540C\u8BCD\u5178\u4E00\u8D77\u4F20\u5165\u3002\u51FD\u6570\u5B57\u6BB5\u4EC5\u4F9B TS \u4FA7\u6C42\u503C,\u6CE8\u5165\u6807\u7B7E\u9875\u811A\u672C\u65F6\n * \u53EA\u6311\u5B57\u7B26\u4E32\u5B57\u6BB5(JSON \u5E8F\u5217\u5316\u4F1A\u4E22\u51FD\u6570)\u3002\n */\nexport interface CrTexts {\n // \u2014\u2014 \u804A\u5929\u5185\u6309\u94AE \u2014\u2014\n openTab: string\n openTabTitle: string\n expand: string\n collapse: string\n expandTitle: string\n collapseTitle: string\n // \u2014\u2014 \u8BC4\u8BBA\u62A5\u544A(\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846)\u2014\u2014\n reportLgtmHeader: (count: number) => string\n reportCommentHeader: (count: number) => string\n // \u2014\u2014 \u72EC\u7ACB\u6807\u7B7E\u9875 \u2014\u2014\n stats: (added: number, removed: number, files: number) => string\n submitComments: string\n copyRaw: string\n copied: string\n sent: string\n submitHint: string\n submitLgtm: string\n submitPlain: string\n rawSummary: string\n commentPlaceholder: string\n addComment: string\n cancel: string\n noNewlineTitle: string\n openerClosed: string\n noSession: string\n status: Record<FileDiff['status'], string>\n}\n\nexport const CR_TEXTS: Record<Lang, CrTexts> = {\n zh: {\n openTab: '\u29C9 \u65B0\u6807\u7B7E\u9875\u6253\u5F00',\n openTabTitle: '\u5728\u72EC\u7ACB\u7684\u6D4F\u89C8\u5668\u6807\u7B7E\u9875\u4E2D\u6253\u5F00 Code Review \u89C6\u56FE',\n expand: '\u5C55\u5F00',\n collapse: '\u6536\u8D77',\n expandTitle: '\u5C55\u5F00\u539F\u59CB diff \u6587\u672C',\n collapseTitle: '\u6536\u8D77\u539F\u59CB diff \u6587\u672C',\n reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review \u5171 ${count} \u6761\u8BC4\u8BBA:`,\n reportCommentHeader: (count) => `Code Review \u610F\u89C1,\u5171 ${count} \u6761:`,\n stats: (added, removed, files) => `+${added} \u2212${removed} \u00B7 ${files} \u4E2A\u6587\u4EF6`,\n submitComments: '\u63D0\u4EA4\u8BC4\u8BBA',\n copyRaw: '\u590D\u5236\u539F\u59CB diff',\n copied: '\u5DF2\u590D\u5236',\n sent: '\u5DF2\u53D1\u9001 \u2713',\n submitHint: '\u628A\u5168\u90E8\u8BC4\u8BBA\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846:',\n submitLgtm: 'Looks Good To Me \u2713',\n submitPlain: '\u4EC5\u63D0\u4EA4\u8BC4\u8BBA',\n rawSummary: '\u539F\u59CB diff \u6587\u672C',\n commentPlaceholder: '\u5728\u6B64\u884C\u6DFB\u52A0\u8BC4\u8BBA\u2026',\n addComment: '\u6DFB\u52A0\u8BC4\u8BBA',\n cancel: '\u53D6\u6D88',\n noNewlineTitle: '\u8BE5\u4FA7\u6587\u4EF6\u672B\u5C3E\u65E0\u6362\u884C\u7B26',\n openerClosed: '\u539F\u6765\u7684 DSH \u9875\u9762\u5DF2\u5173\u95ED,\u65E0\u6CD5\u56DE\u4F20\u8BC4\u8BBA',\n noSession: '\u672A\u80FD\u5199\u5165 DSH \u5BF9\u8BDD\u6846:\u5F53\u524D\u6CA1\u6709\u6253\u5F00\u4E2D\u7684\u4F1A\u8BDD',\n status: STANDALONE_STATUS_LABEL,\n },\n en: {\n openTab: '\u29C9 Open in new tab',\n openTabTitle: 'Open the code review view in a separate browser tab',\n expand: 'Expand',\n collapse: 'Collapse',\n expandTitle: 'Show raw diff text',\n collapseTitle: 'Hide raw diff text',\n reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review with ${count} comment${count === 1 ? '' : 's'}:`,\n reportCommentHeader: (count) => `Code Review comments (${count}):`,\n stats: (added, removed, files) => `+${added} \u2212${removed} \u00B7 ${files} file${files === 1 ? '' : 's'}`,\n submitComments: 'Submit comments',\n copyRaw: 'Copy raw diff',\n copied: 'Copied',\n sent: 'Sent \u2713',\n submitHint: 'Submit all comments to the DSH composer:',\n submitLgtm: 'Looks Good To Me \u2713',\n submitPlain: 'Comments only',\n rawSummary: 'Raw diff text',\n commentPlaceholder: 'Add a comment on this line\u2026',\n addComment: 'Add comment',\n cancel: 'Cancel',\n noNewlineTitle: 'No newline at end of file',\n openerClosed: 'The original DSH page was closed; comments cannot be sent back',\n noSession: 'Could not write to the DSH composer: no open session',\n status: {\n modified: 'modified',\n added: 'new file',\n deleted: 'deleted',\n renamed: 'renamed',\n binary: 'binary',\n mode: 'mode change',\n },\n },\n}\n\n/** \u53EA\u6311\u5B57\u7B26\u4E32\u5B57\u6BB5,\u5E8F\u5217\u5316\u8FDB\u72EC\u7ACB\u9875\u811A\u672C(JSON \u4F1A\u4E22\u51FD\u6570\u5B57\u6BB5)\u3002 */\nfunction stringTexts(texts: CrTexts): Record<string, string> {\n return {\n submitComments: texts.submitComments,\n copyRaw: texts.copyRaw,\n copied: texts.copied,\n sent: texts.sent,\n submitHint: texts.submitHint,\n submitLgtm: texts.submitLgtm,\n submitPlain: texts.submitPlain,\n rawSummary: texts.rawSummary,\n commentPlaceholder: texts.commentPlaceholder,\n addComment: texts.addComment,\n cancel: texts.cancel,\n noNewlineTitle: texts.noNewlineTitle,\n openerClosed: texts.openerClosed,\n noSession: texts.noSession,\n }\n}\n\n/** \u6807\u7B7E\u9875\u56DE\u4F20\u7684\u4E00\u6761\u884C\u5185\u8BC4\u8BBA(\u7ECF postMessage,\u5F62\u72B6\u7531\u63D2\u4EF6\u4FA7\u6821\u9A8C)\u3002 */\nexport interface ReviewComment {\n path: string\n lineNo: number\n side?: string\n text: string\n}\n\n/**\n * \u7EC4\u88C5\u63D0\u4EA4\u5230 DSH \u5BF9\u8BDD\u6846\u7684 Code Review \u62A5\u544A\u6587\u672C(\u7EAF\u51FD\u6570,\u5355\u6D4B\u8986\u76D6)\u3002\n * \u683C\u5F0F:\u6587\u4EF6\u540D:\u884C\u53F7 \u2014 \u8BC4\u8BBA,\u9010\u6761\u7F16\u53F7;LGTM \u65F6\u5934\u90E8\u52A0\u6279\u51C6\u58F0\u660E\u3002\n * \u8BC4\u8BBA\u6587\u672C\u542B\u6362\u884C\u65F6,\u7EED\u884C\u7F29\u8FDB\u4E09\u683C\u4E0E\u6761\u76EE\u5185\u5BB9\u5BF9\u9F50,\u4E0D\u7834\u574F\u7F16\u53F7\u7ED3\u6784\u3002\n * @param comments \u884C\u5185\u8BC4\u8BBA\u5217\u8868\u3002\n * @param kind \u63D0\u4EA4\u7C7B\u578B:lgtm(\u6279\u51C6)/ comment(\u7EAF\u8BC4\u8BBA)\u3002\n * @param lang \u62A5\u544A\u8BED\u8A00(\u9ED8\u8BA4\u4E2D\u6587)\u3002\n */\nexport function buildCommentReport(\n comments: readonly ReviewComment[],\n kind: 'lgtm' | 'comment',\n lang: Lang = 'zh',\n): string {\n const texts = CR_TEXTS[lang]\n const header = kind === 'lgtm' ? texts.reportLgtmHeader(comments.length) : texts.reportCommentHeader(comments.length)\n const items = comments.map((comment, index) => {\n const lines = comment.text.split('\\n')\n const first = `${index + 1}. ${comment.path}:${comment.lineNo} \u2014 ${lines[0]}`\n // \u7EED\u884C\u7F29\u8FDB\u4E09\u683C(\u4E0E \"N. \" \u524D\u7F00\u7B49\u5BBD),\u907F\u514D\u4E0E\u4E0B\u4E00\u6761\u7F16\u53F7\u6DF7\u6DC6\u3002\n const rest = lines.slice(1).map((line) => ` ${line}`)\n return [first, ...rest].join('\\n')\n })\n return [header, '', ...items].join('\\n')\n}\n\n/** \u628A token \u5E8F\u5217\u6E32\u67D3\u6210 HTML(\u8F6C\u4E49\u5728 token \u7EA7\u505A,\u4FDD\u8BC1\u5B89\u5168)\u3002 */\nfunction tokensToHtml(tokens: Token[]): string {\n let out = ''\n for (const token of tokens) {\n const escaped = escapeHtml(token.text)\n out += token.cls === null ? escaped : `<span class=\"${token.cls}\">${escaped}</span>`\n }\n return out\n}\n\n/**\n * \u6E32\u67D3\u4E00\u4E2A\u5185\u5BB9\u683C:\u57FA\u7840\u8BED\u6CD5\u9AD8\u4EAE + \u53EF\u9009\u7684\u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u9AD8\u4EAE\u3002\n * @param text \u5185\u5BB9\u6587\u672C(\u53EF\u80FD\u4E3A\u7A7A)\u3002\n * @param side \u65E7/\u65B0\u4FA7(\u51B3\u5B9A\u4E2D\u6BB5\u80CC\u666F\u7528\u7EA2\u8FD8\u662F\u7EFF)\u3002\n * @param noEol \u672B\u5C3E\u65E0\u6362\u884C\u6807\u8BB0\u3002\n * @param diffRange \u884C\u5185\u5DEE\u5F02\u8303\u56F4(\u4EC5 change \u884C;null = \u4E0D\u505A\u5B57\u7B26\u7EA7\u9AD8\u4EAE)\u3002\n * @param lang \u8BED\u6CD5\u9AD8\u4EAE\u914D\u7F6E(\u65E0\u6CD5\u8BC6\u522B\u8BED\u8A00\u65F6 null)\u3002\n */\nfunction cellHtml(\n text: string | undefined,\n side: 'old' | 'new',\n noEol: boolean | undefined,\n diffRange: DiffMidRange | null,\n lang: LineLang | null,\n noNewlineTitle: string,\n): string {\n if (text === undefined) return `<div class=\"dsh-cr-cell dsh-cr-cell-${side}\"></div>`\n const mark = noEol === true ? `<span class=\"dsh-cr-nonl\" title=\"${escapeHtml(noNewlineTitle)}\">\u23CE</span>` : ''\n let body: string\n if (diffRange === null) {\n // \u65E0\u5B57\u7B26\u7EA7\u9AD8\u4EAE:\u6574\u884C\u4E00\u4E2A\u8BED\u6CD5\u6BB5\u3002\n body = lang === null ? escapeHtml(text) : tokensToHtml(tokenizeLine(text, lang))\n } else {\n // \u6309 code point \u5207\u524D/\u4E2D/\u540E\u4E09\u6BB5(\u7D22\u5F15\u6765\u81EA diffMidRange \u7684\n // Array.from \u8BA1\u7B97,\u5207\u7247\u540C\u6837\u6309 code point,\u4EE3\u7406\u5BF9\u4E0D\u4F1A\u65AD\u88C2)\u3002\n const chars = Array.from(text)\n const start = side === 'old' ? diffRange.oldStart : diffRange.newStart\n const end = side === 'old' ? diffRange.oldEnd : diffRange.newEnd\n const prefix = chars.slice(0, start).join('')\n const mid = chars.slice(start, end).join('')\n const suffix = chars.slice(end).join('')\n const seg = (segText: string): string =>\n lang === null ? escapeHtml(segText) : tokensToHtml(tokenizeLine(segText, lang))\n body = seg(prefix)\n + (mid !== '' ? `<span class=\"dihl-mid-${side}\">${seg(mid)}</span>` : '')\n + seg(suffix)\n }\n return `<div class=\"dsh-cr-cell dsh-cr-cell-${side}\">${body}${mark}</div>`\n}\n\n/** \u6E32\u67D3\u4E00\u884C(\u53CC\u5217 4 \u683C\u7ED3\u6784,\u76F4\u63A5\u4EA7 HTML \u5B57\u7B26\u4E32)\u3002 */\nfunction rowHtml(row: DiffRow, lang: LineLang | null, path: string, noNewlineTitle: string): string {\n if (row.kind === 'gap') {\n return `<div class=\"dsh-cr-gaprow\">${escapeHtml(row.gapText ?? '')}</div>`\n }\n // \u884C\u53F7\u683C\u5E26\u8BC4\u8BBA\u5B9A\u4F4D\u4FE1\u606F(\u6587\u4EF6\u8DEF\u5F84/\u884C\u53F7/\u4FA7\u522B),\u6807\u7B7E\u9875\u811A\u672C\u636E\u6B64\u6302\u884C\u5185\u8BC4\u8BBA\u3002\n const num = (no: number | undefined, side: string): string => {\n if (no === undefined) return `<div class=\"dsh-cr-num dsh-cr-num-${side}\"></div>`\n return `<div class=\"dsh-cr-num dsh-cr-num-${side}\" data-cr-path=\"${escapeHtml(path)}\" data-cr-line=\"${no}\" data-cr-side=\"${side}\">${no}</div>`\n }\n const line = `<div class=\"dsh-cr-row dsh-cr-${row.kind}\">`\n if (row.kind === 'ctx') {\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, null, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, null, lang, noNewlineTitle) + '</div>'\n }\n if (row.kind === 'del') {\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, null, lang, noNewlineTitle)\n + num(undefined, 'new') + cellHtml(undefined, 'new', undefined, null, lang, noNewlineTitle) + '</div>'\n }\n if (row.kind === 'add') {\n return line + num(undefined, 'old') + cellHtml(undefined, 'old', undefined, null, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, null, lang, noNewlineTitle) + '</div>'\n }\n // change \u884C:\u7B97\u884C\u5185\u5B57\u7B26\u7EA7\u5DEE\u5F02\u8303\u56F4,\u4E24\u4FA7\u4E2D\u6BB5\u5404\u81EA\u52A0\u6DF1\u80CC\u666F\u3002\n const range = diffMidRange(row.oldText ?? '', row.newText ?? '')\n return line + num(row.oldNo, 'old') + cellHtml(row.oldText, 'old', row.oldNoEol, range, lang, noNewlineTitle)\n + num(row.newNo, 'new') + cellHtml(row.newText, 'new', row.newNoEol, range, lang, noNewlineTitle) + '</div>'\n}\n\n/**\n * \u6784\u5EFA\u72EC\u7ACB\u6807\u7B7E\u9875\u7684\u5B8C\u6574 HTML(\u81EA\u5305\u542B:\u6837\u5F0F + \u4E3B\u9898\u53D8\u91CF + \u53CC\u5217\u5185\u5BB9)\u3002\n * \u5FC5\u987B\u7531\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u6309\u94AE\u7684\u70B9\u51FB\u5904\u7406\u5668\u5728\u7528\u6237\u624B\u52BF\u5185\u540C\u6B65\u8C03\u7528\n * (window.open \u624D\u4E0D\u4F1A\u88AB\u6D4F\u89C8\u5668\u62E6\u622A)\u3002\n * @param files \u89E3\u6790\u7ED3\u679C\u3002\n * @param theme \u4ECE\u5BBF\u4E3B\u9875\u9762\u6536\u96C6\u7684 DSW \u4E3B\u9898\u53D8\u91CF(name \u2192 value)\u3002\n * @param rawText \u539F\u59CB diff \u6587\u672C(\u590D\u5236/\u4FDD\u5E95\u67E5\u770B\u7528)\u3002\n */\nexport function buildStandaloneHtml(\n files: FileDiff[],\n theme: Record<string, string>,\n rawText: string,\n lang: Lang = 'zh',\n): string {\n const texts = CR_TEXTS[lang]\n const stats = countStats(files)\n const statsText = texts.stats(stats.added, stats.removed, stats.files)\n const rootVars = Object.entries(theme)\n .map(([name, value]) => `${name}: ${value};`)\n .join(' ')\n // \u539F\u59CB\u6587\u672C\u8981\u5185\u5D4C\u8FDB <script> \u7684 JSON \u5B57\u7B26\u4E32:JSON.stringify \u4E0D\u8F6C\u4E49 `<`,\n // diff \u5185\u5BB9\u91CC\u4E00\u65E6\u51FA\u73B0 `</script>` \u4F1A\u63D0\u524D\u7EC8\u6B62\u6807\u7B7E(\u7ECF\u5178\u89E3\u6790\u9677\u9631),\n // \u7EDF\u4E00\u628A `<` \u5199\u6210 \\u003c(JSON \u8BED\u4E49\u7B49\u4EF7,\u89E3\u6790\u5B89\u5168)\u3002\n const rawJson = JSON.stringify(rawText).replace(/</g, '\\\\u003c')\n const nav = files\n .map((file, index) => `<a class=\"navitem\" href=\"#file-${index}\">${escapeHtml(displayPath(file.newPath) || displayPath(file.oldPath))}</a>`)\n .join('')\n const sections = files.map((file, index) => {\n const oldPath = displayPath(file.oldPath)\n const newPath = displayPath(file.newPath)\n const path = oldPath !== '' && oldPath !== newPath ? `${oldPath} \u2192 ${newPath}` : (newPath === '' ? oldPath : newPath)\n const meta = file.meta.filter((m) => !m.startsWith('index '))\n // \u8BED\u8A00\u63A8\u65AD:\u65B0\u8DEF\u5F84\u4F18\u5148(\u65B0\u589E\u6587\u4EF6\u6CA1\u6709\u65E7\u8DEF\u5F84);/dev/null \u65F6\u56DE\u9000\u65E7\u8DEF\u5F84\u3002\n const lang = langFor(newPath) ?? langFor(oldPath)\n const commentPath = newPath !== '' && newPath !== '/dev/null' ? newPath : oldPath\n const rows = file.rows.length === 0\n ? `<div class=\"dsh-cr-gaprow\">${escapeHtml(file.meta.join(' \u00B7 ') || file.status)}</div>`\n : file.rows.map((row) => rowHtml(row, lang, commentPath, texts.noNewlineTitle)).join('')\n const head = `<header class=\"filehead\"><span class=\"badge\">${texts.status[file.status]}</span><span class=\"path\">${escapeHtml(path)}</span>`\n + (meta.length > 0 ? `<span class=\"meta\">${escapeHtml(meta.join(' \u00B7 '))}</span>` : '')\n + '</header>'\n return `<section class=\"file\" id=\"file-${index}\">${head}<div class=\"dsh-cr-grid\">${rows}</div></section>`\n }).join('')\n return `<!doctype html>\n<html lang=\"${lang === 'zh' ? 'zh-CN' : 'en'}\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Code Review \u00B7 ${statsText}</title>\n<style>\n:root { ${rootVars} }\n${STANDALONE_CSS}\n</style>\n</head>\n<body>\n<header class=\"topbar\">\n <span class=\"stats\">${statsText}</span>\n <span class=\"topbar-actions\">\n <button class=\"btn\" id=\"submit-comments\" type=\"button\">${texts.submitComments}</button>\n <button class=\"btn\" id=\"copy-raw\" type=\"button\">${texts.copyRaw}</button>\n </span>\n</header>\n<div class=\"submit-panel\" id=\"submit-panel\" hidden>\n <span class=\"submit-hint\">${texts.submitHint}</span>\n <button class=\"btn\" id=\"submit-lgtm\" type=\"button\">${texts.submitLgtm}</button>\n <button class=\"btn\" id=\"submit-plain\" type=\"button\">${texts.submitPlain}</button>\n</div>\n<nav class=\"sidenav\">${nav}</nav>\n<main>${sections}</main>\n<div class=\"rawwrap\">\n <details><summary class=\"rawsummary\">${texts.rawSummary}</summary>\n <pre class=\"rawpre\">${escapeHtml(rawText)}</pre>\n </details>\n</div>\n<script>\ndocument.getElementById('copy-raw').addEventListener('click', function (event) {\n var raw = ${rawJson}\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(raw).then(function () {\n var btn = event.currentTarget\n btn.textContent = T.copied\n setTimeout(function () { btn.textContent = T.copyRaw }, 1200)\n })\n }\n})\n</script>\n<script>\n/* \u884C\u5185\u8BC4\u8BBA:\u70B9\u51FB\u884C\u53F7\u683C\u63D0\u8BC4\u8BBA,\u6512\u9F50\u540E\u63D0\u4EA4\u56DE DSH \u5BF9\u8BDD\u6846(postMessage)\u3002 */\n(function () {\n var T = ${JSON.stringify(stringTexts(texts))}\n var comments = []\n var draft = null\n var submitBtn = document.getElementById('submit-comments')\n var panel = document.getElementById('submit-panel')\n\n function esc(text) {\n return String(text)\n .replace(/&/g, '&amp;').replace(/</g, '&lt;')\n .replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n }\n function syncSubmit() {\n if (!submitBtn) return\n submitBtn.textContent = comments.length === 0 ? T.submitComments : T.submitComments + ' (' + comments.length + ')'\n }\n function sameLine(a, b) {\n return a.path === b.path && a.lineNo === b.lineNo && a.side === b.side\n }\n function lineOf(num) {\n return {\n path: num.getAttribute('data-cr-path'),\n lineNo: Number(num.getAttribute('data-cr-line')),\n side: num.getAttribute('data-cr-side'),\n }\n }\n function commentsOn(line) {\n return comments.filter(function (c) { return sameLine(c, line) })\n }\n function closeDraft() {\n if (draft && draft.parentNode) draft.parentNode.removeChild(draft)\n draft = null\n }\n function findNum(line) {\n var nums = document.querySelectorAll('.dsh-cr-num[data-cr-line]')\n for (var i = 0; i < nums.length; i += 1) {\n if (sameLine(lineOf(nums[i]), line)) return nums[i]\n }\n return null\n }\n function updateBadge(num) {\n var badge = num.querySelector('.cc')\n if (badge) badge.parentNode.removeChild(badge)\n var n = commentsOn(lineOf(num)).length\n if (n > 0) {\n var span = document.createElement('span')\n span.className = 'cc'\n span.textContent = String(n)\n num.appendChild(span)\n }\n }\n function openDraft(num) {\n closeDraft()\n var line = lineOf(num)\n var existing = commentsOn(line)\n draft = document.createElement('div')\n draft.className = 'comment-draft'\n var html = ''\n if (existing.length > 0) {\n html += '<div class=\"comment-list\">'\n existing.forEach(function (c) { html += '<div class=\"comment-item\">' + esc(c.text) + '</div>' })\n html += '</div>'\n }\n html += '<textarea class=\"comment-input\" placeholder=\"' + T.commentPlaceholder + '\"></textarea>'\n html += '<div class=\"comment-actions\">'\n + '<button class=\"btn\" type=\"button\" data-act=\"save\">' + T.addComment + '</button>'\n + '<button class=\"btn\" type=\"button\" data-act=\"cancel\">' + T.cancel + '</button>'\n + '</div>'\n draft.innerHTML = html\n num.parentElement.insertAdjacentElement('afterend', draft)\n var ta = draft.querySelector('.comment-input')\n ta.focus()\n ta.addEventListener('keydown', function (event) {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) saveDraft(line)\n })\n draft.querySelector('[data-act=\"save\"]').addEventListener('click', function () { saveDraft(line) })\n draft.querySelector('[data-act=\"cancel\"]').addEventListener('click', closeDraft)\n }\n function saveDraft(line) {\n if (!draft) return\n var ta = draft.querySelector('.comment-input')\n var text = ta.value.trim()\n closeDraft()\n if (text === '') return\n comments.push({ path: line.path, lineNo: line.lineNo, side: line.side, text: text })\n var num = findNum(line)\n if (num) updateBadge(num)\n syncSubmit()\n }\n function send(kind) {\n if (comments.length === 0) return\n var target = window.opener\n if (!target) {\n alert(T.openerClosed)\n return\n }\n target.postMessage({\n type: 'dsh-code-review-comments',\n kind: kind,\n comments: comments.map(function (c) {\n return { path: c.path, lineNo: c.lineNo, side: c.side, text: c.text }\n }),\n }, '*')\n panel.hidden = true\n }\n document.addEventListener('click', function (event) {\n var target = event.target\n var num = target && target.closest ? target.closest('.dsh-cr-num[data-cr-line]') : null\n if (num) {\n openDraft(num)\n return\n }\n if (draft && !draft.contains(target)) closeDraft()\n if (panel && target && target.closest && !panel.contains(target) && !target.closest('#submit-comments')) {\n panel.hidden = true\n }\n })\n submitBtn.addEventListener('click', function () {\n if (comments.length === 0) return\n panel.hidden = !panel.hidden\n })\n document.getElementById('submit-lgtm').addEventListener('click', function () { send('lgtm') })\n document.getElementById('submit-plain').addEventListener('click', function () { send('comment') })\n window.addEventListener('message', function (event) {\n if (event.source !== window.opener) return\n var data = event.data\n if (!data || data.type !== 'dsh-code-review-ack') return\n if (data.ok === false) {\n alert(T.noSession)\n } else {\n submitBtn.textContent = T.sent\n setTimeout(syncSubmit, 3000)\n }\n })\n syncSubmit()\n})()\n</script>\n</body>\n</html>\n`\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["text", "lang", "win"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yangzhe1991/dsh-code-review",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "DSH Web 插件:git diff 一键打开独立双列 Code Review 页,支持语法高亮、行内评论与中英双语 | DSH web plugin: one-click standalone two-column code review page for git diff, with syntax highlighting, inline comments & bilingual UI",
5
5
  "publishConfig": {
6
6
  "access": "public"