@yangzhe1991/dsh-code-review 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -11,6 +11,11 @@
11
11
 
12
12
  The conversation itself stays untouched: when a git diff shows up, a small **"open in new tab"** button appears on the block (a markdown diff code block, or the bash tool row that produced the diff — even while collapsed). One click opens the review page.
13
13
 
14
+ ## Compatibility
15
+
16
+ - **dsh ≥ 0.1.2-alpha.4** — fully supported since **0.1.5**. The web UI restructure (chat snapshot moved from `useSession((s) => s.chat)` to the session-standard `useChat` hook; markdown code-block DOM wrapped in a `bannerWrap` layer) is handled on both detection paths: markdown diff blocks and collapsed bash tool rows.
17
+ - **dsh 0.1.0-rc.x** — still supported via the legacy snapshot path.
18
+
14
19
  ## Features
15
20
 
16
21
  - ⬅️➡️ **Two-column side-by-side view** — the old file on the left, the new file on the right, each with its own line numbers, exactly like a GitHub split review.
package/lib/client.js CHANGED
@@ -741,6 +741,7 @@ var CR_TEXTS = {
741
741
  collapseTitle: "\u6536\u8D77\u539F\u59CB diff \u6587\u672C",
742
742
  reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review \u5171 ${count} \u6761\u8BC4\u8BBA:`,
743
743
  reportCommentHeader: (count) => `Code Review \u610F\u89C1,\u5171 ${count} \u6761:`,
744
+ reportLgtmBare: "Looks good to me \u2713",
744
745
  stats: (added, removed, files) => `+${added} \u2212${removed} \xB7 ${files} \u4E2A\u6587\u4EF6`,
745
746
  submitComments: "\u63D0\u4EA4\u8BC4\u8BBA",
746
747
  copyRaw: "\u590D\u5236\u539F\u59CB diff",
@@ -767,6 +768,7 @@ var CR_TEXTS = {
767
768
  collapseTitle: "Hide raw diff text",
768
769
  reportLgtmHeader: (count) => `Looks good to me \u2713 \u2014 Code Review with ${count} comment${count === 1 ? "" : "s"}:`,
769
770
  reportCommentHeader: (count) => `Code Review comments (${count}):`,
771
+ reportLgtmBare: "Looks good to me \u2713",
770
772
  stats: (added, removed, files) => `+${added} \u2212${removed} \xB7 ${files} file${files === 1 ? "" : "s"}`,
771
773
  submitComments: "Submit comments",
772
774
  copyRaw: "Copy raw diff",
@@ -812,6 +814,7 @@ function stringTexts(texts) {
812
814
  }
813
815
  function buildCommentReport(comments, kind, lang = "zh") {
814
816
  const texts = CR_TEXTS[lang];
817
+ if (kind === "lgtm" && comments.length === 0) return texts.reportLgtmBare;
815
818
  const header = kind === "lgtm" ? texts.reportLgtmHeader(comments.length) : texts.reportCommentHeader(comments.length);
816
819
  const items = comments.map((comment, index) => {
817
820
  const lines = comment.text.split("\n");
@@ -1021,7 +1024,8 @@ document.getElementById('copy-raw').addEventListener('click', function (event) {
1021
1024
  syncSubmit()
1022
1025
  }
1023
1026
  function send(kind) {
1024
- if (comments.length === 0) return
1027
+ // \u7EAF\u8BC4\u8BBA\u8981\u6C42\u81F3\u5C11\u4E00\u6761;\u7EAF LGTM(0 \u6761\u8BC4\u8BBA)\u5141\u8BB8\u76F4\u63A5\u63D0\u4EA4\u6279\u51C6\u3002
1028
+ if (comments.length === 0 && kind !== 'lgtm') return
1025
1029
  var target = window.opener
1026
1030
  if (!target) {
1027
1031
  alert(T.openerClosed)
@@ -1049,7 +1053,7 @@ document.getElementById('copy-raw').addEventListener('click', function (event) {
1049
1053
  }
1050
1054
  })
1051
1055
  submitBtn.addEventListener('click', function () {
1052
- if (comments.length === 0) return
1056
+ // 0 \u6761\u8BC4\u8BBA\u65F6\u4E5F\u5141\u8BB8\u6253\u5F00\u9762\u677F(\u7EAF LGTM \u573A\u666F)\u3002
1053
1057
  panel.hidden = !panel.hidden
1054
1058
  })
1055
1059
  document.getElementById('submit-lgtm').addEventListener('click', function () { send('lgtm') })
@@ -1080,8 +1084,25 @@ var RETRY_MS = 3e3;
1080
1084
  var SAFETY_SCAN_MS = 1e4;
1081
1085
  var styleInjected = false;
1082
1086
  var BUTTON_CSS = `
1087
+ /* \u4E3B\u6309\u94AE:\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u2014\u2014 \u54C1\u724C\u84DD\u5B9E\u5FC3 + \u767D\u5B57,\u6DF1\u6D45\u4E3B\u9898\u90FD\u9192\u76EE */
1083
1088
  .dsh-cr-openbtn {
1084
1089
  margin-left: auto;
1090
+ flex: none;
1091
+ font-size: 12px;
1092
+ line-height: 18px;
1093
+ color: #fff;
1094
+ background: var(--dsw-alias-state-business-primary, #3b6fe0);
1095
+ border: none;
1096
+ border-radius: 8px;
1097
+ padding: 2px 10px;
1098
+ cursor: pointer;
1099
+ white-space: nowrap;
1100
+ }
1101
+ .dsh-cr-openbtn:hover {
1102
+ filter: brightness(1.1);
1103
+ }
1104
+ /* \u6B21\u6309\u94AE:\u300C\u5C55\u5F00/\u6536\u8D77\u300D\u2014\u2014 \u4FDD\u6301\u4F4E\u8C03,\u4E0D\u4E0E\u4E3B\u6309\u94AE\u62A2\u89C6\u7EBF */
1105
+ .dsh-cr-foldbtn {
1085
1106
  flex: none;
1086
1107
  font-size: 12px;
1087
1108
  line-height: 18px;
@@ -1093,10 +1114,24 @@ var BUTTON_CSS = `
1093
1114
  cursor: pointer;
1094
1115
  white-space: nowrap;
1095
1116
  }
1096
- .dsh-cr-openbtn:hover {
1117
+ .dsh-cr-foldbtn:hover {
1097
1118
  color: var(--dsw-alias-label-primary);
1098
1119
  border-color: var(--dsw-alias-border-l3);
1099
1120
  }
1121
+ /* \u6309\u94AE\u884C\u5BB9\u5668:\u4E24\u4E2A\u6309\u94AE\u88C5\u8FDB\u4E00\u4E2A\u53F3\u5BF9\u9F50\u7684\u72EC\u7ACB\u884C\u3002
1122
+ * \u65B0\u524D\u7AEF\u4EE3\u7801\u5757\u7ED3\u6784\u662F bannerWrap(\u5757\u7EA7 sticky)\u5305\u7740 banner,\u76F4\u63A5 append
1123
+ * \u4F1A\u88AB\u6324\u5728 banner \u4E4B\u4E0B\u4E14\u9760\u5DE6;\u5B98\u65B9\u590D\u5236\u6309\u94AE\u6240\u5728 .action \u4E5F\u662F flex \u5E76\u6392,
1124
+ * \u63D2\u4EF6\u6309\u94AE\u585E\u8FDB\u53BB\u4F1A\u8DDF\u590D\u5236\u6309\u94AE\u4E89\u4F4D\u3002\u505A\u6210\u72EC\u7ACB\u884C\u540E:\u65E7 banner(flex)\u91CC\u5B83\u662F
1125
+ * \u9760\u53F3\u7684 flex \u9879,\u65B0 bannerWrap(\u5757\u7EA7)\u91CC\u5B83\u6574\u884C\u53F3\u5BF9\u9F50,\u907F\u514D\u4E89\u4F4D\u3002 */
1126
+ .dsh-cr-row {
1127
+ display: flex;
1128
+ justify-content: flex-end;
1129
+ align-items: center;
1130
+ gap: 8px;
1131
+ flex: none;
1132
+ margin-left: auto;
1133
+ padding: 4px 12px 7px;
1134
+ }
1100
1135
  `;
1101
1136
  function ensureStyle() {
1102
1137
  if (styleInjected || typeof document === "undefined") return;
@@ -1175,8 +1210,8 @@ function handleReviewMessage(event) {
1175
1210
  if (data.type !== "dsh-code-review-comments") return;
1176
1211
  if (!reviewWindows.has(event.source)) return;
1177
1212
  const comments = sanitizeComments(data.comments);
1178
- if (comments.length === 0) return;
1179
1213
  const kind = data.kind === "lgtm" ? "lgtm" : "comment";
1214
+ if (comments.length === 0 && kind !== "lgtm") return;
1180
1215
  const report = buildCommentReport(comments, kind, detectLang());
1181
1216
  const source = event.source;
1182
1217
  if (composerWriter !== null) {
@@ -1188,7 +1223,15 @@ function handleReviewMessage(event) {
1188
1223
  }
1189
1224
  function ensureButtons(host, getText, fold) {
1190
1225
  const texts = CR_TEXTS[detectLang()];
1191
- if (host.querySelector("[data-dsh-cr-btn]") === null) {
1226
+ let row = host.querySelector("[data-dsh-cr-row]");
1227
+ if (row === null) {
1228
+ const created = document.createElement("div");
1229
+ created.className = "dsh-cr-row";
1230
+ created.dataset.dshCrRow = "";
1231
+ host.appendChild(created);
1232
+ row = created;
1233
+ }
1234
+ if (row.querySelector("[data-dsh-cr-btn]") === null) {
1192
1235
  const btn = document.createElement("button");
1193
1236
  btn.type = "button";
1194
1237
  btn.className = "dsh-cr-openbtn";
@@ -1201,12 +1244,12 @@ function ensureButtons(host, getText, fold) {
1201
1244
  const text = getText();
1202
1245
  if (text !== null) openReviewTab(text);
1203
1246
  });
1204
- host.appendChild(btn);
1247
+ row.appendChild(btn);
1205
1248
  }
1206
- if (fold !== null && host.querySelector("[data-dsh-cr-fold]") === null) {
1249
+ if (fold !== null && row.querySelector("[data-dsh-cr-fold]") === null) {
1207
1250
  const btn = document.createElement("button");
1208
1251
  btn.type = "button";
1209
- btn.className = "dsh-cr-openbtn";
1252
+ btn.className = "dsh-cr-foldbtn";
1210
1253
  btn.dataset.dshCrFold = "";
1211
1254
  btn.textContent = fold.folded ? texts.expand : texts.collapse;
1212
1255
  btn.title = fold.folded ? texts.expandTitle : texts.collapseTitle;
@@ -1215,13 +1258,17 @@ function ensureButtons(host, getText, fold) {
1215
1258
  event.preventDefault();
1216
1259
  fold.onToggle();
1217
1260
  });
1218
- host.appendChild(btn);
1261
+ row.appendChild(btn);
1219
1262
  }
1220
1263
  }
1221
1264
  function removeButtons(host) {
1222
1265
  for (const btn of host.querySelectorAll("[data-dsh-cr-btn], [data-dsh-cr-fold]")) {
1223
1266
  btn.remove();
1224
1267
  }
1268
+ const row = host.querySelector("[data-dsh-cr-row]");
1269
+ if (row instanceof HTMLElement && row.querySelector("[data-dsh-cr-btn], [data-dsh-cr-fold]") === null) {
1270
+ row.remove();
1271
+ }
1225
1272
  }
1226
1273
  function findCodeBlockBody(el) {
1227
1274
  const first = el.firstElementChild;
@@ -1242,7 +1289,14 @@ function probeCodeBlock(el) {
1242
1289
  const banner = el.firstElementChild;
1243
1290
  const body = findCodeBlockBody(el);
1244
1291
  if (!(banner instanceof HTMLElement) || body === null) return null;
1245
- const lang = (banner.firstElementChild?.textContent ?? "").trim().toLowerCase();
1292
+ let lang = "";
1293
+ const innermost = banner.firstElementChild?.firstElementChild;
1294
+ if (innermost instanceof HTMLElement) {
1295
+ lang = innermost.textContent ?? "";
1296
+ } else {
1297
+ lang = banner.firstElementChild?.textContent ?? "";
1298
+ }
1299
+ lang = lang.trim().toLowerCase();
1246
1300
  const text = body.textContent ?? "";
1247
1301
  const candidate = lang === "diff" || lang === "patch" || isDiffText(text);
1248
1302
  return { text, candidate, running: false };
@@ -1499,13 +1553,13 @@ function CodeReviewEnhancer() {
1499
1553
  }, []);
1500
1554
  return null;
1501
1555
  }
1502
- function DiffToolButtonWatcher({ useSession, sessionId, inputActions }) {
1503
- const chat = useSession((state) => state.chat);
1556
+ function DiffToolButtonWatcher({ useSession, useChat, sessionId, inputActions }) {
1557
+ const chat = useChat !== void 0 ? useChat((state) => state) : useSession((state) => state.chat);
1504
1558
  (0, import_react.useEffect)(() => {
1505
1559
  if (chat !== void 0) syncDiffToolTexts(String(sessionId), chat);
1506
1560
  }, [chat, sessionId]);
1507
1561
  (0, import_react.useEffect)(() => {
1508
- const writer = (text) => inputActions.setDraft(text);
1562
+ const writer = (text) => inputActions?.setDraft(text);
1509
1563
  composerWriter = writer;
1510
1564
  return () => {
1511
1565
  if (composerWriter === writer) composerWriter = null;
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 /* \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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",
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 *\n * \u524D\u7AEF\u7ED3\u6784\u7248\u672C\u5907\u5FD8(0.1.2-alpha.4 \u8D77):\n * - markdown \u4EE3\u7801\u5757\u7ED3\u6784\u53D8\u4E3A [bannerWrap[banner[infostring, action]], body],\n * banner \u5916\u591A\u5305\u4E00\u5C42 sticky \u7684 bannerWrap;\u8BED\u8A00\u6807\u7B7E\u5728 infostring \u91CC;\n * - chat \u5FEB\u7167\u4ECE useSession((s) => s.chat) \u8FC1\u79FB\u5230\u4F1A\u8BDD\u6807\u51C6 hook useChat\n * (dsh-client-ui-chat \u6CE8\u518C),DiffToolButtonWatcher \u5DF2\u9002\u914D\u5E76\u4FDD\u7559\u65E7\u8DEF\u5F84;\n * - inputActions \u4ECD\u662F\u4F1A\u8BDD\u6807\u51C6 props(conversation \u63D2\u4EF6\u63D0\u4F9B),\u8BC4\u8BBA\u56DE\u4F20\n * \u8DEF\u5F84\u4E0D\u53D7\u5F71\u54CD\u3002\n */\nimport { useEffect } from 'react'\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\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/* \u4E3B\u6309\u94AE:\u300C\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u300D\u2014\u2014 \u54C1\u724C\u84DD\u5B9E\u5FC3 + \u767D\u5B57,\u6DF1\u6D45\u4E3B\u9898\u90FD\u9192\u76EE */\n.dsh-cr-openbtn {\n margin-left: auto;\n flex: none;\n font-size: 12px;\n line-height: 18px;\n color: #fff;\n background: var(--dsw-alias-state-business-primary, #3b6fe0);\n border: none;\n border-radius: 8px;\n padding: 2px 10px;\n cursor: pointer;\n white-space: nowrap;\n}\n.dsh-cr-openbtn:hover {\n filter: brightness(1.1);\n}\n/* \u6B21\u6309\u94AE:\u300C\u5C55\u5F00/\u6536\u8D77\u300D\u2014\u2014 \u4FDD\u6301\u4F4E\u8C03,\u4E0D\u4E0E\u4E3B\u6309\u94AE\u62A2\u89C6\u7EBF */\n.dsh-cr-foldbtn {\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-foldbtn:hover {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-border-l3);\n}\n/* \u6309\u94AE\u884C\u5BB9\u5668:\u4E24\u4E2A\u6309\u94AE\u88C5\u8FDB\u4E00\u4E2A\u53F3\u5BF9\u9F50\u7684\u72EC\u7ACB\u884C\u3002\n * \u65B0\u524D\u7AEF\u4EE3\u7801\u5757\u7ED3\u6784\u662F bannerWrap(\u5757\u7EA7 sticky)\u5305\u7740 banner,\u76F4\u63A5 append\n * \u4F1A\u88AB\u6324\u5728 banner \u4E4B\u4E0B\u4E14\u9760\u5DE6;\u5B98\u65B9\u590D\u5236\u6309\u94AE\u6240\u5728 .action \u4E5F\u662F flex \u5E76\u6392,\n * \u63D2\u4EF6\u6309\u94AE\u585E\u8FDB\u53BB\u4F1A\u8DDF\u590D\u5236\u6309\u94AE\u4E89\u4F4D\u3002\u505A\u6210\u72EC\u7ACB\u884C\u540E:\u65E7 banner(flex)\u91CC\u5B83\u662F\n * \u9760\u53F3\u7684 flex \u9879,\u65B0 bannerWrap(\u5757\u7EA7)\u91CC\u5B83\u6574\u884C\u53F3\u5BF9\u9F50,\u907F\u514D\u4E89\u4F4D\u3002 */\n.dsh-cr-row {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n flex: none;\n margin-left: auto;\n padding: 4px 12px 7px;\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 const kind = data.kind === 'lgtm' ? 'lgtm' : 'comment'\n // \u7EAF LGTM \u5141\u8BB8 0 \u6761\u8BC4\u8BBA(\u62A5\u544A\u53EA\u6709\u6279\u51C6\u58F0\u660E\u4E00\u884C);\u7EAF\u8BC4\u8BBA\u4ECD\u8981\u6C42\u81F3\u5C11\u4E00\u6761\u3002\n if (comments.length === 0 && kind !== 'lgtm') return\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 // \u6309\u94AE\u7EDF\u4E00\u88C5\u8FDB\u63D2\u4EF6\u81EA\u5DF1\u7684\u884C\u5BB9\u5668(\u89C1 BUTTON_CSS \u91CC .dsh-cr-row \u7684\u6CE8\u91CA),\n // \u65B0\u65E7\u524D\u7AEF\u7ED3\u6784\u4E0B\u90FD\u9760\u53F3\u3001\u4E0D\u5E72\u6270\u5B98\u65B9\u5E03\u5C40\u3002\n let row: HTMLElement | null = host.querySelector('[data-dsh-cr-row]')\n if (row === null) {\n const created = document.createElement('div')\n created.className = 'dsh-cr-row'\n created.dataset.dshCrRow = ''\n host.appendChild(created)\n row = created\n }\n if (row.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 row.appendChild(btn)\n }\n if (fold !== null && row.querySelector('[data-dsh-cr-fold]') === null) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'dsh-cr-foldbtn'\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 row.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 // \u987A\u5E26\u79FB\u9664\u7A7A\u7684\u6309\u94AE\u884C\u5BB9\u5668,\u907F\u514D\u6B8B\u7559 padding \u767D\u8FB9\u3002\n const row = host.querySelector('[data-dsh-cr-row]')\n if (row instanceof HTMLElement && row.querySelector('[data-dsh-cr-btn], [data-dsh-cr-fold]') === null) {\n row.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 // \u8BED\u8A00\u6807\u7B7E\u8BFB\u53D6\u9002\u914D\u524D\u7AEF\u7ED3\u6784\u53D8\u5316:\n // - \u65B0\u524D\u7AEF(0.1.2-alpha+):bannerWrap > banner > infostring,infostring\n // \u662F\u7EAF\u8BED\u8A00\u6587\u672C,\u53D6\u5230\u6700\u5185\u5C42\u5143\u7D20;\n // - \u65E7\u524D\u7AEF(0.1.0-rc.x):banner \u76F4\u63A5\u662F\u7B2C\u4E00\u5B50\u8282\u70B9,\u8BED\u8A00\u7684\u6587\u672C\u8282\u70B9\u5728\n // banner \u7684\u9996\u4E2A\u5B50\u5143\u7D20\u91CC(\u4E5F\u8BB8\u662F span),\u6700\u5185\u5C42\u53D6\u4E0D\u5230\u65F6\u9000\u56DE\u6D45\u5C42\u8BFB\u53D6\u3002\n // \u6CE8\u610F\u4E0D\u80FD\u76F4\u63A5\u53D6 banner.textContent:\u65B0\u7ED3\u6784\u91CC\u5B83\u6DF7\u7740\u590D\u5236\u6309\u94AE\u6587\u6848(diffCopy)\u3002\n let lang = ''\n const innermost = banner.firstElementChild?.firstElementChild\n if (innermost instanceof HTMLElement) {\n lang = innermost.textContent ?? ''\n } else {\n lang = banner.firstElementChild?.textContent ?? ''\n }\n lang = lang.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 * \u4F1A\u8BDD\u6807\u51C6 props \u7684\u6700\u5C0F\u5F62\u72B6(conversation.session.header.actions \u5185\u5BB9\u6761\u76EE\n * \u8FD0\u884C\u65F6\u5B9E\u9645\u6536\u5230\u7684\u5B57\u6BB5;\u65B0\u65E7\u524D\u7AEF\u90FD\u5305\u542B,\u53EA\u58F0\u660E\u6240\u9700\u90E8\u5206)\u3002\n *\n * \u5173\u952E\u5DEE\u5F02(0.1.2-alpha.4 \u524D\u7AEF\u91CD\u6784):\n * - \u65E7\u524D\u7AEF(0.1.0-rc.x):chat \u5FEB\u7167\u6302\u5728\u4F1A\u8BDD\u5FEB\u7167\u7684 `chat` \u5B57\u6BB5,\n * \u7528 useSession((s) => s.chat) \u8BFB\u53D6;\n * - \u65B0\u524D\u7AEF:\u4F1A\u8BDD\u5FEB\u7167\u4E0D\u518D\u6709 chat \u5B57\u6BB5,\u804A\u5929\u5FEB\u7167\u6539\u7531 dsh-client-ui-chat\n * \u901A\u8FC7 uiSession.provide({ hooks: [\"chat\"] }) \u6CE8\u518C\u6210\u4F1A\u8BDD\u6807\u51C6 hook\n * useChat(\u5F62\u72B6\u4ECD\u662F { order, nodes, locations, timeline, ... },\u4E0E\n * ChatLike \u517C\u5BB9)\u3002useChat \u7F3A\u5931\u65F6(\u5982 chat \u63D2\u4EF6\u4E0D\u5728\u7EC4\u5408\u5185)\u9000\u56DE\u65E7\u8DEF\u5F84\u3002\n */\ninterface HeaderActionsProps {\n sessionId: string\n useSession<T>(selector: (snapshot: { chat?: ChatLike }) => T): T\n useChat?: <T>(selector: (snapshot: ChatLike) => T) => T\n inputActions?: { setDraft(text: string): void }\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, useChat, sessionId, inputActions }: HeaderActionsProps) {\n // chat \u5FEB\u7167\u8BFB\u53D6:\u65B0\u524D\u7AEF\u8D70\u4F1A\u8BDD\u6807\u51C6 hook useChat;\u65E7\u524D\u7AEF\u9000\u56DE\n // useSession((s) => s.chat)\u3002\u9009\u54EA\u6761\u5206\u652F\u7531\u8FD0\u884C\u65F6\u73AF\u5883\u51B3\u5B9A,\u540C\u4E00\u73AF\u5883\u5185\n // \u6BCF\u6B21\u6E32\u67D3\u6052\u5B9A\u8D70\u540C\u4E00\u5206\u652F,\u4E0D\u8FDD\u53CD React hook \u987A\u5E8F\u89C4\u5219\u3002\n const chat = useChat !== undefined\n ? useChat((state) => state)\n : 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 /** \u7EAF LGTM(0 \u6761\u8BC4\u8BBA)\u65F6\u63D0\u4EA4\u7684\u62A5\u544A\u6587\u672C,\u4E0D\u5E26\u8BA1\u6570\u540E\u7F00\u3002 */\n reportLgtmBare: 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 reportLgtmBare: 'Looks good to me \u2713',\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 reportLgtmBare: 'Looks good to me \u2713',\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 // \u7EAF LGTM(0 \u6761\u8BC4\u8BBA)\u53EA\u8F93\u51FA\u6279\u51C6\u58F0\u660E\u4E00\u884C,\u4E0D\u5E26\u7A7A\u8BA1\u6570\u5934\u3002\n if (kind === 'lgtm' && comments.length === 0) return texts.reportLgtmBare\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 // \u7EAF\u8BC4\u8BBA\u8981\u6C42\u81F3\u5C11\u4E00\u6761;\u7EAF LGTM(0 \u6761\u8BC4\u8BBA)\u5141\u8BB8\u76F4\u63A5\u63D0\u4EA4\u6279\u51C6\u3002\n if (comments.length === 0 && kind !== 'lgtm') 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 // 0 \u6761\u8BC4\u8BBA\u65F6\u4E5F\u5141\u8BB8\u6253\u5F00\u9762\u677F(\u7EAF LGTM \u573A\u666F)\u3002\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": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BA,mBAA0B;;;AC6BnB,SAAS,YAAY,MAAsB;AAChD,MAAI,SAAS,YAAa,QAAO;AACjC,SAAO,KAAK,QAAQ,WAAW,EAAE;AACnC;AAGO,SAAS,WAAW,MAAuB;AAChD,QAAM,QAAQ,KAAK,MAAM,IAAI;AAG7B,MAAI,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,aAAa,CAAC,EAAG,QAAO;AACjE,QAAM,UAAU,MAAM,KAAK,CAAC,SAAS,WAAW,KAAK,IAAI,CAAC;AAC1D,QAAM,cAAc,MAAM,KAAK,CAAC,SAAS,mBAAmB,KAAK,IAAI,CAAC;AACtE,SAAO,WAAW;AACpB;AAGO,SAAS,aAAa,MAA0B;AACrD,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,QAAM,QAAoB,CAAC;AAE3B,MAAI,SAAS;AAEb,MAAI,QAAQ;AACZ,MAAI,QAAQ;AAKZ,MAAI,cAAmE,CAAC;AAExE,MAAI,WAAyC;AAE7C,MAAI,aAA6B;AAGjC,WAAS,UAA2B;AAClC,WAAO,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI;AAAA,EACtD;AAGA,WAAS,eAAqB;AAC5B,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,QAAQ,YAAY,WAAW,EAAG;AAC/C,eAAW,OAAO,aAAa;AAC7B,WAAK,KAAK,KAAK;AAAA,QACb,MAAM;AAAA,QACN,OAAO,IAAI;AAAA,QACX,SAAS,IAAI;AAAA,QACb,UAAU,IAAI;AAAA,MAChB,CAAC;AACD,mBAAa;AAAA,IACf;AACA,kBAAc,CAAC;AAAA,EACjB;AAGA,WAAS,UAAU,SAAiB,SAAuB;AACzD,iBAAa;AACb,aAAS;AACT,eAAW;AACX,iBAAa;AACb,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,CAAC;AAAA,MACP,MAAM,CAAC;AAAA,IACT,CAAC;AAAA,EACH;AAGA,WAAS,SAAS,MAAuB;AACvC,UAAM,QAAQ,mDAAmD,KAAK,IAAI;AAC1E,QAAI,UAAU,KAAM,QAAO;AAC3B,iBAAa;AACb,YAAQ,OAAO,MAAM,CAAC,CAAC;AACvB,YAAQ,OAAO,MAAM,CAAC,CAAC;AACvB,aAAS;AACT,eAAW;AACX,iBAAa;AAEb,YAAQ,GAAG,KAAK,KAAK,EAAE,MAAM,OAAO,SAAS,KAAK,CAAC;AACnD,WAAO;AAAA,EACT;AAGA,WAAS,YAAYA,OAAoB;AACvC,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,KAAM;AACnB,UAAM,MAAe,EAAE,MAAM,OAAO,OAAO,OAAO,SAASA,OAAM,SAASA,MAAK;AAC/E,SAAK,KAAK,KAAK,GAAG;AAClB,aAAS;AACT,aAAS;AACT,eAAW;AACX,iBAAa;AAAA,EACf;AAGA,WAAS,WAAW,KAAmD,SAAuB;AAC5F,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,KAAM;AACnB,UAAM,MAAe;AAAA,MACnB,MAAM;AAAA,MACN,OAAO,IAAI;AAAA,MACX;AAAA,MACA,SAAS,IAAI;AAAA,MACb,SAAS;AAAA,MACT,UAAU,IAAI;AAAA,IAChB;AACA,SAAK,KAAK,KAAK,GAAG;AAClB,aAAS;AACT,eAAW;AACX,iBAAa;AAAA,EACf;AAGA,WAAS,QAAQA,OAAoB;AACnC,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,KAAM;AACnB,UAAM,MAAe,EAAE,MAAM,OAAO,OAAO,SAASA,MAAK;AACzD,SAAK,KAAK,KAAK,GAAG;AAClB,aAAS;AACT,eAAW;AACX,iBAAa;AAAA,EACf;AAGA,WAAS,YAAkB;AACzB,QAAI,aAAa,OAAO;AAEtB,YAAM,OAAO,YAAY,YAAY,SAAS,CAAC;AAC/C,UAAI,SAAS,QAAW;AACtB,aAAK,QAAQ;AAAA,MACf,OAAO;AACL,cAAM,OAAO,QAAQ;AACrB,YAAI,SAAS,QAAQ,KAAK,KAAK,SAAS,GAAG;AAEzC,gBAAM,MAAM,KAAK,KAAK,KAAK,KAAK,SAAS,CAAC;AAC1C,cAAI,IAAI,SAAS,MAAO,KAAI,WAAW;AAAA,QACzC;AAAA,MACF;AAAA,IACF,WAAW,aAAa,OAAO;AAE7B,UAAI,eAAe,KAAM,YAAW,WAAW;AAAA,IACjD,WAAW,aAAa,OAAO;AAC7B,YAAM,OAAO,QAAQ;AACrB,UAAI,SAAS,QAAQ,KAAK,KAAK,SAAS,GAAG;AACzC,cAAM,MAAM,KAAK,KAAK,KAAK,KAAK,SAAS,CAAC;AAC1C,YAAI,IAAI,SAAS,OAAO;AACtB,cAAI,WAAW;AACf,cAAI,WAAW;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO;AAGxB,QAAI,SAAS,GAAI;AACjB,QAAI,KAAK,WAAW,aAAa,GAAG;AAElC,YAAM,QAAQ,2CAA2C,KAAK,IAAI;AAClE,gBAAU,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE;AAC5C;AAAA,IACF;AACA,QAAI,KAAK,WAAW,KAAK,GAAG;AAC1B,eAAS,IAAI;AACb;AAAA,IACF;AACA,UAAM,MAAM,QAAQ;AACpB,QAAI,QAAQ,MAAM;AAGhB,UAAI,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,EAAG,WAAU,KAAK,MAAM,CAAC,GAAG,EAAE;AAC3E;AAAA,IACF;AACA,QAAI,CAAC,QAAQ;AAEX,UAAI,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG;AAC9C,YAAI,UAAU,KAAK,MAAM,CAAC;AAAA,MAC5B,WAAW,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG;AACrD,YAAI,UAAU,KAAK,MAAM,CAAC;AAAA,MAC5B,WAAW,KAAK,WAAW,gBAAgB,GAAG;AAC5C,YAAI,SAAS;AACb,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,WAAW,oBAAoB,GAAG;AAChD,YAAI,SAAS;AACb,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,WAAW,cAAc,KAAK,KAAK,WAAW,YAAY,GAAG;AAC3E,YAAI,SAAS;AACb,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB,WACE,KAAK,WAAW,WAAW,KAC3B,KAAK,WAAW,WAAW,KAC3B,KAAK,WAAW,mBAAmB,KACnC,KAAK,WAAW,sBAAsB,KACtC,KAAK,WAAW,YAAY,KAC5B,KAAK,WAAW,UAAU,GAC1B;AACA,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,WAAW,eAAe,KAAK,KAAK,WAAW,kBAAkB,GAAG;AAElF,YAAI,SAAS;AACb,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,WAAW,QAAQ,GAAG;AAEpC,YAAI,KAAK,KAAK,IAAI;AAAA,MACpB;AAEA;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,CAAC;AACnB,UAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAI,SAAS,KAAK;AAChB,mBAAa;AACb,kBAAY,IAAI;AAAA,IAClB,WAAW,SAAS,KAAK;AACvB,kBAAY,KAAK,EAAE,MAAM,MAAM,IAAI,OAAO,OAAO,MAAM,CAAC;AACxD,eAAS;AACT,iBAAW;AAAA,IACb,WAAW,SAAS,KAAK;AACvB,YAAM,MAAM,YAAY,MAAM;AAC9B,UAAI,QAAQ,QAAW;AACrB,mBAAW,KAAK,IAAI;AAAA,MACtB,OAAO;AACL,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF,WAAW,SAAS,QAAQ,KAAK,WAAW,8BAA8B,GAAG;AAC3E,gBAAU;AAAA,IACZ,OAAO;AAEL,mBAAa;AACb,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF;AACA,eAAa;AACb,SAAO;AACT;AAMO,SAAS,WAAW,OAAsE;AAC/F,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,KAAK,MAAM;AAC3B,UAAI,IAAI,SAAS,SAAS,IAAI,SAAS,SAAU,UAAS;AAC1D,UAAI,IAAI,SAAS,SAAS,IAAI,SAAS,SAAU,YAAW;AAAA,IAC9D;AAAA,EACF;AACA,SAAO,EAAE,OAAO,SAAS,OAAO,MAAM,OAAO;AAC/C;;;AChRA,IAAM,WAAW,oBAAI,IAAI;AAAA;AAAA,EAEvB;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAY;AAAA,EAAS;AAAA,EAAW;AAAA,EAAS;AAAA,EAAO;AAAA,EACvE;AAAA,EAAU;AAAA,EAAc;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAO;AAAA,EAAO;AAAA,EACpE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAW;AAAA,EACvE;AAAA,EAAY;AAAA,EAAO;AAAA,EAAS;AAAA,EAAW;AAAA,EAAS;AAAA,EAAO;AAAA,EAAS;AAAA,EAAM;AAAA,EACtE;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAa;AAAA,EAClE;AAAA,EAAc;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAY;AAAA,EAC5D;AAAA,EAAW;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAW;AAAA,EAAO;AAAA,EAAQ;AAAA,EAChE;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAO;AAAA,EAAY;AAAA;AAAA,EAEjD;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EACpE;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAY;AAAA,EAAU;AAAA;AAAA,EAElE;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EACrE;AAAA,EAAO;AAAA,EAAQ;AAAA;AAAA,EAEf;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EACnE;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA;AAAA,EAEnD;AAAA,EAAO;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EACtE;AAAA,EAAY;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAU;AAAA,EAAY;AAAA,EAAa;AAAA,EACnE;AAAA,EAAY;AACd,CAAC;AAGD,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAClE;AAAA,EAAQ;AAAA,EAAK;AAAA,EAAK;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACpE;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAO;AACzE,CAAC;AAGD,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,MAAM,MAAM,QAAQ,OAAO,OAAO,QAAQ,QAAQ,KAAK,CAAC;AAG1F,SAAS,QAAQ,MAA+B;AACrD,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,QAAM,MAAM,OAAO,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,YAAY,IAAI;AAC3D,MAAI,QAAQ,MAAM,CAAC,eAAe,IAAI,GAAG,EAAG,QAAO;AACnD,SAAO,EAAE,aAAa,kBAAkB,IAAI,GAAG,EAAE;AACnD;AAGA,SAAS,aAAa,IAAqB;AACzC,SAAO,YAAY,KAAK,EAAE;AAC5B;AAGA,SAAS,YAAY,IAAqB;AACxC,SAAO,eAAe,KAAK,EAAE;AAC/B;AAGA,SAAS,aAAa,IAAqB;AACzC,SAAO,QAAQ,KAAK,EAAE;AACxB;AAGA,SAAS,YAAY,IAAqB;AACxC,SAAO,sBAAsB,KAAK,EAAE;AACtC;AAMO,SAAS,aAAa,MAAc,MAAyB;AAClE,QAAM,SAAkB,CAAC;AACzB,MAAI,QAAQ;AACZ,QAAM,aAAa,MAAY;AAC7B,QAAI,UAAU,IAAI;AAChB,aAAO,KAAK,EAAE,MAAM,OAAO,KAAK,KAAK,CAAC;AACtC,cAAQ;AAAA,IACV;AAAA,EACF;AACA,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,UAAM,KAAK,KAAK,CAAC;AAEjB,QAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AAC1C,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,KAAK,QAAQ;AACtB,YAAI,KAAK,CAAC,MAAM,MAAM;AACpB,eAAK;AACL;AAAA,QACF;AACA,YAAI,KAAK,CAAC,MAAM,GAAI;AACpB,aAAK;AAAA,MACP;AACA,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,CAAC,GAAG,KAAK,UAAU,CAAC;AACjF,UAAI,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM;AAC/B;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,KAAK;AACrC,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,KAAK,UAAU,CAAC;AACnD;AAAA,IACF;AACA,QAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,KAAK;AACrC,YAAM,QAAQ,KAAK,QAAQ,MAAM,IAAI,CAAC;AACtC,YAAM,MAAM,UAAU,KAAK,KAAK,SAAS,QAAQ;AACjD,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,GAAG,GAAG,KAAK,UAAU,CAAC;AACxD,UAAI;AACJ;AAAA,IACF;AACA,QAAI,KAAK,eAAe,OAAO,KAAK;AAClC,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,KAAK,UAAU,CAAC;AACnD;AAAA,IACF;AAEA,QAAI,aAAa,EAAE,GAAG;AACpB,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,KAAK,UAAU,YAAY,KAAK,CAAC,CAAC,EAAG,MAAK;AACrD,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,CAAC,GAAG,KAAK,UAAU,CAAC;AACtD,UAAI;AACJ;AAAA,IACF;AAEA,QAAI,aAAa,EAAE,GAAG;AACpB,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,KAAK,UAAU,YAAY,KAAK,CAAC,CAAC,EAAG,MAAK;AACrD,YAAM,OAAO,KAAK,MAAM,GAAG,CAAC;AAC5B,iBAAW;AACX,aAAO,KAAK,EAAE,MAAM,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,WAAW,KAAK,CAAC;AACrE,UAAI;AACJ;AAAA,IACF;AACA,aAAS;AACT,SAAK;AAAA,EACP;AACA,aAAW;AACX,SAAO;AACT;AAOO,SAAS,aAAa,SAAiB,SAAsC;AAClF,QAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,QAAM,IAAI,MAAM,KAAK,OAAO;AAE5B,MAAI,MAAM;AACV,QAAM,SAAS,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AAC1C,SAAO,MAAM,UAAU,EAAE,GAAG,MAAM,EAAE,GAAG,EAAG,QAAO;AAEjD,MAAI,SAAS,EAAE;AACf,MAAI,SAAS,EAAE;AACf,SAAO,SAAS,OAAO,SAAS,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACtE,cAAU;AACV,cAAU;AAAA,EACZ;AACA,MAAI,QAAQ,EAAE,UAAU,QAAQ,EAAE,OAAQ,QAAO;AACjD,SAAO,EAAE,UAAU,KAAK,QAAQ,UAAU,KAAK,OAAO;AACxD;;;ACnLA,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAGA,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuNvB,IAAM,0BAA8D;AAAA,EAClE,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AACR;AA2CO,IAAM,WAAkC;AAAA,EAC7C,IAAI;AAAA,IACF,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,kBAAkB,CAAC,UAAU,qDAAsC,KAAK;AAAA,IACxE,qBAAqB,CAAC,UAAU,mCAAoB,KAAK;AAAA,IACzD,gBAAgB;AAAA,IAChB,OAAO,CAAC,OAAO,SAAS,UAAU,IAAI,KAAK,UAAK,OAAO,SAAM,KAAK;AAAA,IAClE,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,kBAAkB,CAAC,UAAU,mDAAyC,KAAK,WAAW,UAAU,IAAI,KAAK,GAAG;AAAA,IAC5G,qBAAqB,CAAC,UAAU,yBAAyB,KAAK;AAAA,IAC9D,gBAAgB;AAAA,IAChB,OAAO,CAAC,OAAO,SAAS,UAAU,IAAI,KAAK,UAAK,OAAO,SAAM,KAAK,QAAQ,UAAU,IAAI,KAAK,GAAG;AAAA,IAChG,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,WAAW;AAAA,IACX,QAAQ;AAAA,MACN,UAAU;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAGA,SAAS,YAAY,OAAwC;AAC3D,SAAO;AAAA,IACL,gBAAgB,MAAM;AAAA,IACtB,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,YAAY,MAAM;AAAA,IAClB,YAAY,MAAM;AAAA,IAClB,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM;AAAA,IAClB,oBAAoB,MAAM;AAAA,IAC1B,YAAY,MAAM;AAAA,IAClB,QAAQ,MAAM;AAAA,IACd,gBAAgB,MAAM;AAAA,IACtB,cAAc,MAAM;AAAA,IACpB,WAAW,MAAM;AAAA,EACnB;AACF;AAkBO,SAAS,mBACd,UACA,MACA,OAAa,MACL;AACR,QAAM,QAAQ,SAAS,IAAI;AAE3B,MAAI,SAAS,UAAU,SAAS,WAAW,EAAG,QAAO,MAAM;AAC3D,QAAM,SAAS,SAAS,SAAS,MAAM,iBAAiB,SAAS,MAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM;AACpH,QAAM,QAAQ,SAAS,IAAI,CAAC,SAAS,UAAU;AAC7C,UAAM,QAAQ,QAAQ,KAAK,MAAM,IAAI;AACrC,UAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,QAAQ,IAAI,IAAI,QAAQ,MAAM,WAAM,MAAM,CAAC,CAAC;AAE3E,UAAM,OAAO,MAAM,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,MAAM,IAAI,EAAE;AACtD,WAAO,CAAC,OAAO,GAAG,IAAI,EAAE,KAAK,IAAI;AAAA,EACnC,CAAC;AACD,SAAO,CAAC,QAAQ,IAAI,GAAG,KAAK,EAAE,KAAK,IAAI;AACzC;AAGA,SAAS,aAAa,QAAyB;AAC7C,MAAI,MAAM;AACV,aAAW,SAAS,QAAQ;AAC1B,UAAM,UAAU,WAAW,MAAM,IAAI;AACrC,WAAO,MAAM,QAAQ,OAAO,UAAU,gBAAgB,MAAM,GAAG,KAAK,OAAO;AAAA,EAC7E;AACA,SAAO;AACT;AAUA,SAAS,SACP,MACA,MACA,OACA,WACA,MACA,gBACQ;AACR,MAAI,SAAS,OAAW,QAAO,uCAAuC,IAAI;AAC1E,QAAM,OAAO,UAAU,OAAO,oCAAoC,WAAW,cAAc,CAAC,oBAAe;AAC3G,MAAI;AACJ,MAAI,cAAc,MAAM;AAEtB,WAAO,SAAS,OAAO,WAAW,IAAI,IAAI,aAAa,aAAa,MAAM,IAAI,CAAC;AAAA,EACjF,OAAO;AAGL,UAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,UAAM,QAAQ,SAAS,QAAQ,UAAU,WAAW,UAAU;AAC9D,UAAM,MAAM,SAAS,QAAQ,UAAU,SAAS,UAAU;AAC1D,UAAM,SAAS,MAAM,MAAM,GAAG,KAAK,EAAE,KAAK,EAAE;AAC5C,UAAM,MAAM,MAAM,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE;AAC3C,UAAM,SAAS,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE;AACvC,UAAM,MAAM,CAAC,YACX,SAAS,OAAO,WAAW,OAAO,IAAI,aAAa,aAAa,SAAS,IAAI,CAAC;AAChF,WAAO,IAAI,MAAM,KACZ,QAAQ,KAAK,yBAAyB,IAAI,KAAK,IAAI,GAAG,CAAC,YAAY,MACpE,IAAI,MAAM;AAAA,EAChB;AACA,SAAO,uCAAuC,IAAI,KAAK,IAAI,GAAG,IAAI;AACpE;AAGA,SAAS,QAAQ,KAAc,MAAuB,MAAc,gBAAgC;AAClG,MAAI,IAAI,SAAS,OAAO;AACtB,WAAO,8BAA8B,WAAW,IAAI,WAAW,EAAE,CAAC;AAAA,EACpE;AAEA,QAAM,MAAM,CAAC,IAAwB,SAAyB;AAC5D,QAAI,OAAO,OAAW,QAAO,qCAAqC,IAAI;AACtE,WAAO,qCAAqC,IAAI,mBAAmB,WAAW,IAAI,CAAC,mBAAmB,EAAE,mBAAmB,IAAI,KAAK,EAAE;AAAA,EACxI;AACA,QAAM,OAAO,iCAAiC,IAAI,IAAI;AACtD,MAAI,IAAI,SAAS,OAAO;AACtB,WAAO,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,MAAM,MAAM,cAAc,IACvG,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,MAAM,MAAM,cAAc,IAAI;AAAA,EACvG;AACA,MAAI,IAAI,SAAS,OAAO;AACtB,WAAO,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,MAAM,MAAM,cAAc,IACvG,IAAI,QAAW,KAAK,IAAI,SAAS,QAAW,OAAO,QAAW,MAAM,MAAM,cAAc,IAAI;AAAA,EAClG;AACA,MAAI,IAAI,SAAS,OAAO;AACtB,WAAO,OAAO,IAAI,QAAW,KAAK,IAAI,SAAS,QAAW,OAAO,QAAW,MAAM,MAAM,cAAc,IAClG,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,MAAM,MAAM,cAAc,IAAI;AAAA,EACvG;AAEA,QAAM,QAAQ,aAAa,IAAI,WAAW,IAAI,IAAI,WAAW,EAAE;AAC/D,SAAO,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,OAAO,MAAM,cAAc,IACxG,IAAI,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,OAAO,MAAM,cAAc,IAAI;AACxG;AAUO,SAAS,oBACd,OACA,OACA,SACA,OAAa,MACL;AACR,QAAM,QAAQ,SAAS,IAAI;AAC3B,QAAM,QAAQ,WAAW,KAAK;AAC9B,QAAM,YAAY,MAAM,MAAM,MAAM,OAAO,MAAM,SAAS,MAAM,KAAK;AACrE,QAAM,WAAW,OAAO,QAAQ,KAAK,EAClC,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,GAAG,IAAI,KAAK,KAAK,GAAG,EAC3C,KAAK,GAAG;AAIX,QAAM,UAAU,KAAK,UAAU,OAAO,EAAE,QAAQ,MAAM,SAAS;AAC/D,QAAM,MAAM,MACT,IAAI,CAAC,MAAM,UAAU,kCAAkC,KAAK,KAAK,WAAW,YAAY,KAAK,OAAO,KAAK,YAAY,KAAK,OAAO,CAAC,CAAC,MAAM,EACzI,KAAK,EAAE;AACV,QAAM,WAAW,MAAM,IAAI,CAAC,MAAM,UAAU;AAC1C,UAAM,UAAU,YAAY,KAAK,OAAO;AACxC,UAAM,UAAU,YAAY,KAAK,OAAO;AACxC,UAAM,OAAO,YAAY,MAAM,YAAY,UAAU,GAAG,OAAO,WAAM,OAAO,KAAM,YAAY,KAAK,UAAU;AAC7G,UAAM,OAAO,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,QAAQ,CAAC;AAE5D,UAAMC,QAAO,QAAQ,OAAO,KAAK,QAAQ,OAAO;AAChD,UAAM,cAAc,YAAY,MAAM,YAAY,cAAc,UAAU;AAC1E,UAAM,OAAO,KAAK,KAAK,WAAW,IAC9B,8BAA8B,WAAW,KAAK,KAAK,KAAK,QAAK,KAAK,KAAK,MAAM,CAAC,WAC9E,KAAK,KAAK,IAAI,CAAC,QAAQ,QAAQ,KAAKA,OAAM,aAAa,MAAM,cAAc,CAAC,EAAE,KAAK,EAAE;AACzF,UAAM,OAAO,gDAAgD,MAAM,OAAO,KAAK,MAAM,CAAC,6BAA6B,WAAW,IAAI,CAAC,aAC9H,KAAK,SAAS,IAAI,sBAAsB,WAAW,KAAK,KAAK,QAAK,CAAC,CAAC,YAAY,MACjF;AACJ,WAAO,kCAAkC,KAAK,KAAK,IAAI,4BAA4B,IAAI;AAAA,EACzF,CAAC,EAAE,KAAK,EAAE;AACV,SAAO;AAAA,cACK,SAAS,OAAO,UAAU,IAAI;AAAA;AAAA;AAAA;AAAA,0BAIrB,SAAS;AAAA;AAAA,UAEtB,QAAQ;AAAA,EAChB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,wBAKQ,SAAS;AAAA;AAAA,6DAE4B,MAAM,cAAc;AAAA,sDAC3B,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA,8BAIrC,MAAM,UAAU;AAAA,uDACS,MAAM,UAAU;AAAA,wDACf,MAAM,WAAW;AAAA;AAAA,uBAElD,GAAG;AAAA,QAClB,QAAQ;AAAA;AAAA,yCAEyB,MAAM,UAAU;AAAA,wBACjC,WAAW,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,cAK7B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAaT,KAAK,UAAU,YAAY,KAAK,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4I9C;;;AHlqBA,IAAM,YAAY;AAElB,IAAM,gBAAgB;AAEtB,IAAM,WAAW;AAEjB,IAAM,iBAAiB;AAGvB,IAAI,gBAAgB;AAIpB,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoDnB,SAAS,cAAoB;AAC3B,MAAI,iBAAiB,OAAO,aAAa,YAAa;AACtD,kBAAgB;AAChB,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,QAAQ,SAAS;AACrB,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC/B;AAKA,IAAM,qBAA6C;AAAA,EACjD,uBAAuB;AAAA,EACvB,0BAA0B;AAAA,EAC1B,mCAAmC;AAAA,EACnC,6BAA6B;AAAA,EAC7B,+BAA+B;AAAA,EAC/B,8BAA8B;AAAA,EAC9B,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,mCAAmC;AAAA,EACnC,qCAAqC;AAAA,EACrC,sCAAsC;AAAA,EACtC,yBAAyB;AAAA;AAAA,EAEzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,0BAA0B;AAC5B;AAGA,SAAS,mBAA2C;AAClD,QAAM,MAA8B,CAAC;AACrC,QAAM,SAAS,iBAAiB,SAAS,IAAI;AAC7C,aAAW,QAAQ,OAAO,KAAK,kBAAkB,GAAG;AAClD,UAAM,QAAQ,OAAO,iBAAiB,IAAI,EAAE,KAAK;AACjD,QAAI,IAAI,IAAI,UAAU,KAAK,QAAQ,mBAAmB,IAAI;AAAA,EAC5D;AACA,SAAO;AACT;AAaA,IAAI,gBAAsC;AAQ1C,SAAS,aAAmB;AAC1B,MAAI,kBAAkB,MAAM;AAC1B,WAAO,cAAc,UAAU,EAAE,WAAW,OAAO,OAAO;AAAA,EAC5D;AACA,QAAM,MAAM,UAAU,YAAY,CAAC,KAAK,UAAU,YAAY;AAC9D,SAAO,IAAI,YAAY,EAAE,WAAW,IAAI,IAAI,OAAO;AACrD;AAEA,SAAS,cAAc,MAAoB;AACzC,QAAM,QAAQ,aAAa,IAAI;AAC/B,MAAI,MAAM,WAAW,EAAG;AACxB,QAAM,OAAO,oBAAoB,OAAO,iBAAiB,GAAG,MAAM,WAAW,CAAC;AAC9E,QAAM,MAAM,IAAI,gBAAgB,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC,CAAC;AAEvE,aAAWC,QAAO,eAAe;AAC/B,QAAIA,SAAQ,QAAQA,KAAI,OAAQ,eAAc,OAAOA,IAAG;AAAA,EAC1D;AACA,QAAM,MAAM,OAAO,KAAK,KAAK,QAAQ;AACrC,MAAI,QAAQ,KAAM,eAAc,IAAI,GAAG;AACvC,SAAO,WAAW,MAAM,IAAI,gBAAgB,GAAG,GAAG,GAAK;AACzD;AAKA,IAAM,gBAAgB,oBAAI,IAAmB;AAG7C,IAAI,iBAAkD;AAGtD,SAAS,iBAAiB,KAA+B;AACvD,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,QAAM,MAAuB,CAAC;AAC9B,aAAW,QAAQ,KAAK;AACtB,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAC/C,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,SAAS,YAAY,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,SAAS,SAAU;AAC9F,QAAI,EAAE,KAAK,KAAK,MAAM,GAAI;AAC1B,QAAI,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,KAAK,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC;AAAA,EACvE;AACA,SAAO;AACT;AAOA,SAAS,oBAAoB,OAA2B;AACtD,QAAM,OAAO,MAAM;AACnB,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAC/C,MAAI,KAAK,SAAS,2BAA4B;AAC9C,MAAI,CAAC,cAAc,IAAI,MAAM,MAAuB,EAAG;AACvD,QAAM,WAAW,iBAAiB,KAAK,QAAQ;AAC/C,QAAM,OAAO,KAAK,SAAS,SAAS,SAAS;AAE7C,MAAI,SAAS,WAAW,KAAK,SAAS,OAAQ;AAC9C,QAAM,SAAS,mBAAmB,UAAU,MAAM,WAAW,CAAC;AAC9D,QAAM,SAAS,MAAM;AACrB,MAAI,mBAAmB,MAAM;AAC3B,mBAAe,MAAM;AACrB,WAAO,YAAY,EAAE,MAAM,uBAAuB,IAAI,KAAK,GAAG,GAAG;AAAA,EACnE,OAAO;AACL,WAAO,YAAY,EAAE,MAAM,uBAAuB,IAAI,MAAM,GAAG,GAAG;AAAA,EACpE;AACF;AAYA,SAAS,cACP,MACA,SACA,MACM;AACN,QAAM,QAAQ,SAAS,WAAW,CAAC;AAGnC,MAAI,MAA0B,KAAK,cAAc,mBAAmB;AACpE,MAAI,QAAQ,MAAM;AAChB,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,YAAQ,QAAQ,WAAW;AAC3B,SAAK,YAAY,OAAO;AACxB,UAAM;AAAA,EACR;AACA,MAAI,IAAI,cAAc,mBAAmB,MAAM,MAAM;AACnD,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,QAAI,QAAQ,WAAW;AACvB,QAAI,cAAc,MAAM;AACxB,QAAI,QAAQ,MAAM;AAClB,QAAI,iBAAiB,SAAS,CAAC,UAAU;AAEvC,YAAM,gBAAgB;AACtB,YAAM,eAAe;AACrB,YAAM,OAAO,QAAQ;AACrB,UAAI,SAAS,KAAM,eAAc,IAAI;AAAA,IACvC,CAAC;AACD,QAAI,YAAY,GAAG;AAAA,EACrB;AACA,MAAI,SAAS,QAAQ,IAAI,cAAc,oBAAoB,MAAM,MAAM;AACrE,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,QAAI,QAAQ,YAAY;AACxB,QAAI,cAAc,KAAK,SAAS,MAAM,SAAS,MAAM;AACrD,QAAI,QAAQ,KAAK,SAAS,MAAM,cAAc,MAAM;AACpD,QAAI,iBAAiB,SAAS,CAAC,UAAU;AACvC,YAAM,gBAAgB;AACtB,YAAM,eAAe;AACrB,WAAK,SAAS;AAAA,IAChB,CAAC;AACD,QAAI,YAAY,GAAG;AAAA,EACrB;AACF;AAGA,SAAS,cAAc,MAAyB;AAC9C,aAAW,OAAO,KAAK,iBAAiB,uCAAuC,GAAG;AAChF,QAAI,OAAO;AAAA,EACb;AAEA,QAAM,MAAM,KAAK,cAAc,mBAAmB;AAClD,MAAI,eAAe,eAAe,IAAI,cAAc,uCAAuC,MAAM,MAAM;AACrG,QAAI,OAAO;AAAA,EACb;AACF;AAuBA,SAAS,kBAAkB,IAAqC;AAC9D,QAAM,QAAQ,GAAG;AACjB,aAAW,SAAS,GAAG,UAAU;AAC/B,QAAI,EAAE,iBAAiB,aAAc;AACrC,QAAI,MAAM,aAAa,iBAAiB,KAAK,MAAM,aAAa,kBAAkB,EAAG;AACrF,QAAI,MAAM,YAAY,MAAO,QAAO;AAAA,EACtC;AACA,aAAW,SAAS,GAAG,UAAU;AAC/B,QAAI,EAAE,iBAAiB,aAAc;AACrC,QAAI,MAAM,aAAa,iBAAiB,KAAK,MAAM,aAAa,kBAAkB,EAAG;AACrF,QAAI,UAAU,MAAO;AACrB,QAAI,MAAM,YAAY,MAAO,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAGA,SAAS,eAAe,IAA+B;AACrD,QAAM,SAAS,GAAG;AAClB,QAAM,OAAO,kBAAkB,EAAE;AACjC,MAAI,EAAE,kBAAkB,gBAAgB,SAAS,KAAM,QAAO;AAO9D,MAAI,OAAO;AACX,QAAM,YAAY,OAAO,mBAAmB;AAC5C,MAAI,qBAAqB,aAAa;AACpC,WAAO,UAAU,eAAe;AAAA,EAClC,OAAO;AACL,WAAO,OAAO,mBAAmB,eAAe;AAAA,EAClD;AACA,SAAO,KAAK,KAAK,EAAE,YAAY;AAC/B,QAAM,OAAO,KAAK,eAAe;AACjC,QAAM,YAAY,SAAS,UAAU,SAAS,WAAW,WAAW,IAAI;AACxE,SAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAC3C;AAOA,SAAS,oBAAoB,SAA0C;AACrE,QAAM,WAAW,QAAQ;AACzB,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,UAAM,QAAQ,SAAS,CAAC;AACxB,QAAI,EAAE,iBAAiB,gBAAgB,MAAM,YAAY,MAAO;AAChE,QAAI,MAAM,aAAa,iBAAiB,KAAK,MAAM,aAAa,kBAAkB,EAAG;AACrF,QAAI,WAAW,MAAM,eAAe,EAAE,EAAG,QAAO;AAAA,EAClD;AACA,SAAO;AACT;AAMA,SAAS,gBAAgB,WAAgC;AACvD,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,UAAU,UAAU;AACtC,QAAI,iBAAiB,eAAe,MAAM,YAAY,OAAO;AAC3D,YAAM,KAAK,MAAM,eAAe,EAAE;AAAA,IACpC;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGA,SAAS,cAAc,IAA+B;AACpD,MAAI,GAAG,aAAa,cAAc,GAAG;AACnC,WAAO,EAAE,MAAM,IAAI,WAAW,OAAO,SAAS,KAAK;AAAA,EACrD;AACA,QAAM,SAAS,oBAAoB,EAAE;AACrC,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,OAAO,gBAAgB,MAAM;AACnC,SAAO,EAAE,MAAM,WAAW,WAAW,IAAI,GAAG,SAAS,MAAM;AAC7D;AAGA,SAAS,SAAS,IAA+B;AAC/C,MAAI,GAAG,UAAU,SAAS,eAAe,EAAG,QAAO,eAAe,EAAE;AACpE,MAAI,GAAG,aAAa,eAAe,GAAG;AAGpC,QAAI,GAAG,QAAQ,sBAAsB,MAAM,KAAM,QAAO;AACxD,WAAO,cAAc,EAAE;AAAA,EACzB;AACA,SAAO;AACT;AAGA,SAAS,cAAc,IAAqC;AAC1D,QAAM,QAAQ,GAAG;AACjB,SAAO,iBAAiB,cAAc,QAAQ;AAChD;AAUA,IAAM,eAAe,oBAAI,IAAiD;AAS1E,SAAS,aAAa,MAAwB;AAC5C,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,SAAQ,KAA4B,QAAQ;AAC9C;AAGA,SAAS,oBAAoB,MAAuF;AAClH,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,IAAI;AACV,MAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,EAAG,QAAO;AAEtC,MAAI,EAAE,SAAS,SAAS,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,QAAQ,OAAQ,EAAE,KAA4B,SAAS,WAAW;AACjI,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,eAAe,MAAuB;AAC7C,MAAI,CAAC,oBAAoB,IAAI,EAAG,QAAO;AACvC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,KAAK,SAAS;AAChC,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM;AACjD,UAAM,IAAI;AACV,QAAI,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,SAAU,OAAM,KAAK,EAAE,IAAI;AAAA,EACxE;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAMA,SAAS,kBAAkB,WAAmB,MAAsB;AAClE,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,OAAO,KAAK,OAAO;AAC5B,UAAM,OAAO,KAAK,MAAM,IAAI,GAAG;AAC/B,QAAI,SAAS,UAAa,KAAK,SAAS,YAAa;AACrD,UAAM,OAAO,aAAa,KAAK,IAAI;AACnC,QAAI,SAAS,QAAQ,CAAC,oBAAoB,IAAI,EAAG;AACjD,QAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,OAAQ;AACrD,UAAM,OAAO,eAAe,IAAI;AAChC,QAAI,CAAC,WAAW,IAAI,EAAG;AACvB,UAAM,IAAI,KAAK,IAAI;AAAA,EACrB;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,cAAc;AACvC,QAAI,MAAM,cAAc,aAAa,CAAC,MAAM,IAAI,GAAG,EAAG,cAAa,OAAO,GAAG;AAAA,EAC/E;AACA,aAAW,CAAC,KAAK,IAAI,KAAK,MAAO,cAAa,IAAI,KAAK,EAAE,WAAW,KAAK,CAAC;AAC5E;AAGA,SAAS,aAAa,KAAiC;AACrD,aAAW,MAAM,SAAS,iBAAiB,wBAAwB,GAAG;AACpE,QAAI,cAAc,eAAe,GAAG,aAAa,sBAAsB,MAAM,IAAK,QAAO;AAAA,EAC3F;AACA,SAAO;AACT;AAGA,SAAS,uBAA6B;AACpC,aAAW,CAAC,KAAK,KAAK,KAAK,cAAc;AACvC,UAAM,MAAM,aAAa,GAAG;AAC5B,QAAI,QAAQ,KAAM;AAClB,UAAM,SAAS,IAAI,cAAc,sBAAsB;AACvD,QAAI,EAAE,kBAAkB,aAAc;AACtC,kBAAc,QAAQ,MAAM,aAAa,IAAI,GAAG,GAAG,QAAQ,MAAM,MAAM,IAAI;AAAA,EAC7E;AACF;AA0BA,IAAM,cAAN,MAAkB;AAAA,EAAlB;AACE,wBAAiB,UAAS,oBAAI,QAAiC;AAC/D,wBAAQ,YAAoC;AAC5C,wBAAQ,aAA2B;AACnC,wBAAQ,eAA6B;AAAA;AAAA,EAErC,QAAc;AACZ,QAAI,KAAK,aAAa,KAAM;AAG5B,SAAK,WAAW,IAAI,iBAAiB,MAAM,KAAK,aAAa,CAAC;AAC9D,SAAK,SAAS,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AACvE,SAAK,aAAa;AAGlB,SAAK,cAAc,OAAO,YAAY,MAAM,KAAK,KAAK,GAAG,cAAc;AAAA,EACzE;AAAA,EAEA,OAAa;AACX,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW;AAChB,QAAI,KAAK,cAAc,MAAM;AAC3B,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACnB;AACA,QAAI,KAAK,gBAAgB,MAAM;AAC7B,oBAAc,KAAK,WAAW;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EAGF;AAAA;AAAA,EAGQ,eAAqB;AAC3B,QAAI,KAAK,cAAc,KAAM;AAC7B,SAAK,YAAY,OAAO,WAAW,MAAM;AACvC,WAAK,YAAY;AACjB,WAAK,KAAK;AAAA,IACZ,GAAG,aAAa;AAAA,EAClB;AAAA;AAAA,EAGQ,OAAa;AACnB,yBAAqB;AACrB,eAAW,MAAM,SAAS,iBAAiB,iCAAiC,GAAG;AAC7E,WAAK,QAAQ,EAAiB;AAAA,IAChC;AAAA,EACF;AAAA;AAAA,EAGQ,QAAQ,IAAuB;AACrC,UAAM,QAAQ,SAAS,EAAE;AACzB,QAAI,UAAU,QAAQ,MAAM,WAAW,CAAC,MAAM,aAAa,MAAM,KAAK,KAAK,MAAM,IAAI;AACnF,YAAM,OAAO,cAAc,EAAE;AAC7B,UAAI,SAAS,KAAM,eAAc,IAAI;AAErC,YAAM,OAAO,kBAAkB,EAAE;AACjC,UAAI,SAAS,KAAM,MAAK,MAAM,UAAU;AACxC,WAAK,OAAO,OAAO,EAAE;AACrB;AAAA,IACF;AACA,UAAM,KAAK,KAAK,OAAO,IAAI,EAAE;AAC7B,QAAI,OAAO,UAAa,GAAG,SAAS,MAAM,MAAM;AAE9C,UAAI,CAAC,GAAG,QAAQ;AAGd,aAAK,eAAe,IAAI,EAAE;AAC1B;AAAA,MACF;AACA,UAAI,KAAK,IAAI,IAAI,GAAG,UAAU,SAAU;AAAA,IAE1C;AACA,QAAI,OAAO,UAAa,GAAG,UAAU,KAAM,cAAa,GAAG,KAAK;AAChE,UAAM,QAAQ,OAAO,WAAW,MAAM;AACpC,YAAM,MAAM,KAAK,OAAO,IAAI,EAAE;AAC9B,UAAI,QAAQ,UAAa,IAAI,UAAU,MAAO;AAC9C,UAAI,QAAQ;AAEZ,YAAM,KAAK,SAAS,EAAE;AACtB,UAAI,OAAO,QAAQ,GAAG,WAAW,CAAC,GAAG,aAAa,GAAG,SAAS,IAAI,KAAM;AACxE,WAAK,aAAa,IAAI,GAAG;AAAA,IAC3B,GAAG,SAAS;AACZ,SAAK,OAAO,IAAI,IAAI;AAAA,MAClB,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,SAAS,IAAI,WAAW;AAAA,MACxB,QAAQ,IAAI,UAAU;AAAA,MACtB,QAAQ,IAAI,UAAU;AAAA,IACxB,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,aAAa,IAAiB,IAAsB;AAC1D,UAAM,OAAO,cAAc,EAAE;AAC7B,QAAI,SAAS,KAAM;AACnB,QAAI,aAAa,GAAG,IAAI,EAAE,WAAW,GAAG;AACtC,SAAG,SAAS;AACZ,SAAG,UAAU,KAAK,IAAI;AACtB,oBAAc,IAAI;AAClB;AAAA,IACF;AACA,OAAG,SAAS;AACZ,UAAM,cAAc,GAAG,UAAU,SAAS,eAAe;AACzD,QAAI,aAAa;AAEf,WAAK,eAAe,IAAI,EAAE;AAAA,IAC5B;AAEA;AAAA,MACE;AAAA,MACA,MAAM,SAAS,EAAE,GAAG,QAAQ,GAAG;AAAA,MAC/B,cAAc,EAAE,QAAQ,GAAG,QAAQ,UAAU,MAAM,KAAK,WAAW,EAAE,EAAE,IAAI;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA,EAGQ,eAAe,IAAiB,IAAsB;AAC5D,QAAI,CAAC,GAAG,UAAU,SAAS,eAAe,EAAG;AAC7C,UAAM,OAAO,kBAAkB,EAAE;AACjC,QAAI,SAAS,KAAM,MAAK,MAAM,UAAU,GAAG,SAAS,SAAS;AAAA,EAC/D;AAAA;AAAA,EAGQ,WAAW,IAAuB;AACxC,UAAM,KAAK,KAAK,OAAO,IAAI,EAAE;AAC7B,QAAI,OAAO,OAAW;AACtB,OAAG,SAAS,CAAC,GAAG;AAChB,SAAK,eAAe,IAAI,EAAE;AAC1B,UAAM,OAAO,cAAc,EAAE;AAC7B,UAAM,MAAM,MAAM,cAAc,oBAAoB;AACpD,QAAI,eAAe,aAAa;AAC9B,YAAM,QAAQ,SAAS,WAAW,CAAC;AACnC,UAAI,cAAc,GAAG,SAAS,MAAM,SAAS,MAAM;AAAA,IACrD;AAAA,EACF;AACF;AAaO,IAAM,SAAS,CAAC,SAAS,QAAQ;AAGjC,SAAS,MAAM,KAA0B;AAE9C,QAAM,SAAS,IAAI;AACnB,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,OAAQ,OAAmC,cAAc,YAAY;AACxH,oBAAgB;AAAA,EAClB;AACA,MAAI,MAAM;AAAA,IACR;AAAA,IACA,MAAM,IAAI,MAAM,SAAS;AAAA,MACvB,MAAM;AAAA,MACN,IAAI;AAAA,IACN,GAAG,kBAAkB;AAAA,EACvB;AACA,MAAI,MAAM;AAAA,IACR;AAAA,IACA,MAAM,IAAI,MAAM,SAAS;AAAA,MACvB,MAAM;AAAA,MACN,IAAI;AAAA;AAAA;AAAA,MAGJ,OAAO;AAAA,IACT,GAAG,qBAAqB;AAAA,EAC1B;AACF;AAOA,SAAS,qBAAqB;AAC5B,8BAAU,MAAM;AACd,gBAAY;AACZ,UAAM,UAAU,IAAI,YAAY;AAChC,YAAQ,MAAM;AACd,UAAM,YAAY;AAClB,WAAO,iBAAiB,WAAW,SAAS;AAC5C,WAAO,MAAM;AACX,cAAQ,KAAK;AACb,aAAO,oBAAoB,WAAW,SAAS;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AA4BA,SAAS,sBAAsB,EAAE,YAAY,SAAS,WAAW,aAAa,GAAuB;AAInG,QAAM,OAAO,YAAY,SACrB,QAAQ,CAAC,UAAU,KAAK,IACxB,WAAW,CAAC,UAAU,MAAM,IAAI;AACpC,8BAAU,MAAM;AACd,QAAI,SAAS,OAAW,mBAAkB,OAAO,SAAS,GAAG,IAAI;AAAA,EACnE,GAAG,CAAC,MAAM,SAAS,CAAC;AACpB,8BAAU,MAAM;AAEd,UAAM,SAAS,CAAC,SAAuB,cAAc,SAAS,IAAI;AAClE,qBAAiB;AACjB,WAAO,MAAM;AACX,UAAI,mBAAmB,OAAQ,kBAAiB;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AACjB,SAAO;AACT;",
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.3",
3
+ "version": "0.1.5",
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"