reasonix 0.39.1 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +21 -13
  2. package/README.zh-CN.md +19 -13
  3. package/dashboard/app.css +586 -2
  4. package/dashboard/dist/app.js +2134 -284
  5. package/dashboard/dist/app.js.map +1 -1
  6. package/dist/cli/acp-64VQZLDJ.js +708 -0
  7. package/dist/cli/acp-64VQZLDJ.js.map +1 -0
  8. package/dist/cli/chat-ZAGX52RV.js +46 -0
  9. package/dist/cli/chunk-2CXPDAWX.js +100 -0
  10. package/dist/cli/chunk-2CXPDAWX.js.map +1 -0
  11. package/dist/cli/{chunk-4D662BWT.js → chunk-4H3ZRJ2U.js} +21 -100
  12. package/dist/cli/chunk-4H3ZRJ2U.js.map +1 -0
  13. package/dist/cli/{chunk-NLV2YORE.js → chunk-4W2CICFQ.js} +62 -11
  14. package/dist/cli/chunk-4W2CICFQ.js.map +1 -0
  15. package/dist/cli/{chunk-SWLIVNTP.js → chunk-65Q5HQ26.js} +122 -1
  16. package/dist/cli/chunk-65Q5HQ26.js.map +1 -0
  17. package/dist/cli/{chunk-XHQIK7B6.js → chunk-7SPOFTMT.js} +2 -2
  18. package/dist/cli/{chunk-6NMWJSES.js → chunk-7VFNPMKG.js} +2 -2
  19. package/dist/cli/{chunk-TPDWAMG6.js → chunk-A3LL4XDV.js} +201 -20
  20. package/dist/cli/chunk-A3LL4XDV.js.map +1 -0
  21. package/dist/cli/chunk-A7VHMMDE.js +413 -0
  22. package/dist/cli/chunk-A7VHMMDE.js.map +1 -0
  23. package/dist/cli/{chunk-DDA76P44.js → chunk-ARF3N2SY.js} +59 -31
  24. package/dist/cli/chunk-ARF3N2SY.js.map +1 -0
  25. package/dist/cli/{code-TTOCA52N.js → chunk-AT6GGIBV.js} +51 -132
  26. package/dist/cli/chunk-AT6GGIBV.js.map +1 -0
  27. package/dist/cli/{chunk-7G3SESEU.js → chunk-BOFL3T45.js} +11 -11
  28. package/dist/cli/chunk-BOFL3T45.js.map +1 -0
  29. package/dist/cli/{chunk-NTVW2TWO.js → chunk-BYZGO3BX.js} +227 -27
  30. package/dist/cli/chunk-BYZGO3BX.js.map +1 -0
  31. package/dist/cli/{chunk-6DR4F3MC.js → chunk-CD4SCQL4.js} +51 -18
  32. package/dist/cli/chunk-CD4SCQL4.js.map +1 -0
  33. package/dist/cli/{chunk-SJNIIH5W.js → chunk-CFY2XLY6.js} +10 -2
  34. package/dist/cli/chunk-CFY2XLY6.js.map +1 -0
  35. package/dist/cli/{chunk-MHDNZXJJ.js → chunk-E46ECXJD.js} +7 -2
  36. package/dist/cli/{chunk-MHDNZXJJ.js.map → chunk-E46ECXJD.js.map} +1 -1
  37. package/dist/cli/{chunk-VM6A6QLY.js → chunk-F2AV2QDK.js} +1445 -1526
  38. package/dist/cli/chunk-F2AV2QDK.js.map +1 -0
  39. package/dist/cli/{chunk-KMWKGPFZ.js → chunk-H4OLWRSX.js} +10 -1
  40. package/dist/cli/chunk-H4OLWRSX.js.map +1 -0
  41. package/dist/cli/{chunk-BTSIAOUG.js → chunk-IEA6JOIP.js} +2463 -2263
  42. package/dist/cli/chunk-IEA6JOIP.js.map +1 -0
  43. package/dist/cli/{chunk-JBBMMYOI.js → chunk-KZYLMMU5.js} +23 -13
  44. package/dist/cli/chunk-KZYLMMU5.js.map +1 -0
  45. package/dist/cli/{chunk-6TJSZ4J2.js → chunk-L7W3HJZQ.js} +2 -2
  46. package/dist/cli/{chunk-MRLXEMZ7.js → chunk-LN27AKV3.js} +1 -1
  47. package/dist/cli/chunk-LN27AKV3.js.map +1 -0
  48. package/dist/cli/chunk-LTXADNCO.js +45 -0
  49. package/dist/cli/chunk-LTXADNCO.js.map +1 -0
  50. package/dist/cli/{chunk-TWJAH4XD.js → chunk-MHGPBJ2T.js} +142 -22
  51. package/dist/cli/chunk-MHGPBJ2T.js.map +1 -0
  52. package/dist/cli/{chunk-V5D77TFD.js → chunk-RAUPWSYA.js} +2 -2
  53. package/dist/cli/chunk-SXLJBFIV.js +245 -0
  54. package/dist/cli/chunk-SXLJBFIV.js.map +1 -0
  55. package/dist/cli/{chunk-SUZRC4NC.js → chunk-UV7XJUJH.js} +2 -2
  56. package/dist/cli/{chunk-XJLZ4HKU.js → chunk-VFG4GIT3.js} +2 -2
  57. package/dist/cli/{chunk-CGX5GIW6.js → chunk-WE3YZULK.js} +2 -2
  58. package/dist/cli/chunk-Y5XNV3NX.js +25 -0
  59. package/dist/cli/chunk-Y5XNV3NX.js.map +1 -0
  60. package/dist/cli/{chunk-6CXT5JRM.js → chunk-YJFKFTAL.js} +8 -1
  61. package/dist/cli/chunk-YJFKFTAL.js.map +1 -0
  62. package/dist/cli/code-X3M6ENTQ.js +150 -0
  63. package/dist/cli/code-X3M6ENTQ.js.map +1 -0
  64. package/dist/cli/{commands-PJMHSP3Z.js → commands-QY7MSQG7.js} +6 -4
  65. package/dist/cli/{commands-PJMHSP3Z.js.map → commands-QY7MSQG7.js.map} +1 -1
  66. package/dist/cli/{commit-R6SC44W5.js → commit-BRCQ3OQO.js} +3 -3
  67. package/dist/cli/desktop-ZTMHQR2Y.js +1026 -0
  68. package/dist/cli/desktop-ZTMHQR2Y.js.map +1 -0
  69. package/dist/cli/{diff-RO2QQBNN.js → diff-YASCB7PU.js} +7 -7
  70. package/dist/cli/{doctor-ISVGUPT2.js → doctor-XCN5ETVP.js} +9 -8
  71. package/dist/cli/{events-SQXPVV7B.js → events-2AJTXR7I.js} +3 -3
  72. package/dist/cli/index.js +132 -38
  73. package/dist/cli/index.js.map +1 -1
  74. package/dist/cli/{mcp-RABKZDX4.js → mcp-YMWBLRR7.js} +2 -2
  75. package/dist/cli/{mcp-browse-H6O73SHN.js → mcp-browse-XLDUE6SB.js} +7 -3
  76. package/dist/cli/mcp-browse-XLDUE6SB.js.map +1 -0
  77. package/dist/cli/{mcp-inspect-XWBO52H6.js → mcp-inspect-H4D2HSJP.js} +39 -4
  78. package/dist/cli/mcp-inspect-H4D2HSJP.js.map +1 -0
  79. package/dist/cli/{prompt-CZSOFYK6.js → prompt-RSIHN62V.js} +4 -3
  80. package/dist/cli/{prune-sessions-FCFOYCBP.js → prune-sessions-4N3BVST2.js} +2 -2
  81. package/dist/cli/{replay-ZDS4TDXB.js → replay-3GTWM75X.js} +8 -8
  82. package/dist/cli/{run-TG7NE73J.js → run-BLZPTRDX.js} +20 -19
  83. package/dist/cli/run-BLZPTRDX.js.map +1 -0
  84. package/dist/cli/{server-2FXGNQ4F.js → server-DRFPXXSH.js} +535 -88
  85. package/dist/cli/server-DRFPXXSH.js.map +1 -0
  86. package/dist/cli/{sessions-XFGZNOOJ.js → sessions-BOWFPTXT.js} +13 -13
  87. package/dist/cli/{setup-EJAMRGKQ.js → setup-FQL2JJC2.js} +6 -6
  88. package/dist/cli/version-XQXYSJ5L.js +30 -0
  89. package/dist/index.d.ts +179 -91
  90. package/dist/index.js +839 -225
  91. package/dist/index.js.map +1 -1
  92. package/package.json +2 -1
  93. package/dist/cli/chat-EVPUW4A4.js +0 -42
  94. package/dist/cli/chunk-4D662BWT.js.map +0 -1
  95. package/dist/cli/chunk-6CXT5JRM.js.map +0 -1
  96. package/dist/cli/chunk-6DR4F3MC.js.map +0 -1
  97. package/dist/cli/chunk-7G3SESEU.js.map +0 -1
  98. package/dist/cli/chunk-BQNUJJN7.js +0 -42
  99. package/dist/cli/chunk-BQNUJJN7.js.map +0 -1
  100. package/dist/cli/chunk-BTSIAOUG.js.map +0 -1
  101. package/dist/cli/chunk-DDA76P44.js.map +0 -1
  102. package/dist/cli/chunk-JBBMMYOI.js.map +0 -1
  103. package/dist/cli/chunk-KMWKGPFZ.js.map +0 -1
  104. package/dist/cli/chunk-MRLXEMZ7.js.map +0 -1
  105. package/dist/cli/chunk-NLV2YORE.js.map +0 -1
  106. package/dist/cli/chunk-NTVW2TWO.js.map +0 -1
  107. package/dist/cli/chunk-SJNIIH5W.js.map +0 -1
  108. package/dist/cli/chunk-SWLIVNTP.js.map +0 -1
  109. package/dist/cli/chunk-TPDWAMG6.js.map +0 -1
  110. package/dist/cli/chunk-TWJAH4XD.js.map +0 -1
  111. package/dist/cli/chunk-VM6A6QLY.js.map +0 -1
  112. package/dist/cli/code-TTOCA52N.js.map +0 -1
  113. package/dist/cli/mcp-browse-H6O73SHN.js.map +0 -1
  114. package/dist/cli/mcp-inspect-XWBO52H6.js.map +0 -1
  115. package/dist/cli/run-TG7NE73J.js.map +0 -1
  116. package/dist/cli/server-2FXGNQ4F.js.map +0 -1
  117. package/dist/cli/version-DPEVFI6I.js +0 -30
  118. /package/dist/cli/{chat-EVPUW4A4.js.map → chat-ZAGX52RV.js.map} +0 -0
  119. /package/dist/cli/{chunk-XHQIK7B6.js.map → chunk-7SPOFTMT.js.map} +0 -0
  120. /package/dist/cli/{chunk-6NMWJSES.js.map → chunk-7VFNPMKG.js.map} +0 -0
  121. /package/dist/cli/{chunk-6TJSZ4J2.js.map → chunk-L7W3HJZQ.js.map} +0 -0
  122. /package/dist/cli/{chunk-V5D77TFD.js.map → chunk-RAUPWSYA.js.map} +0 -0
  123. /package/dist/cli/{chunk-SUZRC4NC.js.map → chunk-UV7XJUJH.js.map} +0 -0
  124. /package/dist/cli/{chunk-XJLZ4HKU.js.map → chunk-VFG4GIT3.js.map} +0 -0
  125. /package/dist/cli/{chunk-CGX5GIW6.js.map → chunk-WE3YZULK.js.map} +0 -0
  126. /package/dist/cli/{commit-R6SC44W5.js.map → commit-BRCQ3OQO.js.map} +0 -0
  127. /package/dist/cli/{diff-RO2QQBNN.js.map → diff-YASCB7PU.js.map} +0 -0
  128. /package/dist/cli/{doctor-ISVGUPT2.js.map → doctor-XCN5ETVP.js.map} +0 -0
  129. /package/dist/cli/{events-SQXPVV7B.js.map → events-2AJTXR7I.js.map} +0 -0
  130. /package/dist/cli/{mcp-RABKZDX4.js.map → mcp-YMWBLRR7.js.map} +0 -0
  131. /package/dist/cli/{prompt-CZSOFYK6.js.map → prompt-RSIHN62V.js.map} +0 -0
  132. /package/dist/cli/{prune-sessions-FCFOYCBP.js.map → prune-sessions-4N3BVST2.js.map} +0 -0
  133. /package/dist/cli/{replay-ZDS4TDXB.js.map → replay-3GTWM75X.js.map} +0 -0
  134. /package/dist/cli/{sessions-XFGZNOOJ.js.map → sessions-BOWFPTXT.js.map} +0 -0
  135. /package/dist/cli/{setup-EJAMRGKQ.js.map → setup-FQL2JJC2.js.map} +0 -0
  136. /package/dist/cli/{version-DPEVFI6I.js.map → version-XQXYSJ5L.js.map} +0 -0
@@ -922,10 +922,10 @@ var require_core = __commonJS({
922
922
  }
923
923
  var version = "11.11.1";
924
924
  var HTMLInjectionError = class extends Error {
925
- constructor(reason, html6) {
925
+ constructor(reason, html8) {
926
926
  super(reason);
927
927
  this.name = "HTMLInjectionError";
928
- this.html = html6;
928
+ this.html = html8;
929
929
  }
930
930
  };
931
931
  var escape3 = escapeHTML;
@@ -959,14 +959,14 @@ var require_core = __commonJS({
959
959
  classes += block2.parentNode ? block2.parentNode.className : "";
960
960
  const match = options2.languageDetectRe.exec(classes);
961
961
  if (match) {
962
- const language = getLanguage(match[1]);
962
+ const language = getLanguage2(match[1]);
963
963
  if (!language) {
964
964
  warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
965
965
  warn("Falling back to no-highlight mode for this block.", block2);
966
966
  }
967
967
  return language ? match[1] : "no-highlight";
968
968
  }
969
- return classes.split(/\s+/).find((_class) => shouldNotHighlight(_class) || getLanguage(_class));
969
+ return classes.split(/\s+/).find((_class) => shouldNotHighlight(_class) || getLanguage2(_class));
970
970
  }
971
971
  function highlight2(codeOrLanguageName, optionsOrCode, ignoreIllegals) {
972
972
  let code = "";
@@ -1244,7 +1244,7 @@ var require_core = __commonJS({
1244
1244
  modeBuffer += lexeme;
1245
1245
  return lexeme.length;
1246
1246
  }
1247
- const language = getLanguage(languageName);
1247
+ const language = getLanguage2(languageName);
1248
1248
  if (!language) {
1249
1249
  error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
1250
1250
  throw new Error('Unknown language: "' + languageName + '"');
@@ -1336,16 +1336,16 @@ var require_core = __commonJS({
1336
1336
  function highlightAuto(code, languageSubset) {
1337
1337
  languageSubset = languageSubset || options2.languages || Object.keys(languages);
1338
1338
  const plaintext = justTextHighlightResult(code);
1339
- const results = languageSubset.filter(getLanguage).filter(autoDetection).map(
1339
+ const results = languageSubset.filter(getLanguage2).filter(autoDetection).map(
1340
1340
  (name) => _highlight(name, code, false)
1341
1341
  );
1342
1342
  results.unshift(plaintext);
1343
1343
  const sorted = results.sort((a3, b2) => {
1344
1344
  if (a3.relevance !== b2.relevance) return b2.relevance - a3.relevance;
1345
1345
  if (a3.language && b2.language) {
1346
- if (getLanguage(a3.language).supersetOf === b2.language) {
1346
+ if (getLanguage2(a3.language).supersetOf === b2.language) {
1347
1347
  return 1;
1348
- } else if (getLanguage(b2.language).supersetOf === a3.language) {
1348
+ } else if (getLanguage2(b2.language).supersetOf === a3.language) {
1349
1349
  return -1;
1350
1350
  }
1351
1351
  }
@@ -1465,7 +1465,7 @@ var require_core = __commonJS({
1465
1465
  function listLanguages() {
1466
1466
  return Object.keys(languages);
1467
1467
  }
1468
- function getLanguage(name) {
1468
+ function getLanguage2(name) {
1469
1469
  name = (name || "").toLowerCase();
1470
1470
  return languages[name] || languages[aliases[name]];
1471
1471
  }
@@ -1478,7 +1478,7 @@ var require_core = __commonJS({
1478
1478
  });
1479
1479
  }
1480
1480
  function autoDetection(name) {
1481
- const lang = getLanguage(name);
1481
+ const lang = getLanguage2(name);
1482
1482
  return lang && !lang.disableAutodetect;
1483
1483
  }
1484
1484
  function upgradePluginAPI(plugin) {
@@ -1533,7 +1533,7 @@ var require_core = __commonJS({
1533
1533
  registerLanguage,
1534
1534
  unregisterLanguage,
1535
1535
  listLanguages,
1536
- getLanguage,
1536
+ getLanguage: getLanguage2,
1537
1537
  registerAliases,
1538
1538
  autoDetection,
1539
1539
  inherit,
@@ -19205,8 +19205,58 @@ var en = {
19205
19205
  tabMemory: "Memory",
19206
19206
  tabHooks: "Hooks",
19207
19207
  tabSettings: "Settings",
19208
+ sectionChanges: "Changes",
19209
+ tabChanges: "Changes",
19208
19210
  footer: "127.0.0.1 only \xB7 token-gated"
19209
19211
  },
19212
+ changes: {
19213
+ chatPlaceholder: "Ask about your code...",
19214
+ chatWelcome: "Changes \u2014 ask questions about your project files.",
19215
+ chatSend: "Send",
19216
+ viewerPlaceholder: "Select a file to view",
19217
+ treeEmpty: "(empty)",
19218
+ tabClose: "Close tab",
19219
+ newConversation: "New",
19220
+ clearConversation: "Clear",
19221
+ newTitle: "/new \u2014 wipe conversation context",
19222
+ clearTitle: "/clear \u2014 wipe visible scrollback",
19223
+ newConfirmBusy: "A turn is in flight. Abort and start a new conversation?",
19224
+ newConfirm: "Clear current conversation and start fresh?",
19225
+ newToast: "new conversation",
19226
+ clearToast: "scrollback cleared",
19227
+ newFailed: "/new failed: {error}",
19228
+ clearFailed: "/clear failed: {error}",
19229
+ chatSendBtn: "Send",
19230
+ fileTreeTitle: "Files",
19231
+ codeViewerTitle: "Code Viewer",
19232
+ chatPanelTitle: "Chat",
19233
+ loadingFiles: "Loading project files\u2026",
19234
+ review: "Review",
19235
+ allFiles: "All Files",
19236
+ changes: "changes",
19237
+ commentLabel: "Commenting on line",
19238
+ commentPlaceholder: "Add a comment\u2026",
19239
+ commentCancel: "Cancel",
19240
+ commentSubmit: "Comment",
19241
+ commentLine: "Line",
19242
+ commentEdit: "Edit",
19243
+ commentDelete: "Delete",
19244
+ diffSourceGit: "Git changes",
19245
+ diffSourceSession: "Previous session",
19246
+ diffSourceCheckpoint: "Checkpoint",
19247
+ checkpointEmpty: "No checkpoints in this workspace yet.",
19248
+ restoreBtn: "Restore",
19249
+ restoreConfirm: 'Restore "{name}"? This will overwrite current files.',
19250
+ deleteBtn: "Delete",
19251
+ deleteConfirm: 'Delete checkpoint "{name}"? Snapshot will be removed, files stay unchanged.',
19252
+ createBtn: "Snapshot",
19253
+ createPlaceholder: "name for snapshot\u2026",
19254
+ backToList: "back to list",
19255
+ diffStyleUnified: "Unified",
19256
+ diffStyleSplit: "Split",
19257
+ expandAll: "Expand all",
19258
+ collapseAll: "Collapse all"
19259
+ },
19210
19260
  common: {
19211
19261
  loading: "loading\u2026",
19212
19262
  loadingFailed: "{name} failed: {error}",
@@ -19786,8 +19836,50 @@ var zhCN = {
19786
19836
  tabMemory: "\u8BB0\u5FC6",
19787
19837
  tabHooks: "\u94A9\u5B50",
19788
19838
  tabSettings: "\u8BBE\u7F6E",
19839
+ sectionChanges: "\u53D8\u66F4",
19840
+ tabChanges: "\u53D8\u66F4",
19789
19841
  footer: "\u4EC5 127.0.0.1 \xB7 Token \u4FDD\u62A4"
19790
19842
  },
19843
+ changes: {
19844
+ chatPlaceholder: "\u8BE2\u95EE\u4EE3\u7801\u95EE\u9898\u2026",
19845
+ chatWelcome: "\u53D8\u66F4 \u2014 \u8BE2\u95EE\u9879\u76EE\u6587\u4EF6\u76F8\u5173\u95EE\u9898\u3002",
19846
+ chatSend: "\u53D1\u9001",
19847
+ viewerPlaceholder: "\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u67E5\u770B",
19848
+ treeEmpty: "\uFF08\u7A7A\uFF09",
19849
+ tabClose: "\u5173\u95ED\u6807\u7B7E",
19850
+ newConversation: "\u65B0\u5EFA",
19851
+ clearConversation: "\u6E05\u9664",
19852
+ newTitle: "/new \u2014 \u6E05\u9664\u5BF9\u8BDD\u4E0A\u4E0B\u6587",
19853
+ clearTitle: "/clear \u2014 \u4EC5\u6E05\u9664\u53EF\u89C1\u7684\u6EDA\u52A8\u56DE\u653E",
19854
+ newConfirmBusy: "\u6709\u8F6E\u6B21\u6B63\u5728\u6267\u884C\u3002\u4E2D\u6B62\u5E76\u5F00\u59CB\u65B0\u5BF9\u8BDD\uFF1F",
19855
+ newConfirm: "\u6E05\u9664\u5F53\u524D\u5BF9\u8BDD\u5E76\u91CD\u65B0\u5F00\u59CB\uFF1F",
19856
+ newToast: "\u65B0\u5BF9\u8BDD",
19857
+ clearToast: "\u6EDA\u52A8\u56DE\u653E\u5DF2\u6E05\u9664",
19858
+ newFailed: "/new \u5931\u8D25\uFF1A{error}",
19859
+ clearFailed: "/clear \u5931\u8D25\uFF1A{error}",
19860
+ chatSendBtn: "\u53D1\u9001",
19861
+ fileTreeTitle: "\u6587\u4EF6",
19862
+ codeViewerTitle: "\u4EE3\u7801\u67E5\u770B\u5668",
19863
+ chatPanelTitle: "\u5BF9\u8BDD",
19864
+ loadingFiles: "\u6B63\u5728\u52A0\u8F7D\u9879\u76EE\u6587\u4EF6\u2026",
19865
+ review: "\u5BA1\u67E5",
19866
+ allFiles: "\u6240\u6709\u6587\u4EF6",
19867
+ changes: "\u66F4\u6539",
19868
+ commentLabel: "\u6B63\u5728\u8BC4\u8BBA \u7B2C",
19869
+ commentPlaceholder: "\u6DFB\u52A0\u8BC4\u8BBA\u2026",
19870
+ commentCancel: "\u53D6\u6D88",
19871
+ commentSubmit: "\u8BC4\u8BBA",
19872
+ commentLine: "\u7B2C",
19873
+ commentEdit: "\u7F16\u8F91",
19874
+ commentDelete: "\u5220\u9664",
19875
+ reviewEmpty: "\u6682\u65E0\u53EF\u5BA1\u67E5\u7684\u66F4\u6539",
19876
+ diffSourceGit: "Git \u53D8\u66F4",
19877
+ diffSourceSession: "\u4E0A\u4E00\u8F6E\u53D8\u66F4",
19878
+ diffStyleUnified: "\u7EDF\u4E00\u89C6\u56FE",
19879
+ diffStyleSplit: "\u5206\u680F\u89C6\u56FE",
19880
+ expandAll: "\u5168\u90E8\u5C55\u5F00",
19881
+ collapseAll: "\u5168\u90E8\u6298\u53E0"
19882
+ },
19791
19883
  common: {
19792
19884
  loading: "\u52A0\u8F7D\u4E2D\u2026",
19793
19885
  loadingFailed: "{name}\u5931\u8D25\uFF1A{error}",
@@ -20349,6 +20441,187 @@ var zhCN = {
20349
20441
  // dashboard/src/i18n/index.ts
20350
20442
  var t4 = createT({ en, "zh-CN": zhCN });
20351
20443
 
20444
+ // node_modules/preact/compat/dist/compat.module.js
20445
+ function g3(n3, t5) {
20446
+ for (var e3 in t5) n3[e3] = t5[e3];
20447
+ return n3;
20448
+ }
20449
+ function E2(n3, t5) {
20450
+ for (var e3 in n3) if ("__source" !== e3 && !(e3 in t5)) return true;
20451
+ for (var r3 in t5) if ("__source" !== r3 && n3[r3] !== t5[r3]) return true;
20452
+ return false;
20453
+ }
20454
+ function M2(n3, t5) {
20455
+ this.props = n3, this.context = t5;
20456
+ }
20457
+ function N2(n3, e3) {
20458
+ function r3(n4) {
20459
+ var t5 = this.props.ref;
20460
+ return t5 != n4.ref && t5 && ("function" == typeof t5 ? t5(null) : t5.current = null), e3 ? !e3(this.props, n4) || t5 != n4.ref : E2(this.props, n4);
20461
+ }
20462
+ function u3(e4) {
20463
+ return this.shouldComponentUpdate = r3, k(n3, e4);
20464
+ }
20465
+ return u3.displayName = "Memo(" + (n3.displayName || n3.name) + ")", u3.__f = u3.prototype.isReactComponent = true, u3.type = n3, u3;
20466
+ }
20467
+ (M2.prototype = new C()).isPureReactComponent = true, M2.prototype.shouldComponentUpdate = function(n3, t5) {
20468
+ return E2(this.props, n3) || E2(this.state, t5);
20469
+ };
20470
+ var T3 = l.__b;
20471
+ l.__b = function(n3) {
20472
+ n3.type && n3.type.__f && n3.ref && (n3.props.ref = n3.ref, n3.ref = null), T3 && T3(n3);
20473
+ };
20474
+ var A3 = "undefined" != typeof Symbol && Symbol.for && /* @__PURE__ */ Symbol.for("react.forward_ref") || 3911;
20475
+ var O2 = l.__e;
20476
+ l.__e = function(n3, t5, e3, r3) {
20477
+ if (n3.then) {
20478
+ for (var u3, o3 = t5; o3 = o3.__; ) if ((u3 = o3.__c) && u3.__c) return null == t5.__e && (t5.__e = e3.__e, t5.__k = e3.__k), u3.__c(n3, t5);
20479
+ }
20480
+ O2(n3, t5, e3, r3);
20481
+ };
20482
+ var U2 = l.unmount;
20483
+ function V2(n3, t5, e3) {
20484
+ return n3 && (n3.__c && n3.__c.__H && (n3.__c.__H.__.forEach(function(n4) {
20485
+ "function" == typeof n4.__c && n4.__c();
20486
+ }), n3.__c.__H = null), null != (n3 = g3({}, n3)).__c && (n3.__c.__P === e3 && (n3.__c.__P = t5), n3.__c.__e = true, n3.__c = null), n3.__k = n3.__k && n3.__k.map(function(n4) {
20487
+ return V2(n4, t5, e3);
20488
+ })), n3;
20489
+ }
20490
+ function W2(n3, t5, e3) {
20491
+ return n3 && e3 && (n3.__v = null, n3.__k = n3.__k && n3.__k.map(function(n4) {
20492
+ return W2(n4, t5, e3);
20493
+ }), n3.__c && n3.__c.__P === t5 && (n3.__e && e3.appendChild(n3.__e), n3.__c.__e = true, n3.__c.__P = e3)), n3;
20494
+ }
20495
+ function P3() {
20496
+ this.__u = 0, this.o = null, this.__b = null;
20497
+ }
20498
+ function j3(n3) {
20499
+ var t5 = n3.__ && n3.__.__c;
20500
+ return t5 && t5.__a && t5.__a(n3);
20501
+ }
20502
+ function B3() {
20503
+ this.i = null, this.l = null;
20504
+ }
20505
+ l.unmount = function(n3) {
20506
+ var t5 = n3.__c;
20507
+ t5 && (t5.__z = true), t5 && t5.__R && t5.__R(), t5 && 32 & n3.__u && (n3.type = null), U2 && U2(n3);
20508
+ }, (P3.prototype = new C()).__c = function(n3, t5) {
20509
+ var e3 = t5.__c, r3 = this;
20510
+ null == r3.o && (r3.o = []), r3.o.push(e3);
20511
+ var u3 = j3(r3.__v), o3 = false, i3 = function() {
20512
+ o3 || r3.__z || (o3 = true, e3.__R = null, u3 ? u3(c3) : c3());
20513
+ };
20514
+ e3.__R = i3;
20515
+ var l3 = e3.__P;
20516
+ e3.__P = null;
20517
+ var c3 = function() {
20518
+ if (!--r3.__u) {
20519
+ if (r3.state.__a) {
20520
+ var n4 = r3.state.__a;
20521
+ r3.__v.__k[0] = W2(n4, n4.__c.__P, n4.__c.__O);
20522
+ }
20523
+ var t6;
20524
+ for (r3.setState({ __a: r3.__b = null }); t6 = r3.o.pop(); ) t6.__P = l3, t6.forceUpdate();
20525
+ }
20526
+ };
20527
+ r3.__u++ || 32 & t5.__u || r3.setState({ __a: r3.__b = r3.__v.__k[0] }), n3.then(i3, i3);
20528
+ }, P3.prototype.componentWillUnmount = function() {
20529
+ this.o = [];
20530
+ }, P3.prototype.render = function(n3, e3) {
20531
+ if (this.__b) {
20532
+ if (this.__v.__k) {
20533
+ var r3 = document.createElement("div"), o3 = this.__v.__k[0].__c;
20534
+ this.__v.__k[0] = V2(this.__b, r3, o3.__O = o3.__P);
20535
+ }
20536
+ this.__b = null;
20537
+ }
20538
+ var i3 = e3.__a && k(S, null, n3.fallback);
20539
+ return i3 && (i3.__u &= -33), [k(S, null, e3.__a ? null : n3.children), i3];
20540
+ };
20541
+ var H2 = function(n3, t5, e3) {
20542
+ if (++e3[1] === e3[0] && n3.l.delete(t5), n3.props.revealOrder && ("t" !== n3.props.revealOrder[0] || !n3.l.size)) for (e3 = n3.i; e3; ) {
20543
+ for (; e3.length > 3; ) e3.pop()();
20544
+ if (e3[1] < e3[0]) break;
20545
+ n3.i = e3 = e3[2];
20546
+ }
20547
+ };
20548
+ (B3.prototype = new C()).__a = function(n3) {
20549
+ var t5 = this, e3 = j3(t5.__v), r3 = t5.l.get(n3);
20550
+ return r3[0]++, function(u3) {
20551
+ var o3 = function() {
20552
+ t5.props.revealOrder ? (r3.push(u3), H2(t5, n3, r3)) : u3();
20553
+ };
20554
+ e3 ? e3(o3) : o3();
20555
+ };
20556
+ }, B3.prototype.render = function(n3) {
20557
+ this.i = null, this.l = /* @__PURE__ */ new Map();
20558
+ var t5 = F(n3.children);
20559
+ n3.revealOrder && "b" === n3.revealOrder[0] && t5.reverse();
20560
+ for (var e3 = t5.length; e3--; ) this.l.set(t5[e3], this.i = [1, 0, this.i]);
20561
+ return n3.children;
20562
+ }, B3.prototype.componentDidUpdate = B3.prototype.componentDidMount = function() {
20563
+ var n3 = this;
20564
+ this.l.forEach(function(t5, e3) {
20565
+ H2(n3, e3, t5);
20566
+ });
20567
+ };
20568
+ var q3 = "undefined" != typeof Symbol && Symbol.for && /* @__PURE__ */ Symbol.for("react.element") || 60103;
20569
+ var G2 = /^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/;
20570
+ var J2 = /^on(Ani|Tra|Tou|BeforeInp|Compo)/;
20571
+ var K2 = /[A-Z0-9]/g;
20572
+ var Q2 = "undefined" != typeof document;
20573
+ var X2 = function(n3) {
20574
+ return ("undefined" != typeof Symbol && "symbol" == typeof /* @__PURE__ */ Symbol() ? /fil|che|rad/ : /fil|che|ra/).test(n3);
20575
+ };
20576
+ C.prototype.isReactComponent = true, ["componentWillMount", "componentWillReceiveProps", "componentWillUpdate"].forEach(function(t5) {
20577
+ Object.defineProperty(C.prototype, t5, { configurable: true, get: function() {
20578
+ return this["UNSAFE_" + t5];
20579
+ }, set: function(n3) {
20580
+ Object.defineProperty(this, t5, { configurable: true, writable: true, value: n3 });
20581
+ } });
20582
+ });
20583
+ var en2 = l.event;
20584
+ l.event = function(n3) {
20585
+ return en2 && (n3 = en2(n3)), n3.persist = function() {
20586
+ }, n3.isPropagationStopped = function() {
20587
+ return this.cancelBubble;
20588
+ }, n3.isDefaultPrevented = function() {
20589
+ return this.defaultPrevented;
20590
+ }, n3.nativeEvent = n3;
20591
+ };
20592
+ var rn;
20593
+ var un = { configurable: true, get: function() {
20594
+ return this.class;
20595
+ } };
20596
+ var on = l.vnode;
20597
+ l.vnode = function(n3) {
20598
+ "string" == typeof n3.type && (function(n4) {
20599
+ var t5 = n4.props, e3 = n4.type, u3 = {}, o3 = -1 == e3.indexOf("-");
20600
+ for (var i3 in t5) {
20601
+ var l3 = t5[i3];
20602
+ if (!("value" === i3 && "defaultValue" in t5 && null == l3 || Q2 && "children" === i3 && "noscript" === e3 || "class" === i3 || "className" === i3)) {
20603
+ var c3 = i3.toLowerCase();
20604
+ "defaultValue" === i3 && "value" in t5 && null == t5.value ? i3 = "value" : "download" === i3 && true === l3 ? l3 = "" : "translate" === c3 && "no" === l3 ? l3 = false : "o" === c3[0] && "n" === c3[1] ? "ondoubleclick" === c3 ? i3 = "ondblclick" : "onchange" !== c3 || "input" !== e3 && "textarea" !== e3 || X2(t5.type) ? "onfocus" === c3 ? i3 = "onfocusin" : "onblur" === c3 ? i3 = "onfocusout" : J2.test(i3) && (i3 = c3) : c3 = i3 = "oninput" : o3 && G2.test(i3) ? i3 = i3.replace(K2, "-$&").toLowerCase() : null === l3 && (l3 = void 0), "oninput" === c3 && u3[i3 = c3] && (i3 = "oninputCapture"), u3[i3] = l3;
20605
+ }
20606
+ }
20607
+ "select" == e3 && (u3.multiple && Array.isArray(u3.value) && (u3.value = F(t5.children).forEach(function(n5) {
20608
+ n5.props.selected = -1 != u3.value.indexOf(n5.props.value);
20609
+ })), null != u3.defaultValue && (u3.value = F(t5.children).forEach(function(n5) {
20610
+ n5.props.selected = u3.multiple ? -1 != u3.defaultValue.indexOf(n5.props.value) : u3.defaultValue == n5.props.value;
20611
+ }))), t5.class && !t5.className ? (u3.class = t5.class, Object.defineProperty(u3, "className", un)) : t5.className && (u3.class = u3.className = t5.className), n4.props = u3;
20612
+ })(n3), n3.$$typeof = q3, on && on(n3);
20613
+ };
20614
+ var ln = l.__r;
20615
+ l.__r = function(n3) {
20616
+ ln && ln(n3), rn = n3.__c;
20617
+ };
20618
+ var cn = l.diffed;
20619
+ l.diffed = function(n3) {
20620
+ cn && cn(n3);
20621
+ var t5 = n3.props, e3 = n3.__e;
20622
+ null != e3 && "textarea" === n3.type && "value" in t5 && t5.value !== e3.value && (e3.value = null == t5.value ? "" : t5.value), rn = null;
20623
+ };
20624
+
20352
20625
  // node_modules/marked/lib/marked.esm.js
20353
20626
  function _getDefaults() {
20354
20627
  return {
@@ -22485,233 +22758,52 @@ var parseInline = marked.parseInline;
22485
22758
  var parser = _Parser.parse;
22486
22759
  var lexer = _Lexer.lex;
22487
22760
 
22488
- // node_modules/preact/compat/dist/compat.module.js
22489
- function g3(n3, t5) {
22490
- for (var e3 in t5) n3[e3] = t5[e3];
22491
- return n3;
22761
+ // dashboard/src/lib/html.ts
22762
+ var html4 = htm_module_default.bind(k);
22763
+
22764
+ // node_modules/highlight.js/es/common.js
22765
+ var import_common = __toESM(require_common(), 1);
22766
+ var common_default = import_common.default;
22767
+
22768
+ // dashboard/src/lib/markdown.ts
22769
+ function escapeHtml(s3) {
22770
+ if (s3 == null) return "";
22771
+ return String(s3).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
22492
22772
  }
22493
- function E2(n3, t5) {
22494
- for (var e3 in n3) if ("__source" !== e3 && !(e3 in t5)) return true;
22495
- for (var r3 in t5) if ("__source" !== r3 && n3[r3] !== t5[r3]) return true;
22496
- return false;
22773
+ var SEARCH_REPLACE_RE = /<{7}\s*SEARCH\s*\n([\s\S]*?)\n={7}\s*\n([\s\S]*?)\n>{7}\s*REPLACE/;
22774
+ function renderSearchReplace(search, replace, file) {
22775
+ const safeSearch = typeof search === "string" ? search : String(search ?? "");
22776
+ const safeReplace = typeof replace === "string" ? replace : String(replace ?? "");
22777
+ const oldLines = safeSearch.split("\n").map((l3) => `<span class="diff-line del">- ${escapeHtml(l3)}</span>`).join("\n");
22778
+ const newLines = safeReplace.split("\n").map((l3) => `<span class="diff-line ins">+ ${escapeHtml(l3)}</span>`).join("\n");
22779
+ const header = file ? `<span class="diff-line hunk">\u25B8 edit ${escapeHtml(file)}</span>
22780
+ ` : "";
22781
+ return `<pre class="diff-block">${header}${oldLines}
22782
+ ${newLines}</pre>`;
22497
22783
  }
22498
- function M2(n3, t5) {
22499
- this.props = n3, this.context = t5;
22784
+ function renderUnifiedDiff(text) {
22785
+ const safe = typeof text === "string" ? text : String(text ?? "");
22786
+ const lines = safe.split("\n").map((l3) => {
22787
+ if (l3.startsWith("+++") || l3.startsWith("---")) {
22788
+ return `<span class="diff-line meta">${escapeHtml(l3)}</span>`;
22789
+ }
22790
+ if (l3.startsWith("+")) return `<span class="diff-line ins">${escapeHtml(l3)}</span>`;
22791
+ if (l3.startsWith("-")) return `<span class="diff-line del">${escapeHtml(l3)}</span>`;
22792
+ if (l3.startsWith("@@")) return `<span class="diff-line hunk">${escapeHtml(l3)}</span>`;
22793
+ return escapeHtml(l3);
22794
+ }).join("\n");
22795
+ return `<pre class="diff-block">${lines}</pre>`;
22500
22796
  }
22501
- function N2(n3, e3) {
22502
- function r3(n4) {
22503
- var t5 = this.props.ref;
22504
- return t5 != n4.ref && t5 && ("function" == typeof t5 ? t5(null) : t5.current = null), e3 ? !e3(this.props, n4) || t5 != n4.ref : E2(this.props, n4);
22505
- }
22506
- function u3(e4) {
22507
- return this.shouldComponentUpdate = r3, k(n3, e4);
22508
- }
22509
- return u3.displayName = "Memo(" + (n3.displayName || n3.name) + ")", u3.__f = u3.prototype.isReactComponent = true, u3.type = n3, u3;
22510
- }
22511
- (M2.prototype = new C()).isPureReactComponent = true, M2.prototype.shouldComponentUpdate = function(n3, t5) {
22512
- return E2(this.props, n3) || E2(this.state, t5);
22513
- };
22514
- var T3 = l.__b;
22515
- l.__b = function(n3) {
22516
- n3.type && n3.type.__f && n3.ref && (n3.props.ref = n3.ref, n3.ref = null), T3 && T3(n3);
22517
- };
22518
- var A3 = "undefined" != typeof Symbol && Symbol.for && /* @__PURE__ */ Symbol.for("react.forward_ref") || 3911;
22519
- var O2 = l.__e;
22520
- l.__e = function(n3, t5, e3, r3) {
22521
- if (n3.then) {
22522
- for (var u3, o3 = t5; o3 = o3.__; ) if ((u3 = o3.__c) && u3.__c) return null == t5.__e && (t5.__e = e3.__e, t5.__k = e3.__k), u3.__c(n3, t5);
22523
- }
22524
- O2(n3, t5, e3, r3);
22525
- };
22526
- var U2 = l.unmount;
22527
- function V2(n3, t5, e3) {
22528
- return n3 && (n3.__c && n3.__c.__H && (n3.__c.__H.__.forEach(function(n4) {
22529
- "function" == typeof n4.__c && n4.__c();
22530
- }), n3.__c.__H = null), null != (n3 = g3({}, n3)).__c && (n3.__c.__P === e3 && (n3.__c.__P = t5), n3.__c.__e = true, n3.__c = null), n3.__k = n3.__k && n3.__k.map(function(n4) {
22531
- return V2(n4, t5, e3);
22532
- })), n3;
22533
- }
22534
- function W2(n3, t5, e3) {
22535
- return n3 && e3 && (n3.__v = null, n3.__k = n3.__k && n3.__k.map(function(n4) {
22536
- return W2(n4, t5, e3);
22537
- }), n3.__c && n3.__c.__P === t5 && (n3.__e && e3.appendChild(n3.__e), n3.__c.__e = true, n3.__c.__P = e3)), n3;
22538
- }
22539
- function P3() {
22540
- this.__u = 0, this.o = null, this.__b = null;
22541
- }
22542
- function j3(n3) {
22543
- var t5 = n3.__ && n3.__.__c;
22544
- return t5 && t5.__a && t5.__a(n3);
22545
- }
22546
- function B3() {
22547
- this.i = null, this.l = null;
22548
- }
22549
- l.unmount = function(n3) {
22550
- var t5 = n3.__c;
22551
- t5 && (t5.__z = true), t5 && t5.__R && t5.__R(), t5 && 32 & n3.__u && (n3.type = null), U2 && U2(n3);
22552
- }, (P3.prototype = new C()).__c = function(n3, t5) {
22553
- var e3 = t5.__c, r3 = this;
22554
- null == r3.o && (r3.o = []), r3.o.push(e3);
22555
- var u3 = j3(r3.__v), o3 = false, i3 = function() {
22556
- o3 || r3.__z || (o3 = true, e3.__R = null, u3 ? u3(c3) : c3());
22557
- };
22558
- e3.__R = i3;
22559
- var l3 = e3.__P;
22560
- e3.__P = null;
22561
- var c3 = function() {
22562
- if (!--r3.__u) {
22563
- if (r3.state.__a) {
22564
- var n4 = r3.state.__a;
22565
- r3.__v.__k[0] = W2(n4, n4.__c.__P, n4.__c.__O);
22566
- }
22567
- var t6;
22568
- for (r3.setState({ __a: r3.__b = null }); t6 = r3.o.pop(); ) t6.__P = l3, t6.forceUpdate();
22569
- }
22570
- };
22571
- r3.__u++ || 32 & t5.__u || r3.setState({ __a: r3.__b = r3.__v.__k[0] }), n3.then(i3, i3);
22572
- }, P3.prototype.componentWillUnmount = function() {
22573
- this.o = [];
22574
- }, P3.prototype.render = function(n3, e3) {
22575
- if (this.__b) {
22576
- if (this.__v.__k) {
22577
- var r3 = document.createElement("div"), o3 = this.__v.__k[0].__c;
22578
- this.__v.__k[0] = V2(this.__b, r3, o3.__O = o3.__P);
22579
- }
22580
- this.__b = null;
22581
- }
22582
- var i3 = e3.__a && k(S, null, n3.fallback);
22583
- return i3 && (i3.__u &= -33), [k(S, null, e3.__a ? null : n3.children), i3];
22584
- };
22585
- var H2 = function(n3, t5, e3) {
22586
- if (++e3[1] === e3[0] && n3.l.delete(t5), n3.props.revealOrder && ("t" !== n3.props.revealOrder[0] || !n3.l.size)) for (e3 = n3.i; e3; ) {
22587
- for (; e3.length > 3; ) e3.pop()();
22588
- if (e3[1] < e3[0]) break;
22589
- n3.i = e3 = e3[2];
22590
- }
22591
- };
22592
- (B3.prototype = new C()).__a = function(n3) {
22593
- var t5 = this, e3 = j3(t5.__v), r3 = t5.l.get(n3);
22594
- return r3[0]++, function(u3) {
22595
- var o3 = function() {
22596
- t5.props.revealOrder ? (r3.push(u3), H2(t5, n3, r3)) : u3();
22597
- };
22598
- e3 ? e3(o3) : o3();
22599
- };
22600
- }, B3.prototype.render = function(n3) {
22601
- this.i = null, this.l = /* @__PURE__ */ new Map();
22602
- var t5 = F(n3.children);
22603
- n3.revealOrder && "b" === n3.revealOrder[0] && t5.reverse();
22604
- for (var e3 = t5.length; e3--; ) this.l.set(t5[e3], this.i = [1, 0, this.i]);
22605
- return n3.children;
22606
- }, B3.prototype.componentDidUpdate = B3.prototype.componentDidMount = function() {
22607
- var n3 = this;
22608
- this.l.forEach(function(t5, e3) {
22609
- H2(n3, e3, t5);
22610
- });
22611
- };
22612
- var q3 = "undefined" != typeof Symbol && Symbol.for && /* @__PURE__ */ Symbol.for("react.element") || 60103;
22613
- var G2 = /^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/;
22614
- var J2 = /^on(Ani|Tra|Tou|BeforeInp|Compo)/;
22615
- var K2 = /[A-Z0-9]/g;
22616
- var Q2 = "undefined" != typeof document;
22617
- var X2 = function(n3) {
22618
- return ("undefined" != typeof Symbol && "symbol" == typeof /* @__PURE__ */ Symbol() ? /fil|che|rad/ : /fil|che|ra/).test(n3);
22619
- };
22620
- C.prototype.isReactComponent = true, ["componentWillMount", "componentWillReceiveProps", "componentWillUpdate"].forEach(function(t5) {
22621
- Object.defineProperty(C.prototype, t5, { configurable: true, get: function() {
22622
- return this["UNSAFE_" + t5];
22623
- }, set: function(n3) {
22624
- Object.defineProperty(this, t5, { configurable: true, writable: true, value: n3 });
22625
- } });
22626
- });
22627
- var en2 = l.event;
22628
- l.event = function(n3) {
22629
- return en2 && (n3 = en2(n3)), n3.persist = function() {
22630
- }, n3.isPropagationStopped = function() {
22631
- return this.cancelBubble;
22632
- }, n3.isDefaultPrevented = function() {
22633
- return this.defaultPrevented;
22634
- }, n3.nativeEvent = n3;
22635
- };
22636
- var rn;
22637
- var un = { configurable: true, get: function() {
22638
- return this.class;
22639
- } };
22640
- var on = l.vnode;
22641
- l.vnode = function(n3) {
22642
- "string" == typeof n3.type && (function(n4) {
22643
- var t5 = n4.props, e3 = n4.type, u3 = {}, o3 = -1 == e3.indexOf("-");
22644
- for (var i3 in t5) {
22645
- var l3 = t5[i3];
22646
- if (!("value" === i3 && "defaultValue" in t5 && null == l3 || Q2 && "children" === i3 && "noscript" === e3 || "class" === i3 || "className" === i3)) {
22647
- var c3 = i3.toLowerCase();
22648
- "defaultValue" === i3 && "value" in t5 && null == t5.value ? i3 = "value" : "download" === i3 && true === l3 ? l3 = "" : "translate" === c3 && "no" === l3 ? l3 = false : "o" === c3[0] && "n" === c3[1] ? "ondoubleclick" === c3 ? i3 = "ondblclick" : "onchange" !== c3 || "input" !== e3 && "textarea" !== e3 || X2(t5.type) ? "onfocus" === c3 ? i3 = "onfocusin" : "onblur" === c3 ? i3 = "onfocusout" : J2.test(i3) && (i3 = c3) : c3 = i3 = "oninput" : o3 && G2.test(i3) ? i3 = i3.replace(K2, "-$&").toLowerCase() : null === l3 && (l3 = void 0), "oninput" === c3 && u3[i3 = c3] && (i3 = "oninputCapture"), u3[i3] = l3;
22649
- }
22650
- }
22651
- "select" == e3 && (u3.multiple && Array.isArray(u3.value) && (u3.value = F(t5.children).forEach(function(n5) {
22652
- n5.props.selected = -1 != u3.value.indexOf(n5.props.value);
22653
- })), null != u3.defaultValue && (u3.value = F(t5.children).forEach(function(n5) {
22654
- n5.props.selected = u3.multiple ? -1 != u3.defaultValue.indexOf(n5.props.value) : u3.defaultValue == n5.props.value;
22655
- }))), t5.class && !t5.className ? (u3.class = t5.class, Object.defineProperty(u3, "className", un)) : t5.className && (u3.class = u3.className = t5.className), n4.props = u3;
22656
- })(n3), n3.$$typeof = q3, on && on(n3);
22657
- };
22658
- var ln = l.__r;
22659
- l.__r = function(n3) {
22660
- ln && ln(n3), rn = n3.__c;
22661
- };
22662
- var cn = l.diffed;
22663
- l.diffed = function(n3) {
22664
- cn && cn(n3);
22665
- var t5 = n3.props, e3 = n3.__e;
22666
- null != e3 && "textarea" === n3.type && "value" in t5 && t5.value !== e3.value && (e3.value = null == t5.value ? "" : t5.value), rn = null;
22667
- };
22668
-
22669
- // dashboard/src/lib/html.ts
22670
- var html4 = htm_module_default.bind(k);
22671
-
22672
- // node_modules/highlight.js/es/common.js
22673
- var import_common = __toESM(require_common(), 1);
22674
- var common_default = import_common.default;
22675
-
22676
- // dashboard/src/lib/markdown.ts
22677
- function escapeHtml(s3) {
22678
- if (s3 == null) return "";
22679
- return String(s3).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
22680
- }
22681
- var SEARCH_REPLACE_RE = /<{7}\s*SEARCH\s*\n([\s\S]*?)\n={7}\s*\n([\s\S]*?)\n>{7}\s*REPLACE/;
22682
- function renderSearchReplace(search, replace, file) {
22683
- const safeSearch = typeof search === "string" ? search : String(search ?? "");
22684
- const safeReplace = typeof replace === "string" ? replace : String(replace ?? "");
22685
- const oldLines = safeSearch.split("\n").map((l3) => `<span class="diff-line del">- ${escapeHtml(l3)}</span>`).join("\n");
22686
- const newLines = safeReplace.split("\n").map((l3) => `<span class="diff-line ins">+ ${escapeHtml(l3)}</span>`).join("\n");
22687
- const header = file ? `<span class="diff-line hunk">\u25B8 edit ${escapeHtml(file)}</span>
22688
- ` : "";
22689
- return `<pre class="diff-block">${header}${oldLines}
22690
- ${newLines}</pre>`;
22691
- }
22692
- function renderUnifiedDiff(text) {
22693
- const safe = typeof text === "string" ? text : String(text ?? "");
22694
- const lines = safe.split("\n").map((l3) => {
22695
- if (l3.startsWith("+++") || l3.startsWith("---")) {
22696
- return `<span class="diff-line meta">${escapeHtml(l3)}</span>`;
22697
- }
22698
- if (l3.startsWith("+")) return `<span class="diff-line ins">${escapeHtml(l3)}</span>`;
22699
- if (l3.startsWith("-")) return `<span class="diff-line del">${escapeHtml(l3)}</span>`;
22700
- if (l3.startsWith("@@")) return `<span class="diff-line hunk">${escapeHtml(l3)}</span>`;
22701
- return escapeHtml(l3);
22702
- }).join("\n");
22703
- return `<pre class="diff-block">${lines}</pre>`;
22704
- }
22705
- var renderer = new marked.Renderer();
22706
- renderer.code = function reasonixCode(arg1, arg2) {
22707
- let text;
22708
- let lang;
22709
- if (arg1 && typeof arg1 === "object" && !Array.isArray(arg1)) {
22710
- text = arg1.text;
22711
- lang = arg1.lang;
22712
- } else {
22713
- text = arg1;
22714
- lang = arg2;
22797
+ var renderer = new marked.Renderer();
22798
+ renderer.code = function reasonixCode(arg1, arg2) {
22799
+ let text;
22800
+ let lang;
22801
+ if (arg1 && typeof arg1 === "object" && !Array.isArray(arg1)) {
22802
+ text = arg1.text;
22803
+ lang = arg1.lang;
22804
+ } else {
22805
+ text = arg1;
22806
+ lang = arg2;
22715
22807
  }
22716
22808
  if (text == null) text = "";
22717
22809
  const codeText = typeof text === "string" ? text : String(text);
@@ -23862,15 +23954,6 @@ function ChatPanel() {
23862
23954
  autoScrollInFlight.current = false;
23863
23955
  }, 0);
23864
23956
  }, [messages, streaming]);
23865
- const allMessages = streaming ? [
23866
- ...messages,
23867
- {
23868
- id: streaming.id,
23869
- role: "assistant",
23870
- text: streaming.text,
23871
- reasoning: streaming.reasoning
23872
- }
23873
- ] : messages;
23874
23957
  const resolveModal = q2(async (kind, choice, text) => {
23875
23958
  try {
23876
23959
  await api("/modal/resolve", {
@@ -24015,19 +24098,7 @@ function ChatPanel() {
24015
24098
 
24016
24099
  <div class="chat-body">
24017
24100
  <div class="chat-main">
24018
- <div class="chat-feed" ref=${feedRef}>
24019
- ${allMessages.length === 0 ? html4`<div class="chat-empty">
24020
- ${t4("chat.noConversation")}
24021
- </div>` : allMessages.map(
24022
- (m3) => html4`
24023
- <${ChatMessage}
24024
- key=${m3.id}
24025
- msg=${m3}
24026
- streaming=${Boolean(streaming && streaming.id === m3.id)}
24027
- />
24028
- `
24029
- )}
24030
- </div>
24101
+ <${ChatFeed} messages=${messages} streaming=${streaming} innerRef=${feedRef} />
24031
24102
 
24032
24103
  <div class="chat-input-area" style="position:relative">
24033
24104
  ${popoverKind && popoverItems.length > 0 ? html4`
@@ -24089,7 +24160,32 @@ function ChatPanel() {
24089
24160
  </div>
24090
24161
  `;
24091
24162
  }
24092
- function SideRail({ stats, budgetUsd, activePlan }) {
24163
+ var ChatFeed = N2(function ChatFeed2({ messages, streaming, innerRef }) {
24164
+ useLang();
24165
+ const allMessages = streaming ? [
24166
+ ...messages,
24167
+ {
24168
+ id: streaming.id,
24169
+ role: "assistant",
24170
+ text: streaming.text,
24171
+ reasoning: streaming.reasoning
24172
+ }
24173
+ ] : messages;
24174
+ return html4`
24175
+ <div class="chat-feed" ref=${innerRef}>
24176
+ ${allMessages.length === 0 ? html4`<div class="chat-empty">${t4("chat.noConversation")}</div>` : allMessages.map(
24177
+ (m3) => html4`
24178
+ <${ChatMessage}
24179
+ key=${m3.id}
24180
+ msg=${m3}
24181
+ streaming=${Boolean(streaming && streaming.id === m3.id)}
24182
+ />
24183
+ `
24184
+ )}
24185
+ </div>
24186
+ `;
24187
+ });
24188
+ var SideRail = N2(function SideRail2({ stats, budgetUsd, activePlan }) {
24093
24189
  useLang();
24094
24190
  if (!stats && !activePlan) return html4`<aside class="chat-rail"></aside>`;
24095
24191
  const cachePct = stats ? stats.cacheHitRatio * 100 : 0;
@@ -24126,7 +24222,7 @@ function SideRail({ stats, budgetUsd, activePlan }) {
24126
24222
  ` : null}
24127
24223
  </aside>
24128
24224
  `;
24129
- }
24225
+ });
24130
24226
  function ActivePlanCard({ plan }) {
24131
24227
  useLang();
24132
24228
  const dots = [];
@@ -24212,7 +24308,7 @@ function InFlightRow({
24212
24308
  </div>
24213
24309
  `;
24214
24310
  }
24215
- function ChatStatusBar({ stats, model }) {
24311
+ var ChatStatusBar = N2(function ChatStatusBar2({ stats, model }) {
24216
24312
  useLang();
24217
24313
  if (!stats) {
24218
24314
  return html4`
@@ -24261,7 +24357,7 @@ function ChatStatusBar({ stats, model }) {
24261
24357
  ` : null}
24262
24358
  </div>
24263
24359
  `;
24264
- }
24360
+ });
24265
24361
 
24266
24362
  // dashboard/src/panels/hooks.ts
24267
24363
  function buildMatrix(data) {
@@ -27767,37 +27863,1791 @@ function UsagePanel() {
27767
27863
  `;
27768
27864
  }
27769
27865
 
27770
- // dashboard/app.js
27866
+ // dashboard/src/lib/file-tree.ts
27771
27867
  var html5 = htm_module_default.bind(k);
27772
- function tabSections() {
27773
- return [
27774
- {
27775
- label: t4("app.sectionWorkspace"),
27868
+ var EXT_ICONS = {
27869
+ ts: "TS",
27870
+ tsx: "TS",
27871
+ js: "JS",
27872
+ jsx: "JS",
27873
+ json: "{}",
27874
+ css: "#",
27875
+ scss: "#",
27876
+ html: "<>",
27877
+ md: "MD",
27878
+ py: "PY",
27879
+ rs: "RS",
27880
+ go: "GO",
27881
+ yaml: "Y",
27882
+ yml: "Y",
27883
+ toml: "T",
27884
+ xml: "<>",
27885
+ svg: "<>",
27886
+ png: "[]",
27887
+ jpg: "[]",
27888
+ ico: "[]",
27889
+ sh: "$",
27890
+ bash: "$",
27891
+ ps1: "$",
27892
+ bat: "$",
27893
+ sql: "DB",
27894
+ graphql: "GQ",
27895
+ proto: "PB",
27896
+ dockerfile: "D",
27897
+ makefile: "MK"
27898
+ };
27899
+ var EXT_LANG = {
27900
+ ts: "typescript",
27901
+ tsx: "typescript",
27902
+ js: "javascript",
27903
+ jsx: "javascript",
27904
+ json: "json",
27905
+ css: "css",
27906
+ scss: "scss",
27907
+ html: "html",
27908
+ md: "markdown",
27909
+ py: "python",
27910
+ rs: "rust",
27911
+ go: "go",
27912
+ yaml: "yaml",
27913
+ yml: "yaml",
27914
+ toml: "toml",
27915
+ xml: "xml",
27916
+ sh: "bash",
27917
+ bash: "bash",
27918
+ ps1: "powershell",
27919
+ bat: "batch",
27920
+ sql: "sql",
27921
+ graphql: "graphql",
27922
+ proto: "protobuf",
27923
+ dockerfile: "dockerfile",
27924
+ makefile: "makefile"
27925
+ };
27926
+ function getFileIcon(name) {
27927
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
27928
+ const icon = EXT_ICONS[ext] ?? "\xB7";
27929
+ const cls = ext || "file";
27930
+ return { icon, cls };
27931
+ }
27932
+ function getLanguage(name) {
27933
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
27934
+ return EXT_LANG[ext] ?? ext;
27935
+ }
27936
+ function isBinaryExt(name) {
27937
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
27938
+ const binary = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "ico", "svg", "woff", "woff2", "ttf", "eot", "mp4", "webm", "mp3", "wav", "zip", "tar", "gz", "7z", "pdf"]);
27939
+ return binary.has(ext);
27940
+ }
27941
+ function useProjectTree() {
27942
+ const [tree, setTree] = d2([]);
27943
+ const [loading, setLoading] = d2(true);
27944
+ const [error, setError] = d2(null);
27945
+ y2(() => {
27946
+ if (MODE === "standalone") {
27947
+ setLoading(false);
27948
+ setTree(createDemoTree());
27949
+ return;
27950
+ }
27951
+ let cancelled = false;
27952
+ api("/project-tree").then((r3) => {
27953
+ if (!cancelled) {
27954
+ setTree(r3.tree);
27955
+ setLoading(false);
27956
+ }
27957
+ }).catch((err) => {
27958
+ if (!cancelled) {
27959
+ setError(err.message);
27960
+ setLoading(false);
27961
+ setTree(createDemoTree());
27962
+ }
27963
+ });
27964
+ return () => {
27965
+ cancelled = true;
27966
+ };
27967
+ }, []);
27968
+ return { tree, loading, error };
27969
+ }
27970
+ function useFileTreeState(initialTree) {
27971
+ const [expanded, setExpanded] = d2(/* @__PURE__ */ new Set());
27972
+ const [openFiles, setOpenFiles] = d2([]);
27973
+ const [activeFilePath, setActiveFilePath] = d2(null);
27974
+ const [loadingFiles, setLoadingFiles] = d2({});
27975
+ const toggleExpand = q2((path) => {
27976
+ setExpanded((prev) => {
27977
+ const next = new Set(prev);
27978
+ if (next.has(path)) next.delete(path);
27979
+ else next.add(path);
27980
+ return next;
27981
+ });
27982
+ }, []);
27983
+ const openFile = q2(async (node) => {
27984
+ if (node.isDir) {
27985
+ toggleExpand(node.path);
27986
+ return;
27987
+ }
27988
+ if (isBinaryExt(node.name)) return;
27989
+ const existing = openFiles.find((f3) => f3.path === node.path);
27990
+ if (existing) {
27991
+ setActiveFilePath(node.path);
27992
+ return;
27993
+ }
27994
+ if (loadingFiles[node.path]) return;
27995
+ setLoadingFiles((prev) => ({ ...prev, [node.path]: true }));
27996
+ const lang = getLanguage(node.name);
27997
+ if (MODE === "standalone") {
27998
+ const mockContent = generateMockContent(node.name, lang);
27999
+ setOpenFiles((prev) => [...prev, { path: node.path, name: node.name, content: mockContent, language: lang }]);
28000
+ setActiveFilePath(node.path);
28001
+ setLoadingFiles((prev) => {
28002
+ const next = { ...prev };
28003
+ delete next[node.path];
28004
+ return next;
28005
+ });
28006
+ return;
28007
+ }
28008
+ try {
28009
+ const encodedPath = node.path.split("/").map(encodeURIComponent).join("/");
28010
+ const data = await api(`/file/${encodedPath}`);
28011
+ setOpenFiles((prev) => [...prev, { path: node.path, name: node.name, content: data.content, language: lang }]);
28012
+ setActiveFilePath(node.path);
28013
+ } catch (err) {
28014
+ console.error(`[file-tree] failed to load ${node.path}:`, err);
28015
+ setOpenFiles((prev) => [...prev, { path: node.path, name: node.name, content: `// Failed to load file: ${err.message}
28016
+ `, language: lang }]);
28017
+ setActiveFilePath(node.path);
28018
+ } finally {
28019
+ setLoadingFiles((prev) => {
28020
+ const next = { ...prev };
28021
+ delete next[node.path];
28022
+ return next;
28023
+ });
28024
+ }
28025
+ }, [openFiles, toggleExpand, loadingFiles]);
28026
+ const closeFile = q2((path) => {
28027
+ setOpenFiles((prev) => {
28028
+ const next = prev.filter((f3) => f3.path !== path);
28029
+ if (activeFilePath === path) {
28030
+ const lastFile = next[next.length - 1];
28031
+ setActiveFilePath(lastFile ? lastFile.path : null);
28032
+ }
28033
+ return next;
28034
+ });
28035
+ }, [activeFilePath]);
28036
+ const activeFile = openFiles.find((f3) => f3.path === activeFilePath) ?? null;
28037
+ return { expanded, openFiles, activeFilePath, activeFile, toggleExpand, openFile, closeFile, setActiveFilePath, loadingFiles };
28038
+ }
28039
+ function generateMockContent(name, lang) {
28040
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
28041
+ if (ext === "json") return JSON.stringify({ name: "example", version: "1.0.0", dependencies: { react: "^18.0.0" } }, null, 2);
28042
+ if (ext === "md") return '# Example Document\n\nThis is a sample markdown file.\n\n## Section\n\n- Item 1\n- Item 2\n\n```js\nconsole.log("hello");\n```';
28043
+ if (ext === "css") return "/* styles */\n.container {\n display: flex;\n padding: 16px;\n color: var(--fg-1);\n}";
28044
+ if (ext === "html") return "<!doctype html>\n<html>\n<head><title>Example</title></head>\n<body>\n <h1>Hello</h1>\n</body>\n</html>";
28045
+ if (ext === "py") return "def hello():\n print('Hello, World!')\n\nif __name__ == '__main__':\n hello()";
28046
+ if (ext === "yaml" || ext === "yml") return "name: example\nversion: '1.0'\nservices:\n app:\n image: node:18\n ports:\n - '3000:3000'";
28047
+ return `// ${name}
28048
+ // Language: ${lang}
28049
+
28050
+ export function example() {
28051
+ return "Hello from ${name}";
28052
+ }
28053
+ `;
28054
+ }
28055
+ function createDemoTree() {
28056
+ return [
28057
+ {
28058
+ name: "src",
28059
+ path: "src",
28060
+ isDir: true,
28061
+ children: [
28062
+ { name: "index.ts", path: "src/index.ts", isDir: false },
28063
+ { name: "app.tsx", path: "src/app.tsx", isDir: false },
28064
+ { name: "config.ts", path: "src/config.ts", isDir: false },
28065
+ {
28066
+ name: "components",
28067
+ path: "src/components",
28068
+ isDir: true,
28069
+ children: [
28070
+ { name: "Header.tsx", path: "src/components/Header.tsx", isDir: false },
28071
+ { name: "Sidebar.tsx", path: "src/components/Sidebar.tsx", isDir: false },
28072
+ { name: "Button.tsx", path: "src/components/Button.tsx", isDir: false }
28073
+ ]
28074
+ },
28075
+ {
28076
+ name: "lib",
28077
+ path: "src/lib",
28078
+ isDir: true,
28079
+ children: [
28080
+ { name: "api.ts", path: "src/lib/api.ts", isDir: false },
28081
+ { name: "format.ts", path: "src/lib/format.ts", isDir: false }
28082
+ ]
28083
+ }
28084
+ ]
28085
+ },
28086
+ {
28087
+ name: "tests",
28088
+ path: "tests",
28089
+ isDir: true,
28090
+ children: [
28091
+ { name: "app.test.ts", path: "tests/app.test.ts", isDir: false },
28092
+ { name: "utils.test.ts", path: "tests/utils.test.ts", isDir: false }
28093
+ ]
28094
+ },
28095
+ { name: "package.json", path: "package.json", isDir: false },
28096
+ { name: "tsconfig.json", path: "tsconfig.json", isDir: false },
28097
+ { name: "README.md", path: "README.md", isDir: false },
28098
+ { name: "styles.css", path: "styles.css", isDir: false },
28099
+ { name: "index.html", path: "index.html", isDir: false }
28100
+ ];
28101
+ }
28102
+
28103
+ // dashboard/src/lib/line-comments.ts
28104
+ function useLineComments() {
28105
+ const [comments, setComments] = d2([]);
28106
+ const [draft, setDraft] = d2(null);
28107
+ const addComment = q2((file, lineNumber, content) => {
28108
+ const id = `comment-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
28109
+ setComments((prev) => [...prev, { id, file, lineNumber, content, timestamp: Date.now() }]);
28110
+ setDraft(null);
28111
+ }, []);
28112
+ const updateComment = q2((id, content) => {
28113
+ setComments((prev) => prev.map((c3) => c3.id === id ? { ...c3, content } : c3));
28114
+ }, []);
28115
+ const deleteComment = q2((id) => {
28116
+ setComments((prev) => prev.filter((c3) => c3.id !== id));
28117
+ }, []);
28118
+ const startDraft = q2((file, lineNumber) => {
28119
+ setDraft({ file, lineNumber, content: "" });
28120
+ }, []);
28121
+ const editComment = q2((id, content) => {
28122
+ const comment = comments.find((c3) => c3.id === id);
28123
+ if (comment) {
28124
+ setDraft({ file: comment.file, lineNumber: comment.lineNumber, content, editingId: id });
28125
+ }
28126
+ }, [comments]);
28127
+ const cancelDraft = q2(() => {
28128
+ setDraft(null);
28129
+ }, []);
28130
+ const setDraftContent = q2((content) => {
28131
+ setDraft((prev) => prev ? { ...prev, content } : null);
28132
+ }, []);
28133
+ const submitDraft = q2(() => {
28134
+ if (draft && draft.content.trim()) {
28135
+ if (draft.editingId) {
28136
+ updateComment(draft.editingId, draft.content.trim());
28137
+ } else {
28138
+ addComment(draft.file, draft.lineNumber, draft.content.trim());
28139
+ }
28140
+ setDraft(null);
28141
+ }
28142
+ }, [draft, addComment, updateComment]);
28143
+ const commentsForFile = q2(
28144
+ (file) => comments.filter((c3) => c3.file === file),
28145
+ [comments]
28146
+ );
28147
+ const commentsForLine = q2(
28148
+ (file, lineNumber) => comments.filter((c3) => c3.file === file && c3.lineNumber === lineNumber),
28149
+ [comments]
28150
+ );
28151
+ return {
28152
+ comments,
28153
+ draft,
28154
+ addComment,
28155
+ updateComment,
28156
+ deleteComment,
28157
+ startDraft,
28158
+ editComment,
28159
+ cancelDraft,
28160
+ setDraftContent,
28161
+ submitDraft,
28162
+ commentsForFile,
28163
+ commentsForLine
28164
+ };
28165
+ }
28166
+
28167
+ // dashboard/src/lib/review-diffs.ts
28168
+ function useReviewDiffs() {
28169
+ const [diffs, setDiffs] = d2([]);
28170
+ const [loading, setLoading] = d2(false);
28171
+ const loadDiffs = q2(async (ep = "/review-diffs") => {
28172
+ setLoading(true);
28173
+ try {
28174
+ const data = await api(ep);
28175
+ setDiffs(Array.isArray(data) ? data : []);
28176
+ } catch {
28177
+ setDiffs([]);
28178
+ } finally {
28179
+ setLoading(false);
28180
+ }
28181
+ }, []);
28182
+ const modifiedFiles = q2(() => new Set(diffs.map((d3) => d3.file)), [diffs]);
28183
+ const modifiedCount = q2(() => diffs.length, [diffs]);
28184
+ return { diffs, loading, modifiedFiles, modifiedCount, reload: loadDiffs };
28185
+ }
28186
+
28187
+ // dashboard/src/lib/diff-parser.ts
28188
+ function parseHunks(patch) {
28189
+ if (!patch) return [];
28190
+ const hunks = [];
28191
+ const rawLines = patch.split("\n");
28192
+ let cursor = 0;
28193
+ while (cursor < rawLines.length) {
28194
+ const line = rawLines[cursor];
28195
+ const m3 = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(line);
28196
+ if (m3) {
28197
+ const oldStart = parseInt(m3[1], 10);
28198
+ const oldLen = m3[2] !== void 0 ? parseInt(m3[2], 10) : 1;
28199
+ const newStart = parseInt(m3[3], 10);
28200
+ const newLen = m3[4] !== void 0 ? parseInt(m3[4], 10) : 1;
28201
+ const lines = [];
28202
+ let oldNum = oldStart;
28203
+ let newNum = newStart;
28204
+ cursor++;
28205
+ while (cursor < rawLines.length && !rawLines[cursor].startsWith("@@ ") && !rawLines[cursor].startsWith("diff ") && !rawLines[cursor].startsWith("--- ") && !rawLines[cursor].startsWith("index ")) {
28206
+ const l3 = rawLines[cursor];
28207
+ if (l3.startsWith("\\")) {
28208
+ cursor++;
28209
+ continue;
28210
+ }
28211
+ const ch = l3[0];
28212
+ const content = l3.slice(1);
28213
+ if (ch === "-") {
28214
+ lines.push({ type: "del", content, oldLineNum: oldNum });
28215
+ oldNum++;
28216
+ } else if (ch === "+") {
28217
+ lines.push({ type: "add", content, newLineNum: newNum });
28218
+ newNum++;
28219
+ } else {
28220
+ lines.push({ type: "ctx", content, oldLineNum: oldNum, newLineNum: newNum });
28221
+ oldNum++;
28222
+ newNum++;
28223
+ }
28224
+ cursor++;
28225
+ }
28226
+ hunks.push({ oldStart, oldLines: oldLen, newStart, newLines: newLen, lines });
28227
+ } else {
28228
+ cursor++;
28229
+ }
28230
+ }
28231
+ return hunks;
28232
+ }
28233
+
28234
+ // dashboard/src/panels/changes.ts
28235
+ var html6 = htm_module_default.bind(k);
28236
+ function escapeAttr(s3) {
28237
+ return s3.replace(/["&<>]/g, (c3) => ({ '"': "&quot;", "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c3]);
28238
+ }
28239
+ function lineDiff2(a3, b2) {
28240
+ const m3 = a3.length, n3 = b2.length;
28241
+ const dp = Array.from({ length: m3 + 1 }, () => new Array(n3 + 1).fill(0));
28242
+ for (let i4 = 1; i4 <= m3; i4++) for (let j5 = 1; j5 <= n3; j5++)
28243
+ dp[i4][j5] = a3[i4 - 1] === b2[j5 - 1] ? dp[i4 - 1][j5 - 1] + 1 : Math.max(dp[i4 - 1][j5], dp[i4][j5 - 1]);
28244
+ const out = [];
28245
+ let i3 = m3, j4 = n3;
28246
+ while (i3 > 0 || j4 > 0) {
28247
+ if (i3 > 0 && j4 > 0 && a3[i3 - 1] === b2[j4 - 1]) {
28248
+ out.push({ kind: "context", text: a3[i3 - 1] });
28249
+ i3--;
28250
+ j4--;
28251
+ } else if (j4 > 0 && (i3 === 0 || dp[i3][j4 - 1] >= dp[i3 - 1][j4])) {
28252
+ out.push({ kind: "ins", text: b2[j4 - 1] });
28253
+ j4--;
28254
+ } else {
28255
+ out.push({ kind: "del", text: a3[i3 - 1] });
28256
+ i3--;
28257
+ }
28258
+ }
28259
+ return out.reverse();
28260
+ }
28261
+ function pairDiffRows2(diff) {
28262
+ const rows = [];
28263
+ let k3 = 0;
28264
+ while (k3 < diff.length) {
28265
+ const e3 = diff[k3];
28266
+ if (e3.kind === "context") {
28267
+ rows.push({ left: e3.text, right: e3.text, kind: "context" });
28268
+ k3++;
28269
+ continue;
28270
+ }
28271
+ const d3 = [], ins = [];
28272
+ while (k3 < diff.length && diff[k3].kind === "del") d3.push(diff[k3].text), k3++;
28273
+ while (k3 < diff.length && diff[k3].kind === "ins") ins.push(diff[k3].text), k3++;
28274
+ const p3 = Math.max(d3.length, ins.length);
28275
+ for (let i3 = 0; i3 < p3; i3++) rows.push({ left: d3[i3] ?? null, right: ins[i3] ?? null, kind: d3[i3] != null && ins[i3] != null ? "change" : d3[i3] != null ? "del" : "ins" });
28276
+ }
28277
+ return rows;
28278
+ }
28279
+ function hE(s3) {
28280
+ return s3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
28281
+ }
28282
+ function renderDiffHtml(patch, style) {
28283
+ const hunks = parseHunks(patch);
28284
+ if (hunks.length === 0) return "";
28285
+ if (style === "unified") {
28286
+ let html9 = "";
28287
+ for (const hunk of hunks) {
28288
+ html9 += `<div class="diff-hunk-header">@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@</div>`;
28289
+ for (const line of hunk.lines) {
28290
+ const cls = line.type === "add" ? "diff-add" : line.type === "del" ? "diff-del" : "";
28291
+ const prefix = line.type === "add" ? "+" : line.type === "del" ? "-" : " ";
28292
+ html9 += `<div class="diff-line ${cls}"><span class="diff-ln-old">${line.oldLineNum ?? ""}</span><span class="diff-ln-new">${line.newLineNum ?? ""}</span><span class="diff-prefix">${prefix}</span><span class="diff-content">${hE(line.content)}</span></div>`;
28293
+ }
28294
+ }
28295
+ return html9;
28296
+ }
28297
+ const oldLines = [], newLines = [];
28298
+ for (const hunk of hunks) {
28299
+ for (const line of hunk.lines) {
28300
+ if (line.type === "ctx") {
28301
+ oldLines.push(line.content);
28302
+ newLines.push(line.content);
28303
+ } else if (line.type === "del") oldLines.push(line.content);
28304
+ else newLines.push(line.content);
28305
+ }
28306
+ }
28307
+ const diff = lineDiff2(oldLines, newLines);
28308
+ const rows = pairDiffRows2(diff);
28309
+ let oldNum = 1, newNum = 1;
28310
+ let html8 = `<div class="edit-diff-head"><div class="edit-diff-side edit-diff-side-old"><span class="edit-diff-marker">\u2212</span> Before</div><div class="edit-diff-side edit-diff-side-new"><span class="edit-diff-marker">+</span> After</div></div><div class="edit-diff-body">`;
28311
+ for (const row of rows) {
28312
+ html8 += `<div class="edit-diff-row edit-diff-row-${row.kind}">`;
28313
+ html8 += `<div class="edit-diff-cell edit-diff-cell-old">`;
28314
+ if (row.left != null) {
28315
+ html8 += `<span class="edit-diff-ln">${oldNum}</span><span class="edit-diff-marker">${row.kind === "del" || row.kind === "change" ? "\u2212" : " "}</span>${hE(row.left)}`;
28316
+ oldNum++;
28317
+ }
28318
+ html8 += `</div>`;
28319
+ html8 += `<div class="edit-diff-cell edit-diff-cell-new">`;
28320
+ if (row.right != null) {
28321
+ html8 += `<span class="edit-diff-ln">${newNum}</span><span class="edit-diff-marker">${row.kind === "ins" || row.kind === "change" ? "+" : " "}</span>${hE(row.right)}`;
28322
+ newNum++;
28323
+ }
28324
+ html8 += `</div></div>`;
28325
+ }
28326
+ html8 += `</div>`;
28327
+ return html8;
28328
+ }
28329
+ function ChangesPanel() {
28330
+ useLang();
28331
+ const { tree, loading } = useProjectTree();
28332
+ const { expanded, openFiles, activeFilePath, activeFile, toggleExpand, openFile, closeFile, setActiveFilePath } = useFileTreeState(tree);
28333
+ const { comments, draft, startDraft, cancelDraft, setDraftContent, submitDraft, commentsForFile, deleteComment, editComment } = useLineComments();
28334
+ const { diffs, modifiedFiles, modifiedCount, reload } = useReviewDiffs();
28335
+ const [diffSource, setDiffSource] = d2("git");
28336
+ const [checkpointList, setCheckpointList] = d2([]);
28337
+ const [selectedCheckpointId, setSelectedCheckpointId] = d2(null);
28338
+ const [createName, setCreateName] = d2("");
28339
+ const [leftPct, setLeftPct] = d2(30);
28340
+ const [rightPct, setRightPct] = d2(30);
28341
+ const [showOnlyModified, setShowOnlyModified] = d2(false);
28342
+ const [reviewMode, setReviewMode] = d2(true);
28343
+ const [diffStyle, setDiffStyle] = d2("unified");
28344
+ const [reviewHtml, setReviewHtml] = d2("");
28345
+ const openingFile = A2(false);
28346
+ y2(() => {
28347
+ if (openFiles.length === 0 && !openingFile.current) setReviewMode(true);
28348
+ }, [openFiles]);
28349
+ const diffEndpoint = diffSource === "checkpoint" ? selectedCheckpointId ? `/checkpoint-diffs?id=${selectedCheckpointId}` : null : diffSource === "git" ? "/git-diffs" : "/review-diffs";
28350
+ y2(() => {
28351
+ if (diffSource === "checkpoint") {
28352
+ api("/checkpoints").then((list2) => setCheckpointList(list2)).catch(() => setCheckpointList([]));
28353
+ }
28354
+ }, [diffSource]);
28355
+ y2(() => {
28356
+ if (diffEndpoint) {
28357
+ reload(diffEndpoint);
28358
+ } else {
28359
+ setReviewHtml(`<div class="review-empty">${t4("changes.reviewEmpty") || "Select a checkpoint to compare"}</div>`);
28360
+ }
28361
+ void diffEndpoint;
28362
+ }, [diffEndpoint, reload]);
28363
+ y2(() => {
28364
+ if (diffs.length === 0) {
28365
+ const emptyMsg = t4("changes.reviewEmpty") || "No changes to review";
28366
+ setReviewHtml(`<div class="review-empty">${emptyMsg}</div>`);
28367
+ return;
28368
+ }
28369
+ setReviewHtml(
28370
+ diffs.map((diff) => {
28371
+ const file = hE(diff.file);
28372
+ const chev = '<span class="chev">\u25B8</span>';
28373
+ const stat = `<span class="stat"><span class="add">+${diff.additions}</span><span class="rem"> -${diff.deletions}</span></span>`;
28374
+ const body = diff.patch ? `<div class="review-file-body" style="display:none">${renderDiffHtml(diff.patch, diffStyle)}</div>` : "";
28375
+ return `<div class="review-file-item" data-file="${escapeAttr(file)}"><div class="review-file-header">${chev}<span class="filename">${escapeAttr(file)}</span>${stat}</div>${body}</div>`;
28376
+ }).join("")
28377
+ );
28378
+ }, [diffs, diffStyle, t4]);
28379
+ const expandAll = q2(() => {
28380
+ document.querySelectorAll(".review-file-body").forEach((el) => {
28381
+ el.style.display = "";
28382
+ });
28383
+ document.querySelectorAll(".review-file-header .chev").forEach((el) => {
28384
+ el.textContent = "\u25BE";
28385
+ });
28386
+ }, []);
28387
+ const collapseAll = q2(() => {
28388
+ document.querySelectorAll(".review-file-body").forEach((el) => {
28389
+ el.style.display = "none";
28390
+ });
28391
+ document.querySelectorAll(".review-file-header .chev").forEach((el) => {
28392
+ el.textContent = "\u25B8";
28393
+ });
28394
+ }, []);
28395
+ const handleLeftResize = q2((delta) => {
28396
+ setLeftPct((prev) => {
28397
+ const containerWidth = window.innerWidth;
28398
+ const deltaPct = delta / containerWidth * 100;
28399
+ return Math.max(15, Math.min(50, prev + deltaPct));
28400
+ });
28401
+ }, []);
28402
+ const handleRightResize = q2((delta) => {
28403
+ setRightPct((prev) => {
28404
+ const containerWidth = window.innerWidth;
28405
+ const deltaPct = delta / containerWidth * 100;
28406
+ return Math.max(15, Math.min(50, prev - deltaPct));
28407
+ });
28408
+ }, []);
28409
+ const toggleModifiedFilter = q2(() => {
28410
+ setShowOnlyModified((prev) => !prev);
28411
+ }, []);
28412
+ const toggleReviewMode = q2(() => {
28413
+ setReviewMode((prev) => !prev);
28414
+ }, []);
28415
+ const openReviewWithFilePicker = q2(() => {
28416
+ setReviewMode(true);
28417
+ }, []);
28418
+ const handleOpenFile = q2(
28419
+ async (filePath) => {
28420
+ const findInTree = (nodes, path) => {
28421
+ for (const n3 of nodes) {
28422
+ if (n3.path === path) return n3;
28423
+ if (n3.children) {
28424
+ const found = findInTree(n3.children, path);
28425
+ if (found) return found;
28426
+ }
28427
+ }
28428
+ return null;
28429
+ };
28430
+ let node = findInTree(tree, filePath);
28431
+ if (!node) {
28432
+ const parts = filePath.split("/");
28433
+ const name = parts[parts.length - 1] || filePath;
28434
+ node = { path: filePath, name, isDir: false };
28435
+ }
28436
+ await openFile(node);
28437
+ },
28438
+ [tree, openFile]
28439
+ );
28440
+ y2(() => {
28441
+ const handler = (e3) => {
28442
+ const header = e3.target.closest(".review-file-header");
28443
+ if (!header) return;
28444
+ const item = header.closest(".review-file-item");
28445
+ if (!item) return;
28446
+ const filePath = item.getAttribute("data-file");
28447
+ if (!filePath) return;
28448
+ const body = item.querySelector(".review-file-body");
28449
+ if (body) {
28450
+ const isOpen = body.style.display !== "none";
28451
+ body.style.display = isOpen ? "none" : "";
28452
+ const chev = header.querySelector(".chev");
28453
+ if (chev) chev.textContent = isOpen ? "\u25B8" : "\u25BE";
28454
+ }
28455
+ };
28456
+ document.addEventListener("click", handler);
28457
+ return () => document.removeEventListener("click", handler);
28458
+ }, []);
28459
+ const activeFileComments = activeFile ? commentsForFile(activeFile.path) : [];
28460
+ return html6`
28461
+ <div class="changes-layout">
28462
+ <div class="changes-panel changes-panel-left" style=${{ width: `${leftPct}%` }}>
28463
+ <div class="changes-panel-header">
28464
+ <span class="glyph">◆</span>
28465
+ <span>${t4("changes.chatPanelTitle")}</span>
28466
+ </div>
28467
+ <div class="changes-panel-body">
28468
+ <${ChatPane}
28469
+ comments=${comments}
28470
+ deleteComment=${deleteComment}
28471
+ />
28472
+ </div>
28473
+ </div>
28474
+
28475
+ <${ResizeHandle} onResize=${handleLeftResize} direction="horizontal" />
28476
+
28477
+ <div class="changes-panel changes-panel-center">
28478
+ ${reviewMode ? html6`
28479
+ <${TabBar}
28480
+ reviewTab=${html6`<${ReviewTab} count=${modifiedCount()} active=${true} onClick=${toggleReviewMode} />`}
28481
+ fileList=${diffs.map((d3) => d3.file)}
28482
+ onOpenFile=${(f3) => {
28483
+ handleOpenFile(f3);
28484
+ setReviewMode(false);
28485
+ }}
28486
+ onToggleReview=${toggleReviewMode}
28487
+ files=${openFiles}
28488
+ activePath=${activeFilePath}
28489
+ onSelect=${(path) => {
28490
+ setActiveFilePath(path);
28491
+ setReviewMode(false);
28492
+ }}
28493
+ onClose=${closeFile}
28494
+ />
28495
+ <div class="review-controls" style=${{ display: "flex", alignItems: "center", gap: "8px", padding: "6px 12px", borderBottom: "1px solid var(--bd)", fontSize: "12px" }}>
28496
+ <select value=${diffSource} onChange=${(e3) => {
28497
+ const v3 = e3.target.value;
28498
+ setDiffSource(v3);
28499
+ if (v3 !== "checkpoint") setSelectedCheckpointId(null);
28500
+ }} style=${{ fontSize: "12px", fontWeight: 500, padding: "1px 4px", borderRadius: "3px", background: "var(--bg-elev)", color: "var(--fg-0)", border: "1px solid var(--bd)", cursor: "pointer", outline: "none" }}>
28501
+ <option value="git">${t4("changes.diffSourceGit")}</option>
28502
+ <option value="session">${t4("changes.diffSourceSession")}</option>
28503
+ <option value="checkpoint">${t4("changes.diffSourceCheckpoint")}</option>
28504
+ </select>
28505
+ ${diffSource !== "checkpoint" || selectedCheckpointId ? html6`
28506
+ <span style=${{ color: "var(--fg-3)" }}>${modifiedCount()}</span>
28507
+ <div style=${{ marginLeft: "auto", display: "flex", alignItems: "center", gap: "4px" }}>
28508
+ <button class=${`toggle-btn ${diffStyle === "unified" ? "active" : ""}`} onClick=${() => setDiffStyle("unified")} style=${{ fontSize: "11px", padding: "2px 6px" }}>${t4("changes.diffStyleUnified")}</button>
28509
+ <button class=${`toggle-btn ${diffStyle === "split" ? "active" : ""}`} onClick=${() => setDiffStyle("split")} style=${{ fontSize: "11px", padding: "2px 6px" }}>${t4("changes.diffStyleSplit")}</button>
28510
+ <button class="toggle-btn" onClick=${expandAll} style=${{ fontSize: "11px", padding: "2px 6px" }}>${t4("changes.expandAll")}</button>
28511
+ <button class="toggle-btn" onClick=${collapseAll} style=${{ fontSize: "11px", padding: "2px 6px" }}>${t4("changes.collapseAll")}</button>
28512
+ </div>
28513
+ ` : null}
28514
+ </div>
28515
+ ${diffSource === "checkpoint" && selectedCheckpointId ? html6`
28516
+ <div style=${{ padding: "4px 12px", fontSize: "11px", color: "var(--fg-3)", borderBottom: "1px solid var(--bd)", cursor: "pointer" }}>
28517
+ <span onClick=${() => setSelectedCheckpointId(null)} style=${{ color: "var(--c-brand)", cursor: "pointer" }}>← ${t4("changes.backToList")}</span>
28518
+ </div>
28519
+ ` : null}
28520
+ ${diffSource === "checkpoint" && !selectedCheckpointId ? html6`
28521
+ <div class="checkpoint-picker" style=${{ flex: "1", overflowY: "auto", padding: "8px 12px" }}>
28522
+ <div style=${{ display: "flex", gap: "6px", marginBottom: "8px" }}>
28523
+ <input
28524
+ value=${createName}
28525
+ onInput=${(e3) => setCreateName(e3.target.value)}
28526
+ placeholder=${t4("changes.createPlaceholder")}
28527
+ style=${{ flex: 1, fontSize: "12px", padding: "4px 8px", background: "var(--bg-input)", border: "1px solid var(--bd)", borderRadius: "3px", color: "var(--fg-0)" }}
28528
+ />
28529
+ <button
28530
+ class="primary"
28531
+ onClick=${async () => {
28532
+ const name = createName.trim();
28533
+ if (!name) return;
28534
+ try {
28535
+ await api("/checkpoint-create", { method: "POST", body: { name } });
28536
+ setCreateName("");
28537
+ const list2 = await api("/checkpoints");
28538
+ setCheckpointList(list2);
28539
+ } catch {
28540
+ alert("create failed");
28541
+ }
28542
+ }}
28543
+ disabled=${!createName.trim()}
28544
+ style=${{ padding: "5px 12px" }}
28545
+ >${t4("changes.createBtn")}</button>
28546
+ </div>
28547
+ ${checkpointList.length === 0 ? html6`
28548
+ <div class="empty" style=${{ textAlign: "center", margin: "12px" }}>${t4("changes.checkpointEmpty")}</div>
28549
+ ` : checkpointList.map((c3) => html6`
28550
+ <div
28551
+ key=${c3.id}
28552
+ class="checkpoint-item"
28553
+ style=${{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "6px 8px", cursor: "pointer", borderRadius: "4px", borderBottom: "1px solid var(--bd)" }}
28554
+ onMouseEnter=${(e3) => {
28555
+ e3.currentTarget.style.background = "var(--bg-hover)";
28556
+ }}
28557
+ onMouseLeave=${(e3) => {
28558
+ e3.currentTarget.style.background = "transparent";
28559
+ }}
28560
+ >
28561
+ <div
28562
+ onClick=${() => {
28563
+ setSelectedCheckpointId(c3.id);
28564
+ }}
28565
+ style=${{ display: "flex", flexDirection: "column", gap: "2px", flex: 1 }}
28566
+ >
28567
+ <span style=${{ fontSize: "13px", fontWeight: 500 }}>${c3.name}</span>
28568
+ <span style=${{ fontSize: "11px", color: "var(--fg-3)" }}>${c3.id.slice(0, 7)} · ${c3.fileCount} file${c3.fileCount === 1 ? "" : "s"}</span>
28569
+ </div>
28570
+ <div style=${{ display: "flex", alignItems: "center", gap: "8px" }}>
28571
+ <span style=${{ fontSize: "11px", color: "var(--fg-4)" }}>${c3.ago}</span>
28572
+ <button
28573
+ onClick=${async (e3) => {
28574
+ e3.stopPropagation();
28575
+ if (confirm(t4("changes.restoreConfirm").replace("{name}", c3.name))) {
28576
+ try {
28577
+ await api("/checkpoint-restore", { method: "POST", body: { id: c3.id } });
28578
+ setSelectedCheckpointId(null);
28579
+ setDiffSource("git");
28580
+ } catch {
28581
+ alert("restore failed");
28582
+ }
28583
+ }
28584
+ }}
28585
+ style=${{ fontSize: "11px", padding: "2px 6px", background: "var(--c-brand)", color: "#fff", border: "none", borderRadius: "3px", cursor: "pointer" }}
28586
+ >${t4("changes.restoreBtn")}</button>
28587
+ <button
28588
+ onClick=${async (e3) => {
28589
+ e3.stopPropagation();
28590
+ if (confirm(t4("changes.deleteConfirm").replace("{name}", c3.name))) {
28591
+ try {
28592
+ await api("/checkpoint-delete", { method: "POST", body: { id: c3.id } });
28593
+ setCheckpointList((prev) => prev.filter((x3) => x3.id !== c3.id));
28594
+ } catch {
28595
+ alert("delete failed");
28596
+ }
28597
+ }
28598
+ }}
28599
+ style=${{ fontSize: "11px", padding: "2px 6px", color: "var(--fg-3)", border: "1px solid var(--bd)", borderRadius: "3px", cursor: "pointer", background: "transparent" }}
28600
+ >${t4("changes.deleteBtn")}</button>
28601
+ </div>
28602
+ </div>
28603
+ `)}
28604
+ </div>
28605
+ ` : null}
28606
+ <div class="review-diff-view" style=${{ flex: "1", overflowY: "auto" }}>
28607
+ <div class="review-diff-list" style=${{ padding: "0 12px" }} key=${diffStyle} dangerouslySetInnerHTML=${{ __html: reviewHtml }}></div>
28608
+ </div>
28609
+ ` : html6`
28610
+ <${TabBar}
28611
+ reviewTab=${html6`<${ReviewTab} count=${modifiedCount()} active=${false} onClick=${toggleReviewMode} />`}
28612
+ fileList=${diffs.map((d3) => d3.file)}
28613
+ onOpenFile=${handleOpenFile}
28614
+ files=${openFiles}
28615
+ activePath=${activeFilePath}
28616
+ onSelect=${setActiveFilePath}
28617
+ onClose=${closeFile}
28618
+ />
28619
+ <${CodeViewer}
28620
+ key=${activeFile?.path ?? "empty"}
28621
+ file=${activeFile}
28622
+ comments=${activeFileComments}
28623
+ draft=${draft && draft.file === activeFilePath ? draft : null}
28624
+ onStartComment=${startDraft}
28625
+ onEditComment=${editComment}
28626
+ onCancelComment=${cancelDraft}
28627
+ onCommentChange=${setDraftContent}
28628
+ onSubmitComment=${submitDraft}
28629
+ onDeleteComment=${deleteComment}
28630
+ />
28631
+ `}
28632
+ </div>
28633
+
28634
+ <${ResizeHandle} onResize=${handleRightResize} direction="horizontal" />
28635
+
28636
+ <div class="changes-panel changes-panel-right" style=${{ width: `${rightPct}%` }}>
28637
+ <div class="changes-panel-header">
28638
+ <span class="glyph">▼</span>
28639
+ <span>${t4("changes.fileTreeTitle")}</span>
28640
+ </div>
28641
+ <${FileTreeToggle}
28642
+ showOnlyModified=${showOnlyModified}
28643
+ modifiedCount=${modifiedCount()}
28644
+ onToggle=${toggleModifiedFilter}
28645
+ />
28646
+ <div class="changes-panel-body">
28647
+ ${loading ? html6`<div class="empty" style=${{ margin: "12px", textAlign: "center" }}>${t4("changes.loadingFiles")}</div>` : html6`<${FileTree}
28648
+ nodes=${tree}
28649
+ expanded=${expanded}
28650
+ onToggle=${toggleExpand}
28651
+ onSelect=${(node) => {
28652
+ setReviewMode(false);
28653
+ openFile(node);
28654
+ }}
28655
+ modifiedFiles=${modifiedFiles()}
28656
+ showOnlyModified=${showOnlyModified}
28657
+ />`}
28658
+ </div>
28659
+ </div>
28660
+ </div>
28661
+ `;
28662
+ }
28663
+ function fmtCost2(usd, currency) {
28664
+ if (currency === "CNY" || currency === "\xA5") {
28665
+ return `\xA5${(usd * 7.2).toFixed(4)}`;
28666
+ }
28667
+ return `$${usd.toFixed(4)}`;
28668
+ }
28669
+ function ChatStatusBar3({ stats, model }) {
28670
+ useLang();
28671
+ if (!stats) {
28672
+ return html6`
28673
+ <div class="chat-statusbar">
28674
+ <span class="muted">—</span>
28675
+ </div>
28676
+ `;
28677
+ }
28678
+ const ctxPct = stats.contextCapTokens > 0 ? stats.lastPromptTokens / stats.contextCapTokens * 100 : 0;
28679
+ const balance = stats.balance && stats.balance.length > 0 ? stats.balance[0] : null;
28680
+ return html6`
28681
+ <div class="chat-statusbar">
28682
+ <span class="status-item">
28683
+ <span class="status-label">${t4("chat.statusModel")}</span>
28684
+ <code>${model ?? "\u2014"}</code>
28685
+ </span>
28686
+ <span class="status-item">
28687
+ <span class="status-label">${t4("chat.statusCtx")}</span>
28688
+ <span class="status-bar-mini">
28689
+ <span class="status-bar-mini-fill" style=${`width: ${Math.min(100, ctxPct).toFixed(1)}%;`}></span>
28690
+ </span>
28691
+ <span class="muted">${stats.lastPromptTokens.toLocaleString()} / ${(stats.contextCapTokens / 1e3).toFixed(0)}K</span>
28692
+ </span>
28693
+ <span class="status-item">
28694
+ <span class="status-label">${t4("chat.statusCache")}</span>
28695
+ <span class=${stats.cacheHitRatio >= 0.9 ? "status-ok" : stats.cacheHitRatio >= 0.6 ? "status-warn" : "status-err"}>
28696
+ ${(stats.cacheHitRatio * 100).toFixed(1)}%
28697
+ </span>
28698
+ </span>
28699
+ <span class="status-item">
28700
+ <span class="status-label">${t4("chat.statusTurn")}</span>
28701
+ <code>${fmtCost2(stats.lastTurnCostUsd, balance?.currency)}</code>
28702
+ </span>
28703
+ <span class="status-item">
28704
+ <span class="status-label">${t4("chat.statusSession")}</span>
28705
+ <code>${fmtCost2(stats.totalCostUsd, balance?.currency)}</code>
28706
+ <span class="muted" style="font-size: 10px;">
28707
+ ${t4("chat.statusTurns", { count: stats.turns, s: stats.turns === 1 ? "" : "s" })}
28708
+ </span>
28709
+ </span>
28710
+ ${balance ? html6`
28711
+ <span class="status-item">
28712
+ <span class="status-label">${t4("chat.statusBalance")}</span>
28713
+ <code>${balance.total_balance} ${balance.currency}</code>
28714
+ </span>
28715
+ ` : null}
28716
+ </div>
28717
+ `;
28718
+ }
28719
+ function CommentCard(props) {
28720
+ return html6`
28721
+ <div class="comment-card">
28722
+ <span class="comment-card-icon">⬥</span>
28723
+ <span class="comment-card-file">${props.fileName}:${props.lineNumber}</span>
28724
+ <span class="comment-card-content">${props.content}</span>
28725
+ <span class="comment-card-remove" onClick=${props.onRemove}>×</span>
28726
+ </div>
28727
+ `;
28728
+ }
28729
+ function filterModifiedNodes(nodes, modifiedFiles) {
28730
+ return nodes.map((node) => {
28731
+ if (node.isDir && node.children) {
28732
+ const filteredChildren = filterModifiedNodes(node.children, modifiedFiles);
28733
+ if (filteredChildren.length === 0) return null;
28734
+ return { ...node, children: filteredChildren };
28735
+ }
28736
+ if (modifiedFiles.has(node.path)) return node;
28737
+ return null;
28738
+ }).filter((n3) => n3 !== null);
28739
+ }
28740
+ function renderTree(props) {
28741
+ const { nodes, expanded, onToggle, onSelect, indent = 0, modifiedFiles = /* @__PURE__ */ new Set(), showOnlyModified = false } = props;
28742
+ const displayNodes = showOnlyModified ? filterModifiedNodes(nodes, modifiedFiles) : nodes;
28743
+ return displayNodes.map((node) => {
28744
+ const isExpanded = expanded.has(node.path);
28745
+ const indentEls = [];
28746
+ for (let i3 = 0; i3 < indent; i3++) {
28747
+ indentEls.push(html6`<span class="indent" key=${`indent-${i3}`} />`);
28748
+ }
28749
+ if (node.isDir) {
28750
+ const cls2 = isExpanded ? "tree-node open" : "tree-node";
28751
+ return html6`
28752
+ <div key=${node.path}>
28753
+ <div class=${cls2} onClick=${() => onToggle(node.path)}>
28754
+ ${indentEls}
28755
+ <span class="arrow">${isExpanded ? "\u25BE" : "\u25B8"}</span>
28756
+ <span class="icon dir">▼</span>
28757
+ <span class="name">${node.name}</span>
28758
+ </div>
28759
+ ${isExpanded && node.children && node.children.length > 0 ? renderTree({ nodes: node.children, expanded, onToggle, onSelect, indent: indent + 1, modifiedFiles, showOnlyModified }) : null}
28760
+ ${isExpanded && (!node.children || node.children.length === 0) ? html6`<div class="tree-node" style=${{ paddingLeft: `${(indent + 1) * 14 + 8}px` }}>
28761
+ <span class="name muted">${t4("changes.treeEmpty")}</span>
28762
+ </div>` : null}
28763
+ </div>
28764
+ `;
28765
+ }
28766
+ const { icon, cls } = getFileIcon(node.name);
28767
+ const isModified = modifiedFiles.has(node.path);
28768
+ return html6`
28769
+ <div
28770
+ key=${node.path}
28771
+ class="tree-node"
28772
+ onClick=${() => onSelect(node)}
28773
+ style=${{ paddingLeft: `${indent * 14 + 8}px` }}
28774
+ >
28775
+ <span class=${`icon ${cls}`}>${icon}</span>
28776
+ <span class="name">${node.name}</span>
28777
+ ${isModified ? html6`<span class="mod-indicator" />` : null}
28778
+ </div>
28779
+ `;
28780
+ });
28781
+ }
28782
+ function FileTree(props) {
28783
+ return html6`
28784
+ <div class="tree">
28785
+ ${renderTree(props)}
28786
+ </div>
28787
+ `;
28788
+ }
28789
+ function FileTreeToggle(props) {
28790
+ return html6`
28791
+ <div class="file-tree-toggle">
28792
+ <button
28793
+ class=${`toggle-btn ${props.showOnlyModified ? "active" : ""}`}
28794
+ onClick=${props.onToggle}
28795
+ >
28796
+ ${props.modifiedCount} ${t4("changes.changes")}
28797
+ </button>
28798
+ <button
28799
+ class=${`toggle-btn ${!props.showOnlyModified ? "active" : ""}`}
28800
+ onClick=${props.onToggle}
28801
+ >
28802
+ ${t4("changes.allFiles")}
28803
+ </button>
28804
+ </div>
28805
+ `;
28806
+ }
28807
+ function ReviewTab(props) {
28808
+ return html6`
28809
+ <div
28810
+ class=${`editor-tab review-tab${props.active ? " active" : ""}`}
28811
+ onClick=${props.onClick}
28812
+ style=${{ display: "flex", alignItems: "center", gap: "3px", padding: "4px 6px", cursor: props.onClick ? "pointer" : "default" }}
28813
+ >
28814
+ <span class="review-icon">◑</span>
28815
+ <span>${t4("changes.review")}</span>
28816
+ <span style=${{ color: "var(--fg-3)", fontSize: "10.5px" }}>${props.count}</span>
28817
+ </div>
28818
+ `;
28819
+ }
28820
+ function ResizeHandle(props) {
28821
+ const { onResize, direction } = props;
28822
+ const dragging = A2(false);
28823
+ const startX = A2(0);
28824
+ const onMouseDown = q2((e3) => {
28825
+ e3.preventDefault();
28826
+ dragging.current = true;
28827
+ startX.current = direction === "horizontal" ? e3.clientX : e3.clientY;
28828
+ document.body.style.cursor = direction === "horizontal" ? "col-resize" : "row-resize";
28829
+ document.body.style.userSelect = "none";
28830
+ }, [direction]);
28831
+ y2(() => {
28832
+ const onMouseMove = (e3) => {
28833
+ if (!dragging.current) return;
28834
+ const current = direction === "horizontal" ? e3.clientX : e3.clientY;
28835
+ const delta = current - startX.current;
28836
+ startX.current = current;
28837
+ onResize(delta);
28838
+ };
28839
+ const onMouseUp = () => {
28840
+ if (!dragging.current) return;
28841
+ dragging.current = false;
28842
+ document.body.style.cursor = "";
28843
+ document.body.style.userSelect = "";
28844
+ };
28845
+ document.addEventListener("mousemove", onMouseMove);
28846
+ document.addEventListener("mouseup", onMouseUp);
28847
+ return () => {
28848
+ document.removeEventListener("mousemove", onMouseMove);
28849
+ document.removeEventListener("mouseup", onMouseUp);
28850
+ };
28851
+ }, [onResize, direction]);
28852
+ const isH = direction === "horizontal";
28853
+ return html6`
28854
+ <div
28855
+ onMouseDown=${onMouseDown}
28856
+ style=${{
28857
+ width: isH ? "4px" : "100%",
28858
+ height: isH ? "100%" : "4px",
28859
+ cursor: isH ? "col-resize" : "row-resize",
28860
+ background: "var(--bd)",
28861
+ flexShrink: 0,
28862
+ transition: "background 0.15s"
28863
+ }}
28864
+ onMouseEnter=${(e3) => {
28865
+ e3.target.style.background = "var(--c-brand)";
28866
+ }}
28867
+ onMouseLeave=${(e3) => {
28868
+ e3.target.style.background = "var(--bd)";
28869
+ }}
28870
+ />
28871
+ `;
28872
+ }
28873
+ function TabBar(props) {
28874
+ const { files, activePath, onSelect, onClose, reviewTab, fileList, onOpenFile } = props;
28875
+ const popupRef = A2(null);
28876
+ const btnRef = A2(null);
28877
+ y2(() => {
28878
+ const btn = btnRef.current;
28879
+ if (!btn || !fileList || fileList.length === 0) return;
28880
+ const toggle = (e3) => {
28881
+ e3.stopPropagation();
28882
+ if (popupRef.current) {
28883
+ popupRef.current.remove();
28884
+ popupRef.current = null;
28885
+ return;
28886
+ }
28887
+ const allFiles = fileList;
28888
+ const popup = document.createElement("div");
28889
+ popupRef.current = popup;
28890
+ popup.style.cssText = "position:fixed;top:20%;left:50%;transform:translateX(-50%);background:var(--bg-elev-2);border:1px solid var(--bd);border-radius:6px;max-height:400px;display:flex;flex-direction:column;z-index:1000;min-width:380px;max-width:600px;box-shadow:0 8px 24px rgba(0,0,0,.4)";
28891
+ const input = document.createElement("input");
28892
+ input.placeholder = "\u641C\u7D22\u6587\u4EF6...";
28893
+ input.style.cssText = "margin:6px 8px;padding:5px 8px;font-size:12px;background:var(--bg);color:var(--fg-0);border:1px solid var(--bd);border-radius:4px;outline:none;flex-shrink:0";
28894
+ input.onclick = (ev) => ev.stopPropagation();
28895
+ popup.appendChild(input);
28896
+ const listWrap = document.createElement("div");
28897
+ listWrap.style.cssText = "overflow-y:auto;flex:1;padding:0 4px 4px";
28898
+ popup.appendChild(listWrap);
28899
+ function renderList(filter) {
28900
+ listWrap.innerHTML = "";
28901
+ const q4 = filter.toLowerCase();
28902
+ for (const f3 of allFiles) {
28903
+ if (q4 && !f3.toLowerCase().includes(q4)) continue;
28904
+ const row = document.createElement("div");
28905
+ row.textContent = f3;
28906
+ row.style.cssText = "padding:3px 8px;font-size:11px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:var(--font-mono);border-radius:3px";
28907
+ row.onmouseenter = () => row.style.background = "var(--bg-hover)";
28908
+ row.onmouseleave = () => row.style.background = "transparent";
28909
+ row.onclick = (ev) => {
28910
+ ev.stopPropagation();
28911
+ onOpenFile?.(f3);
28912
+ popup.remove();
28913
+ popupRef.current = null;
28914
+ };
28915
+ listWrap.appendChild(row);
28916
+ }
28917
+ }
28918
+ renderList("");
28919
+ input.oninput = () => renderList(input.value);
28920
+ setTimeout(() => input.focus(), 50);
28921
+ document.body.appendChild(popup);
28922
+ const close = (ev) => {
28923
+ if (popupRef.current && !popup.contains(ev.target) && ev.target !== btn) {
28924
+ popup.remove();
28925
+ popupRef.current = null;
28926
+ document.removeEventListener("mousedown", close, true);
28927
+ }
28928
+ };
28929
+ document.addEventListener("mousedown", close, true);
28930
+ };
28931
+ btn.addEventListener("click", toggle);
28932
+ return () => {
28933
+ btn.removeEventListener("click", toggle);
28934
+ if (popupRef.current) {
28935
+ popupRef.current.remove();
28936
+ popupRef.current = null;
28937
+ }
28938
+ };
28939
+ }, [fileList, onOpenFile]);
28940
+ return html6`
28941
+ <div class="editor-tabs">
28942
+ ${reviewTab || null}
28943
+ ${fileList ? html6`
28944
+ <span
28945
+ ref=${btnRef}
28946
+ style=${{
28947
+ fontSize: "14px",
28948
+ padding: "4px 3px",
28949
+ cursor: "pointer",
28950
+ color: "var(--fg-3)",
28951
+ userSelect: "none",
28952
+ lineHeight: "1",
28953
+ fontFamily: "var(--font-mono)"
28954
+ }}
28955
+ title="Open file"
28956
+ >+</span>
28957
+ ` : null}
28958
+ ${files.map((f3) => html6`
28959
+ <div
28960
+ key=${f3.path}
28961
+ class=${`editor-tab ${f3.path === activePath ? "active" : ""}`}
28962
+ onClick=${() => onSelect(f3.path)}
28963
+ title=${f3.path}
28964
+ >
28965
+ <span>${f3.name}</span>
28966
+ <span
28967
+ class="x"
28968
+ onClick=${(e3) => {
28969
+ e3.stopPropagation();
28970
+ onClose(f3.path);
28971
+ }}
28972
+ title=${t4("changes.tabClose")}
28973
+ >×</span>
28974
+ </div>
28975
+ `)}
28976
+ </div>
28977
+ `;
28978
+ }
28979
+ function CodeViewer(props) {
28980
+ const { file, comments = [], draft, onStartComment, onEditComment, onCancelComment, onCommentChange, onSubmitComment, onDeleteComment } = props;
28981
+ const codeRef = A2(null);
28982
+ const [hoveredLine, setHoveredLine] = d2(null);
28983
+ y2(() => {
28984
+ if (!file) return;
28985
+ const el = codeRef.current;
28986
+ if (!el) return;
28987
+ el.innerHTML = "";
28988
+ const lines = file.content.split("\n");
28989
+ const commentsByLine = /* @__PURE__ */ new Map();
28990
+ comments.forEach((c3) => {
28991
+ const existing = commentsByLine.get(c3.lineNumber) || [];
28992
+ existing.push(c3);
28993
+ commentsByLine.set(c3.lineNumber, existing);
28994
+ });
28995
+ lines.forEach((line, i3) => {
28996
+ const lineNumber = i3 + 1;
28997
+ const lineComments = commentsByLine.get(lineNumber) || [];
28998
+ const hasComments = lineComments.length > 0;
28999
+ const lineDiv = document.createElement("div");
29000
+ lineDiv.className = "editor-line";
29001
+ lineDiv.dataset.lineNumber = String(lineNumber);
29002
+ lineDiv.style.position = "relative";
29003
+ lineDiv.addEventListener("mouseenter", () => setHoveredLine(lineNumber));
29004
+ lineDiv.addEventListener("mouseleave", () => setHoveredLine(null));
29005
+ const gutter = document.createElement("div");
29006
+ gutter.className = "lineno";
29007
+ gutter.textContent = String(lineNumber);
29008
+ gutter.style.position = "relative";
29009
+ gutter.style.display = "flex";
29010
+ gutter.style.alignItems = "center";
29011
+ gutter.style.justifyContent = "center";
29012
+ gutter.style.gap = "4px";
29013
+ if (onStartComment) {
29014
+ const isVisible = hoveredLine === lineNumber && (!draft || draft.file !== file.path || draft.lineNumber !== lineNumber);
29015
+ const anchorBtn = document.createElement("span");
29016
+ anchorBtn.className = `line-comment-anchor ${isVisible ? "visible" : ""}`;
29017
+ anchorBtn.style.cssText = "width:16px;height:16px;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;cursor:pointer;transition:opacity 0.15s ease;flex-shrink:0;";
29018
+ if (isVisible) {
29019
+ anchorBtn.style.opacity = "1";
29020
+ anchorBtn.style.pointerEvents = "auto";
29021
+ }
29022
+ if (hasComments) {
29023
+ anchorBtn.innerHTML = `<span class="comment-count" style="background:rgba(121,192,255,0.12);border-radius:2px;padding:0 3px;font-size:10px;color:#79c0ff;font-family:monospace;">${lineComments.length}</span>`;
29024
+ } else {
29025
+ anchorBtn.innerHTML = `<span class="plus-icon" style="font-family:monospace;font-size:14px;color:#6e7681;line-height:1;">+</span>`;
29026
+ }
29027
+ anchorBtn.addEventListener("mouseenter", () => {
29028
+ anchorBtn.style.opacity = "1";
29029
+ });
29030
+ anchorBtn.addEventListener("click", (e3) => {
29031
+ e3.stopPropagation();
29032
+ onStartComment(file.path, lineNumber);
29033
+ });
29034
+ gutter.appendChild(anchorBtn);
29035
+ }
29036
+ const content = document.createElement("span");
29037
+ content.className = "ln-content";
29038
+ content.textContent = line || " ";
29039
+ lineDiv.appendChild(gutter);
29040
+ lineDiv.appendChild(content);
29041
+ el.appendChild(lineDiv);
29042
+ if (draft && draft.file === file.path && draft.lineNumber === lineNumber) {
29043
+ const editorContainer = document.createElement("div");
29044
+ editorContainer.className = "line-comment-editor";
29045
+ const labelDiv = document.createElement("div");
29046
+ labelDiv.className = "line-comment-label";
29047
+ labelDiv.textContent = `${t4("changes.commentLabel")} ${lineNumber}`;
29048
+ const textarea = document.createElement("textarea");
29049
+ textarea.className = "line-comment-textarea";
29050
+ textarea.placeholder = t4("changes.commentPlaceholder");
29051
+ textarea.rows = 2;
29052
+ textarea.value = draft.content;
29053
+ let isComposing = false;
29054
+ textarea.addEventListener("compositionstart", () => {
29055
+ isComposing = true;
29056
+ });
29057
+ textarea.addEventListener("compositionend", (e3) => {
29058
+ isComposing = false;
29059
+ if (onCommentChange) onCommentChange(e3.target.value);
29060
+ });
29061
+ textarea.addEventListener("input", (e3) => {
29062
+ if (!isComposing && onCommentChange) onCommentChange(e3.target.value);
29063
+ });
29064
+ textarea.addEventListener("keydown", (e3) => {
29065
+ if (e3.key === "Escape" && onCancelComment) {
29066
+ e3.preventDefault();
29067
+ onCancelComment();
29068
+ } else if (e3.key === "Enter" && e3.ctrlKey && onSubmitComment) {
29069
+ e3.preventDefault();
29070
+ onSubmitComment();
29071
+ }
29072
+ });
29073
+ const actionsDiv = document.createElement("div");
29074
+ actionsDiv.className = "line-comment-actions";
29075
+ actionsDiv.style.cssText = "display:flex;gap:4px;justify-content:flex-end;";
29076
+ const cancelBtn = document.createElement("button");
29077
+ cancelBtn.className = "btn ghost";
29078
+ cancelBtn.textContent = t4("changes.commentCancel");
29079
+ cancelBtn.style.cssText = "background:transparent;border:none;color:#6e7681;padding:3px 8px;font-size:11px;cursor:pointer;";
29080
+ cancelBtn.addEventListener("click", () => {
29081
+ if (onCancelComment) onCancelComment();
29082
+ });
29083
+ const submitBtn = document.createElement("button");
29084
+ submitBtn.className = "btn primary";
29085
+ submitBtn.textContent = t4("changes.commentSubmit");
29086
+ submitBtn.style.cssText = "background:#79c0ff;color:#0a0c10;border:none;padding:3px 8px;font-size:11px;cursor:pointer;border-radius:2px;font-weight:600;";
29087
+ submitBtn.disabled = !draft.content.trim();
29088
+ submitBtn.addEventListener("click", () => {
29089
+ if (onSubmitComment) onSubmitComment();
29090
+ });
29091
+ actionsDiv.appendChild(cancelBtn);
29092
+ actionsDiv.appendChild(submitBtn);
29093
+ editorContainer.appendChild(labelDiv);
29094
+ editorContainer.appendChild(textarea);
29095
+ editorContainer.appendChild(actionsDiv);
29096
+ el.appendChild(editorContainer);
29097
+ setTimeout(() => textarea.focus(), 0);
29098
+ }
29099
+ if (hasComments) {
29100
+ lineComments.forEach((comment) => {
29101
+ if (el.querySelector(`.line-comment-bubble[data-id="${comment.id}"]`)) return;
29102
+ const isEditing = draft && draft.editingId === comment.id;
29103
+ if (isEditing) return;
29104
+ const bubbleDiv = document.createElement("div");
29105
+ bubbleDiv.className = "line-comment-bubble";
29106
+ bubbleDiv.dataset.id = comment.id;
29107
+ const contentDiv = document.createElement("div");
29108
+ contentDiv.className = "bubble-content";
29109
+ contentDiv.textContent = comment.content;
29110
+ const footerDiv = document.createElement("div");
29111
+ footerDiv.className = "bubble-footer";
29112
+ const lineSpan = document.createElement("span");
29113
+ lineSpan.className = "bubble-line";
29114
+ lineSpan.textContent = `\u8BC4\u8BBA\u7B2C ${comment.lineNumber} \u884C`;
29115
+ const actionsDiv = document.createElement("div");
29116
+ actionsDiv.className = "bubble-actions";
29117
+ actionsDiv.style.display = "flex";
29118
+ actionsDiv.style.gap = "4px";
29119
+ if (onEditComment) {
29120
+ const editBtn = document.createElement("button");
29121
+ editBtn.className = "bubble-btn";
29122
+ editBtn.textContent = "\u7F16\u8F91";
29123
+ editBtn.style.cssText = "background:transparent;border:none;color:#6e7681;padding:3px 8px;font-size:11px;cursor:pointer;border-radius:2px;";
29124
+ editBtn.addEventListener("click", (e3) => {
29125
+ e3.stopPropagation();
29126
+ onEditComment(comment.id, comment.content);
29127
+ });
29128
+ actionsDiv.appendChild(editBtn);
29129
+ }
29130
+ if (onDeleteComment) {
29131
+ const deleteBtn = document.createElement("button");
29132
+ deleteBtn.className = "bubble-btn danger";
29133
+ deleteBtn.textContent = "\u5220\u9664";
29134
+ deleteBtn.style.cssText = "background:transparent;border:none;color:#6e7681;padding:3px 8px;font-size:11px;cursor:pointer;border-radius:2px;";
29135
+ deleteBtn.addEventListener("click", (e3) => {
29136
+ e3.stopPropagation();
29137
+ onDeleteComment(comment.id);
29138
+ });
29139
+ actionsDiv.appendChild(deleteBtn);
29140
+ }
29141
+ footerDiv.appendChild(lineSpan);
29142
+ footerDiv.appendChild(actionsDiv);
29143
+ bubbleDiv.appendChild(contentDiv);
29144
+ bubbleDiv.appendChild(footerDiv);
29145
+ el.appendChild(bubbleDiv);
29146
+ });
29147
+ }
29148
+ });
29149
+ if (common_default) {
29150
+ const codeEl = codeRef.current;
29151
+ if (codeEl) {
29152
+ codeEl.querySelectorAll(".ln-content").forEach((span) => {
29153
+ const text = span.textContent ?? "";
29154
+ try {
29155
+ const result = common_default.highlight(text, { language: file.language, ignoreIllegals: true });
29156
+ span.innerHTML = result.value;
29157
+ } catch {
29158
+ span.textContent = text;
29159
+ }
29160
+ });
29161
+ }
29162
+ }
29163
+ }, [file, comments, draft]);
29164
+ y2(() => {
29165
+ if (!codeRef.current || !file) return;
29166
+ const anchors = codeRef.current.querySelectorAll(".line-comment-anchor");
29167
+ anchors.forEach((anchor) => {
29168
+ const lineDiv = anchor.closest(".editor-line");
29169
+ if (!lineDiv) return;
29170
+ const lineNumber = parseInt(lineDiv.dataset.lineNumber || "0", 10);
29171
+ const isVisible = hoveredLine === lineNumber && (!draft || draft.file !== file.path || draft.lineNumber !== lineNumber);
29172
+ anchor.style.opacity = isVisible ? "1" : "0";
29173
+ anchor.style.pointerEvents = isVisible ? "auto" : "none";
29174
+ });
29175
+ }, [hoveredLine, draft, file]);
29176
+ if (!file) {
29177
+ return html6`
29178
+ <div class="editor-area" style=${{ display: "flex", alignItems: "center", justifyContent: "center" }}>
29179
+ <div class="empty">${t4("changes.viewerPlaceholder")}</div>
29180
+ </div>
29181
+ `;
29182
+ }
29183
+ return html6`
29184
+ <div class="editor-area" ref=${codeRef} />
29185
+ <div class="editor-status">
29186
+ <span class="glyph">◆</span>
29187
+ <span class="v">${file.name}</span>
29188
+ <span class="grow"></span>
29189
+ <span>${file.language}</span>
29190
+ <span class="v">${String(file.content.split("\n").length)} lines</span>
29191
+ </div>
29192
+ `;
29193
+ }
29194
+ function ChatPane(props) {
29195
+ useLang();
29196
+ const [messages, setMessages] = d2([]);
29197
+ const [streaming, setStreaming] = d2(null);
29198
+ const [activeTool, setActiveTool] = d2(null);
29199
+ const [busy, setBusy] = d2(false);
29200
+ const [input, setInput] = d2("");
29201
+ const [error, setError] = d2(null);
29202
+ const [statusLine, setStatusLine] = d2(null);
29203
+ const [stats, setStats] = d2(null);
29204
+ const [model, setModel] = d2(null);
29205
+ const shouldAutoScroll = A2(true);
29206
+ const feedRef = A2(null);
29207
+ const streamBufRef = A2(null);
29208
+ const streamRafRef = A2(null);
29209
+ const autoScrollInFlight = A2(false);
29210
+ const [slashCommands, setSlashCommands] = d2([]);
29211
+ const [popoverKind, setPopoverKind] = d2(null);
29212
+ const [popoverItems, setPopoverItems] = d2([]);
29213
+ const [popoverSel, setPopoverSel] = d2(0);
29214
+ y2(() => {
29215
+ let cancelled = false;
29216
+ (async () => {
29217
+ try {
29218
+ const r3 = await api("/slash");
29219
+ if (!cancelled) setSlashCommands(r3.commands);
29220
+ } catch {
29221
+ }
29222
+ })();
29223
+ return () => {
29224
+ cancelled = true;
29225
+ };
29226
+ }, []);
29227
+ y2(() => {
29228
+ let cancelled = false;
29229
+ (async () => {
29230
+ try {
29231
+ const data = await api("/messages");
29232
+ if (!cancelled) {
29233
+ setMessages(data.messages ?? []);
29234
+ setBusy(Boolean(data.busy));
29235
+ }
29236
+ } catch {
29237
+ if (!cancelled) setMessages([]);
29238
+ }
29239
+ })();
29240
+ return () => {
29241
+ cancelled = true;
29242
+ };
29243
+ }, []);
29244
+ y2(() => {
29245
+ let cancelled = false;
29246
+ const tick = async () => {
29247
+ try {
29248
+ const data = await api("/overview");
29249
+ if (cancelled) return;
29250
+ setStats(data.stats ?? null);
29251
+ setModel(data.model ?? null);
29252
+ } catch {
29253
+ }
29254
+ };
29255
+ tick();
29256
+ const t5 = setInterval(tick, 2500);
29257
+ return () => {
29258
+ cancelled = true;
29259
+ clearInterval(t5);
29260
+ };
29261
+ }, []);
29262
+ const flushStreaming = q2(() => {
29263
+ streamRafRef.current = null;
29264
+ if (streamBufRef.current) setStreaming(streamBufRef.current);
29265
+ }, []);
29266
+ const cancelStreamingRaf = q2(() => {
29267
+ if (streamRafRef.current !== null) {
29268
+ cancelAnimationFrame(streamRafRef.current);
29269
+ streamRafRef.current = null;
29270
+ }
29271
+ streamBufRef.current = null;
29272
+ }, []);
29273
+ const refetchCanonicalState = q2(async () => {
29274
+ try {
29275
+ const data = await api("/messages");
29276
+ setMessages(data.messages ?? []);
29277
+ setBusy(Boolean(data.busy));
29278
+ cancelStreamingRaf();
29279
+ setStreaming(null);
29280
+ setActiveTool(null);
29281
+ } catch {
29282
+ }
29283
+ }, [cancelStreamingRaf]);
29284
+ y2(() => {
29285
+ const es = new EventSource(`/api/events?token=${TOKEN}`);
29286
+ let firstOpen = true;
29287
+ es.onopen = () => {
29288
+ if (firstOpen) {
29289
+ firstOpen = false;
29290
+ return;
29291
+ }
29292
+ void refetchCanonicalState();
29293
+ };
29294
+ es.onmessage = (ev) => {
29295
+ let dash;
29296
+ try {
29297
+ dash = JSON.parse(ev.data);
29298
+ } catch {
29299
+ return;
29300
+ }
29301
+ if (dash.kind === "ping") return;
29302
+ if (dash.kind === "busy-change") {
29303
+ setBusy(dash.busy);
29304
+ return;
29305
+ }
29306
+ if (dash.kind === "user") {
29307
+ setMessages((prev) => [...prev, { id: dash.id, role: "user", text: dash.text }]);
29308
+ return;
29309
+ }
29310
+ if (dash.kind === "assistant_delta") {
29311
+ const cur = streamBufRef.current;
29312
+ const baseId = cur?.id === dash.id ? cur : null;
29313
+ streamBufRef.current = {
29314
+ id: dash.id,
29315
+ text: (baseId?.text ?? "") + (dash.contentDelta ?? ""),
29316
+ reasoning: (baseId?.reasoning ?? "") + (dash.reasoningDelta ?? "")
29317
+ };
29318
+ if (streamRafRef.current === null) {
29319
+ streamRafRef.current = requestAnimationFrame(flushStreaming);
29320
+ }
29321
+ return;
29322
+ }
29323
+ if (dash.kind === "assistant_final") {
29324
+ cancelStreamingRaf();
29325
+ setStreaming(null);
29326
+ setMessages((prev) => [
29327
+ ...prev,
29328
+ { id: dash.id, role: "assistant", text: dash.text, reasoning: dash.reasoning }
29329
+ ]);
29330
+ return;
29331
+ }
29332
+ if (dash.kind === "tool_start") {
29333
+ setActiveTool({ id: dash.id, toolName: dash.toolName, args: dash.args });
29334
+ return;
29335
+ }
29336
+ if (dash.kind === "tool") {
29337
+ setActiveTool((cur) => cur && cur.id === dash.id ? null : cur);
29338
+ setMessages((prev) => [
29339
+ ...prev,
29340
+ { id: dash.id, role: "tool", text: dash.content, toolName: dash.toolName, toolArgs: dash.args }
29341
+ ]);
29342
+ return;
29343
+ }
29344
+ if (dash.kind === "warning" || dash.kind === "error" || dash.kind === "info") {
29345
+ if (dash.kind === "error") setActiveTool(null);
29346
+ setMessages((prev) => [...prev, { id: dash.id, role: dash.kind, text: dash.text }]);
29347
+ return;
29348
+ }
29349
+ if (dash.kind === "status") {
29350
+ setStatusLine(dash.text);
29351
+ setTimeout(() => setStatusLine((cur) => cur === dash.text ? null : cur), 5e3);
29352
+ return;
29353
+ }
29354
+ };
29355
+ es.onerror = () => {
29356
+ setError(t4("chat.eventStreamError"));
29357
+ setTimeout(() => setError(null), 3e3);
29358
+ };
29359
+ return () => {
29360
+ es.close();
29361
+ cancelStreamingRaf();
29362
+ };
29363
+ }, [refetchCanonicalState, cancelStreamingRaf]);
29364
+ y2(() => {
29365
+ if (!shouldAutoScroll.current) return;
29366
+ const el = feedRef.current;
29367
+ if (!el) return;
29368
+ autoScrollInFlight.current = true;
29369
+ el.scrollTop = el.scrollHeight;
29370
+ setTimeout(() => {
29371
+ autoScrollInFlight.current = false;
29372
+ }, 0);
29373
+ }, [messages, streaming]);
29374
+ y2(() => {
29375
+ const el = feedRef.current;
29376
+ if (!el) return;
29377
+ const onScroll = () => {
29378
+ if (autoScrollInFlight.current) return;
29379
+ const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
29380
+ shouldAutoScroll.current = distFromBottom < 80;
29381
+ };
29382
+ el.addEventListener("scroll", onScroll, { passive: true });
29383
+ return () => el.removeEventListener("scroll", onScroll);
29384
+ }, []);
29385
+ const updatePopover = q2(
29386
+ async (text) => {
29387
+ const slashMatch = /^\/([A-Za-z0-9_-]*)$/.exec(text);
29388
+ if (slashMatch) {
29389
+ const prefix = slashMatch[1].toLowerCase();
29390
+ const items = slashCommands.filter((c3) => c3.cmd.startsWith(prefix)).slice(0, 12).map((c3) => ({
29391
+ label: `/${c3.cmd}`,
29392
+ meta: c3.summary,
29393
+ insert: `/${c3.cmd}${c3.argsHint ? " " : ""}`
29394
+ }));
29395
+ setPopoverKind("slash");
29396
+ setPopoverItems(items);
29397
+ setPopoverSel(0);
29398
+ return;
29399
+ }
29400
+ setPopoverKind(null);
29401
+ },
29402
+ [slashCommands]
29403
+ );
29404
+ const applyPopover = q2(() => {
29405
+ const item = popoverItems[popoverSel];
29406
+ if (!item) return false;
29407
+ setInput(item.insert);
29408
+ setPopoverKind(null);
29409
+ return true;
29410
+ }, [popoverItems, popoverSel, popoverKind, input]);
29411
+ const onInput = q2(
29412
+ (e3) => {
29413
+ const v3 = e3.target.value;
29414
+ setInput(v3);
29415
+ updatePopover(v3);
29416
+ },
29417
+ [updatePopover]
29418
+ );
29419
+ const send = q2(async () => {
29420
+ const text = input.trim();
29421
+ if (busy) return;
29422
+ if (!text && props.comments.length === 0) return;
29423
+ setError(null);
29424
+ let prompt = text;
29425
+ if (props.comments.length > 0) {
29426
+ const commentRefs = props.comments.map((c3) => `\u{1F4DD} ${c3.file}:${c3.lineNumber} ${c3.content}`).join("\n");
29427
+ prompt = text ? `${text}
29428
+
29429
+ ${commentRefs}` : commentRefs;
29430
+ }
29431
+ try {
29432
+ const res = await api("/submit", {
29433
+ method: "POST",
29434
+ body: { prompt }
29435
+ });
29436
+ if (!res.accepted) {
29437
+ setError(res.reason ?? "rejected");
29438
+ return;
29439
+ }
29440
+ setInput("");
29441
+ props.comments.forEach((c3) => props.deleteComment(c3.id));
29442
+ } catch (err) {
29443
+ setError(err.message);
29444
+ }
29445
+ }, [input, busy, props.comments]);
29446
+ const abort = q2(async () => {
29447
+ try {
29448
+ await api("/abort", { method: "POST" });
29449
+ } catch {
29450
+ }
29451
+ }, []);
29452
+ const newConversation = q2(async () => {
29453
+ if (busy) {
29454
+ if (!confirm(t4("changes.newConfirmBusy"))) return;
29455
+ } else if (messages.length > 0 && !confirm(t4("changes.newConfirm"))) {
29456
+ return;
29457
+ }
29458
+ try {
29459
+ await api("/submit", { method: "POST", body: { prompt: "/new" } });
29460
+ setMessages([]);
29461
+ setStreaming(null);
29462
+ setActiveTool(null);
29463
+ showToast(t4("changes.newToast"), "info");
29464
+ setTimeout(async () => {
29465
+ try {
29466
+ const r3 = await api("/messages");
29467
+ setMessages(r3.messages ?? []);
29468
+ } catch {
29469
+ }
29470
+ }, 200);
29471
+ } catch (err) {
29472
+ setError(t4("changes.newFailed", { error: err.message }));
29473
+ }
29474
+ }, [busy, messages.length]);
29475
+ const clearScrollback = q2(async () => {
29476
+ try {
29477
+ await api("/submit", { method: "POST", body: { prompt: "/clear" } });
29478
+ setMessages([]);
29479
+ setStreaming(null);
29480
+ setActiveTool(null);
29481
+ showToast(t4("changes.clearToast"), "info");
29482
+ setTimeout(async () => {
29483
+ try {
29484
+ const r3 = await api("/messages");
29485
+ setMessages(r3.messages ?? []);
29486
+ } catch {
29487
+ }
29488
+ }, 200);
29489
+ } catch (err) {
29490
+ setError(t4("changes.clearFailed", { error: err.message }));
29491
+ }
29492
+ }, []);
29493
+ const onKeyDown = q2((e3) => {
29494
+ if (popoverKind && popoverItems.length > 0) {
29495
+ if (e3.key === "ArrowDown") {
29496
+ e3.preventDefault();
29497
+ setPopoverSel((i3) => (i3 + 1) % popoverItems.length);
29498
+ return;
29499
+ }
29500
+ if (e3.key === "ArrowUp") {
29501
+ e3.preventDefault();
29502
+ setPopoverSel((i3) => (i3 - 1 + popoverItems.length) % popoverItems.length);
29503
+ return;
29504
+ }
29505
+ if (e3.key === "Tab" || e3.key === "Enter" && !e3.shiftKey) {
29506
+ e3.preventDefault();
29507
+ if (applyPopover() && e3.key === "Enter" && popoverKind === "slash") send();
29508
+ return;
29509
+ }
29510
+ if (e3.key === "Escape") {
29511
+ e3.preventDefault();
29512
+ setPopoverKind(null);
29513
+ return;
29514
+ }
29515
+ }
29516
+ if (e3.key === "Escape" && busy) {
29517
+ e3.preventDefault();
29518
+ abort();
29519
+ return;
29520
+ }
29521
+ if (e3.key === "Enter" && !e3.shiftKey) {
29522
+ e3.preventDefault();
29523
+ send();
29524
+ }
29525
+ }, [send, abort, busy, popoverKind, popoverItems, applyPopover]);
29526
+ const allMessages = streaming ? [...messages, { id: streaming.id, role: "assistant", text: streaming.text, reasoning: streaming.reasoning }] : messages;
29527
+ return html6`
29528
+ <div style=${{ display: "flex", flexDirection: "column", height: "100%" }}>
29529
+ ${statusLine ? html6`<div class="changes-panel-header"><span>${statusLine}</span></div>` : null}
29530
+ <div class="chat-feed" style=${{ flex: 1, overflowY: "auto", padding: "8px" }} ref=${feedRef}>
29531
+ ${allMessages.length === 0 && !streaming ? html6`<div class="empty" style=${{ margin: "12px", textAlign: "center" }}>${t4("changes.chatWelcome")}</div>` : null}
29532
+ ${allMessages.map((msg) => {
29533
+ const isStreaming = streaming && msg.id === streaming.id;
29534
+ if (msg.role === "tool") {
29535
+ return html6`
29536
+ <div class="chat-msg tool" key=${msg.id}>
29537
+ <div class="glyph">▣</div>
29538
+ <${ToolCard} msg=${msg} />
29539
+ </div>
29540
+ `;
29541
+ }
29542
+ return html6`
29543
+ <${ChatMessage}
29544
+ key=${msg.id}
29545
+ msg=${{ id: msg.id, role: msg.role, text: msg.text, reasoning: msg.reasoning, toolName: msg.toolName, toolArgs: msg.toolArgs }}
29546
+ streaming=${Boolean(isStreaming)}
29547
+ />
29548
+ `;
29549
+ })}
29550
+ </div>
29551
+ ${error ? html6`<div class="notice err" style=${{ margin: "0 8px 4px" }}>${error}</div>` : null}
29552
+ <div style=${{ padding: "8px", borderTop: "1px solid var(--bd)", flexShrink: 0 }}>
29553
+ ${props.comments.length > 0 ? html6`
29554
+ <div class="comment-cards-container" style=${{ display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }}>
29555
+ ${props.comments.map((comment) => html6`
29556
+ <${CommentCard}
29557
+ key=${comment.id}
29558
+ fileName=${comment.file}
29559
+ lineNumber=${comment.lineNumber}
29560
+ content=${comment.content}
29561
+ onRemove=${() => props.deleteComment(comment.id)}
29562
+ />
29563
+ `)}
29564
+ </div>
29565
+ ` : null}
29566
+ <div style=${{ display: "flex", gap: "8px", alignItems: "flex-end", position: "relative" }}>
29567
+ <div style=${{ flex: 1, position: "relative" }}>
29568
+ ${popoverKind && popoverItems.length > 0 ? html6`
29569
+ <div class="popover" style="position:absolute;bottom:calc(100% + 6px);left:0;width:380px;max-height:280px;overflow-y:auto;z-index:10">
29570
+ <div class="popover-h">${t4("chat.slashCommands")}</div>
29571
+ ${popoverItems.map(
29572
+ (it, i3) => html6`
29573
+ <div
29574
+ class=${`popover-row ${i3 === popoverSel ? "sel" : ""}`}
29575
+ onMouseDown=${(e3) => {
29576
+ e3.preventDefault();
29577
+ setPopoverSel(i3);
29578
+ applyPopover();
29579
+ }}
29580
+ >
29581
+ <span class="g">/</span>
29582
+ <span class="name">${it.label}</span>
29583
+ ${it.meta ? html6`<span class="meta">${it.meta}</span>` : null}
29584
+ </div>
29585
+ `
29586
+ )}
29587
+ </div>
29588
+ ` : null}
29589
+ <textarea
29590
+ class="input"
29591
+ style=${{ width: "100%", resize: "none", minHeight: "36px", fontFamily: "inherit", fontSize: "13px", padding: "8px 10px", lineHeight: "1.4", background: "var(--bg-input)", border: "1px solid var(--bd)", borderRadius: "4px", color: "var(--fg-0)" }}
29592
+ placeholder=${props.comments.length > 0 ? "\u603B\u7ED3\u8BC4\u8BBA..." : t4("changes.chatPlaceholder")}
29593
+ value=${input}
29594
+ onInput=${onInput}
29595
+ onKeyDown=${onKeyDown}
29596
+ onBlur=${() => setTimeout(() => setPopoverKind(null), 150)}
29597
+ rows="2"
29598
+ />
29599
+ </div>
29600
+ <div style=${{ display: "flex", flexDirection: "column", gap: "6px", flexShrink: 0 }}>
29601
+ <button class="primary" onClick=${send} disabled=${busy || !input.trim() && props.comments.length === 0} style=${{ padding: "8px 12px", borderRadius: "4px" }}>${t4("changes.chatSend")}</button>
29602
+ <div style=${{ display: "flex", gap: "6px" }}>
29603
+ <button onClick=${newConversation} title=${t4("changes.newTitle")}>${t4("changes.newConversation")}</button>
29604
+ <button onClick=${clearScrollback} title=${t4("changes.clearTitle")}>${t4("changes.clearConversation")}</button>
29605
+ </div>
29606
+ </div>
29607
+ </div>
29608
+ </div>
29609
+ <${ChatStatusBar3} stats=${stats} model=${model} />
29610
+ </div>
29611
+ `;
29612
+ }
29613
+
29614
+ // dashboard/app.js
29615
+ var html7 = htm_module_default.bind(k);
29616
+ function tabSections() {
29617
+ return [
29618
+ {
29619
+ label: t4("app.sectionWorkspace"),
29620
+ tabs: [
29621
+ { id: "chat", name: t4("app.tabChat"), glyph: "\u25C6", panel: () => html7`<${ChatPanel} />` },
29622
+ { id: "plans", name: t4("app.tabPlans"), glyph: "\u229E", panel: () => html7`<${PlansPanel} />` },
29623
+ { id: "sessions", name: t4("app.tabSessions"), glyph: "\u203A", panel: () => html7`<${SessionsPanel} />` }
29624
+ ]
29625
+ },
29626
+ {
29627
+ label: t4("app.sectionChanges"),
27776
29628
  tabs: [
27777
- { id: "chat", name: t4("app.tabChat"), glyph: "\u25C6", panel: () => html5`<${ChatPanel} />` },
27778
- { id: "plans", name: t4("app.tabPlans"), glyph: "\u229E", panel: () => html5`<${PlansPanel} />` },
27779
- { id: "sessions", name: t4("app.tabSessions"), glyph: "\u203A", panel: () => html5`<${SessionsPanel} />` }
29629
+ { id: "changes", name: t4("app.tabChanges"), glyph: "\u25A8", panel: () => html7`<${ChangesPanel} />` }
27780
29630
  ]
27781
29631
  },
27782
29632
  {
27783
29633
  label: t4("app.sectionObserve"),
27784
29634
  tabs: [
27785
- { id: "overview", name: t4("app.tabOverview"), glyph: "\u25C8", panel: () => html5`<${OverviewPanel} />` },
27786
- { id: "usage", name: t4("app.tabUsage"), glyph: "$", panel: () => html5`<${UsagePanel} />` },
27787
- { id: "health", name: t4("app.tabSystem"), glyph: "+", panel: () => html5`<${SystemPanel} />` },
27788
- { id: "semantic", name: t4("app.tabSemantic"), glyph: "\u2248", panel: () => html5`<${SemanticPanel} />` }
29635
+ { id: "overview", name: t4("app.tabOverview"), glyph: "\u25C8", panel: () => html7`<${OverviewPanel} />` },
29636
+ { id: "usage", name: t4("app.tabUsage"), glyph: "$", panel: () => html7`<${UsagePanel} />` },
29637
+ { id: "health", name: t4("app.tabSystem"), glyph: "+", panel: () => html7`<${SystemPanel} />` },
29638
+ { id: "semantic", name: t4("app.tabSemantic"), glyph: "\u2248", panel: () => html7`<${SemanticPanel} />` }
27789
29639
  ]
27790
29640
  },
27791
29641
  {
27792
29642
  label: t4("app.sectionConfigure"),
27793
29643
  tabs: [
27794
- { id: "tools", name: t4("app.tabTools"), glyph: "\u25A3", panel: () => html5`<${ToolsPanel} />` },
27795
- { id: "permissions", name: t4("app.tabPermissions"), glyph: "\u258E", panel: () => html5`<${PermissionsPanel} />` },
27796
- { id: "mcp", name: t4("app.tabMcp"), glyph: "M", panel: () => html5`<${McpPanel} />` },
27797
- { id: "skills", name: t4("app.tabSkills"), glyph: "S", panel: () => html5`<${SkillsPanel} />` },
27798
- { id: "memory", name: t4("app.tabMemory"), glyph: "\xB7", panel: () => html5`<${MemoryPanel} />` },
27799
- { id: "hooks", name: t4("app.tabHooks"), glyph: "H", panel: () => html5`<${HooksPanel} />` },
27800
- { id: "settings", name: t4("app.tabSettings"), glyph: "\u2318", panel: () => html5`<${SettingsPanel} />` }
29644
+ { id: "tools", name: t4("app.tabTools"), glyph: "\u25A3", panel: () => html7`<${ToolsPanel} />` },
29645
+ { id: "permissions", name: t4("app.tabPermissions"), glyph: "\u258E", panel: () => html7`<${PermissionsPanel} />` },
29646
+ { id: "mcp", name: t4("app.tabMcp"), glyph: "M", panel: () => html7`<${McpPanel} />` },
29647
+ { id: "skills", name: t4("app.tabSkills"), glyph: "S", panel: () => html7`<${SkillsPanel} />` },
29648
+ { id: "memory", name: t4("app.tabMemory"), glyph: "\xB7", panel: () => html7`<${MemoryPanel} />` },
29649
+ { id: "hooks", name: t4("app.tabHooks"), glyph: "H", panel: () => html7`<${HooksPanel} />` },
29650
+ { id: "settings", name: t4("app.tabSettings"), glyph: "\u2318", panel: () => html7`<${SettingsPanel} />` }
27801
29651
  ]
27802
29652
  }
27803
29653
  ];
@@ -27848,7 +29698,7 @@ function App() {
27848
29698
  return () => appBus.removeEventListener("navigate-tab", onNav);
27849
29699
  }, []);
27850
29700
  const pickTab = q2((id) => setActiveId(id), []);
27851
- return html5`
29701
+ return html7`
27852
29702
  <div class=${`app ${sidebarCollapsed ? "collapsed" : ""}`}>
27853
29703
  <aside class="app-side">
27854
29704
  <div class="brand">
@@ -27858,10 +29708,10 @@ function App() {
27858
29708
  </div>
27859
29709
  <div class="side-tabs">
27860
29710
  ${TAB_SECTIONS.map(
27861
- (section) => html5`
29711
+ (section) => html7`
27862
29712
  <div class="side-section">${section.label}</div>
27863
29713
  ${section.tabs.map(
27864
- (tab) => html5`
29714
+ (tab) => html7`
27865
29715
  <div
27866
29716
  class=${`side-tab ${tab.id === active.id ? "active" : ""}`}
27867
29717
  onClick=${() => pickTab(tab.id)}
@@ -27904,5 +29754,5 @@ function App() {
27904
29754
  <${ErrorOverlay} />
27905
29755
  `;
27906
29756
  }
27907
- R(html5`<${App} />`, document.getElementById("root"));
29757
+ R(html7`<${App} />`, document.getElementById("root"));
27908
29758
  //# sourceMappingURL=app.js.map