@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/openvulcan-icon.svg +33 -0
  4. package/cordis.patch.yml +19 -0
  5. package/node_modules/@openvulcan/dsh-decision-guard/LICENSE +21 -0
  6. package/node_modules/@openvulcan/dsh-decision-guard/README.md +14 -0
  7. package/node_modules/@openvulcan/dsh-decision-guard/assets/decision-guard-icon.png +0 -0
  8. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.d.ts +40 -0
  9. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.js +150 -0
  10. package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +2250 -0
  11. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.d.ts +90 -0
  12. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.js +108 -0
  13. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.d.ts +129 -0
  14. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.js +572 -0
  15. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.d.ts +22 -0
  16. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.js +77 -0
  17. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.d.ts +158 -0
  18. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.js +340 -0
  19. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.d.ts +133 -0
  20. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.js +186 -0
  21. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.d.ts +55 -0
  22. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.js +202 -0
  23. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.d.ts +62 -0
  24. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.js +155 -0
  25. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.d.ts +2 -0
  26. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.js +4 -0
  27. package/node_modules/@openvulcan/dsh-decision-guard/package.json +68 -0
  28. package/node_modules/@openvulcan/dsh-vulcan-account/LICENSE +21 -0
  29. package/node_modules/@openvulcan/dsh-vulcan-account/assets/account-icon.png +0 -0
  30. package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +573 -0
  31. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.d.ts +92 -0
  32. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.js +194 -0
  33. package/node_modules/@openvulcan/dsh-vulcan-account/package.json +57 -0
  34. package/node_modules/@openvulcan/dsh-vulcan-answers/LICENSE +21 -0
  35. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/answers-icon.png +0 -0
  36. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-interactive.md +79 -0
  37. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-standard.md +1 -0
  38. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.d.ts +258 -0
  39. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.js +698 -0
  40. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +671 -0
  41. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.d.ts +29 -0
  42. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.js +70 -0
  43. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.d.ts +19 -0
  44. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.js +52 -0
  45. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.d.ts +9 -0
  46. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.js +9 -0
  47. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.d.ts +22 -0
  48. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.js +39 -0
  49. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.d.ts +20 -0
  50. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.js +17 -0
  51. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.d.ts +66 -0
  52. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.js +118 -0
  53. package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +59 -0
  54. package/node_modules/@openvulcan/dsh-vulcan-luaskills/LICENSE +21 -0
  55. package/node_modules/@openvulcan/dsh-vulcan-luaskills/assets/luaskills-icon.svg +1 -0
  56. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +949 -0
  57. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.d.ts +46 -0
  58. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.js +51 -0
  59. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.d.ts +55 -0
  60. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.js +149 -0
  61. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.d.ts +49 -0
  62. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.js +116 -0
  63. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.d.ts +26 -0
  64. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.js +276 -0
  65. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.d.ts +65 -0
  66. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.js +2 -0
  67. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.d.ts +111 -0
  68. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.js +281 -0
  69. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.d.ts +26 -0
  70. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.js +87 -0
  71. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.d.ts +79 -0
  72. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.js +165 -0
  73. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.d.ts +34 -0
  74. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.js +61 -0
  75. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.d.ts +59 -0
  76. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.js +107 -0
  77. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.d.ts +10 -0
  78. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.js +140 -0
  79. package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +57 -0
  80. package/node_modules/@openvulcan/dsh-vulcan-memory/LICENSE +21 -0
  81. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +21 -0
  82. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +31 -0
  83. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-icon.png +0 -0
  84. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/client.js +1090 -0
  85. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.d.ts +30 -0
  86. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.js +240 -0
  87. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.d.ts +30 -0
  88. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.js +46 -0
  89. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.d.ts +23 -0
  90. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.js +25 -0
  91. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.d.ts +108 -0
  92. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.js +373 -0
  93. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +14 -0
  94. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +31 -0
  95. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +196 -0
  96. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +204 -0
  97. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.d.ts +92 -0
  98. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +472 -0
  99. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.d.ts +126 -0
  100. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.js +553 -0
  101. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.d.ts +56 -0
  102. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.js +182 -0
  103. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.d.ts +13 -0
  104. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.js +24 -0
  105. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.d.ts +65 -0
  106. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.js +227 -0
  107. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.d.ts +34 -0
  108. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.js +171 -0
  109. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.d.ts +72 -0
  110. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.js +250 -0
  111. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.d.ts +6 -0
  112. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.js +12 -0
  113. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.d.ts +8 -0
  114. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.js +38 -0
  115. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.d.ts +132 -0
  116. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.js +235 -0
  117. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.d.ts +14 -0
  118. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.js +27 -0
  119. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.d.ts +29 -0
  120. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.js +62 -0
  121. package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +63 -0
  122. package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
  123. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
  124. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
  125. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
  126. package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
  127. package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
  128. package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
  129. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1121 -0
  130. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
  131. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
  132. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
  133. package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
  134. package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
  135. package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
  136. package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
  137. package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
  138. package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
  139. package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
  140. package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
  141. package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
  142. package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
  143. package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
  144. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
  145. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
  146. package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
  147. package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
  148. package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
  149. package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
  150. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
  151. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
  152. package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
  153. package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
  154. package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
  155. package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
  156. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
  157. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
  158. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
  159. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
  160. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
  161. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
  162. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
  163. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
  164. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
  165. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
  166. package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
  167. package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
  168. package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
  169. package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
  170. package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
  171. package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
  172. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
  173. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
  174. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
  175. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
  176. package/package.json +55 -4
@@ -0,0 +1,1090 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@openvulcan/dsh-vulcan-memory",
3
+ factory: (platformRequire) => {
4
+ var definitions = {
5
+ "./dsh-client": function (module, exports, require) {
6
+ "use strict";
7
+ /**
8
+ * The parts of dsh's browser runtime this client half uses.
9
+ *
10
+ * dsh serves these modules from its page module table; their packages are not published at the
11
+ * Host's version, so the shapes are restated here from dsh 0.2.1-rc.2 source (the same subset
12
+ * `dsh-vulcan-luaskills/client/dsh-client.ts` restates) and must be checked again when the
13
+ * supported dsh range moves:
14
+ * - `packages/client/ui-layout/src/client/index.ts` (the keyed `main` slot)
15
+ * - `packages/client/ui-sidebar/src/client/contract/slots.ts` (`sidebar.panellist`)
16
+ * - `packages/client/locale/src/client/index.ts` (`bind`)
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+
20
+ },
21
+ "./i18n": function (module, exports, require) {
22
+ "use strict";
23
+ /**
24
+ * The plugin's interface text; dsh picks the dictionary that matches its interface language.
25
+ * Wording follows VulcanCode's `memory.markdown.*` catalog entries (`crates/vulcan-i18n/catalogs/webui`).
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.en = exports.zh = exports.NAMESPACE = void 0;
29
+ exports.fill = fill;
30
+ exports.reasonLabel = reasonLabel;
31
+ exports.NAMESPACE = 'vulcan-memory';
32
+ exports.zh = {
33
+ 'panel': '长期记忆',
34
+ 'page.lead': 'AI 维护的 Markdown 长期记忆:用户画像、所有项目共用的空间记忆,以及每个项目自己的记忆。',
35
+ 'loading': '正在加载记忆',
36
+ 'loadFailed': '无法加载记忆',
37
+ 'writeFailed': '无法修改记忆',
38
+ 'retry': '重试',
39
+ 'cancel': '取消',
40
+ 'save': '保存',
41
+ 'saved': '记忆设置已保存',
42
+ 'saveFailed': '无法保存记忆设置',
43
+ 'nav.mode': '记忆模式',
44
+ 'nav.user': '用户画像',
45
+ 'nav.space': '空间',
46
+ 'nav.project': '项目',
47
+ 'detail.mode': '选择长期记忆系统与 Markdown 注入预算。',
48
+ 'detail.user': '你是谁、喜欢怎样协作。全文注入每个会话。',
49
+ 'detail.space': '所有项目共用的规则与环境。基础区全文注入,其余只注入标题。',
50
+ 'detail.project': '单个项目的事实。基础区全文注入,其余只注入标题。',
51
+ 'mode.title': '记忆模式',
52
+ 'mode.hint': '所有项目共用一种记忆系统。工具在下一次请求生效,注入内容在新会话或压缩后生效。',
53
+ 'mode.off': '关闭',
54
+ 'mode.offHint': '不注入也不写入长期记忆。',
55
+ 'mode.vmm': 'VMM',
56
+ 'mode.vmmHint': 'VulcanMemoryMesh 尚未接入 dsh:选择它时不注入也不写入长期记忆。',
57
+ 'mode.markdown': 'Markdown',
58
+ 'mode.markdownHint': '由 AI 维护可读的 Markdown 笔记,你可以在这里查看和编辑。',
59
+ 'budget.title': '注入预算',
60
+ 'budget.hint': '每个注入部分的 Token 数。超出预算的部分会被缩减;目录先舍去较深的标题层级。',
61
+ 'budget.invalid': '每项必须是 0 到 {max} 之间的整数。',
62
+ 'budget.total': '合计:{tokens} Token',
63
+ 'budget.reset': '恢复默认',
64
+ 'part.user': '用户画像',
65
+ 'part.spaceBase': '空间基础区',
66
+ 'part.projectBase': '项目基础区',
67
+ 'part.spaceCatalog': '空间目录',
68
+ 'part.projectCatalog': '项目目录',
69
+ 'projects.title': '记忆项目',
70
+ 'projects.hint': '关闭某项目的记忆后,该项目的会话不使用用户、空间与项目记忆;文件会保留。',
71
+ 'projects.empty': '还没有记忆项目。写入第一条项目记忆时会自动创建。',
72
+ 'projects.enabled': '本项目使用记忆',
73
+ 'projects.orphaned': '文件夹已不存在',
74
+ 'project.select': '记忆项目',
75
+ 'project.otherGroup': '未关联到已打开的项目',
76
+ 'project.none': '打开项目后即可查看其记忆。',
77
+ 'project.unbound': '该项目还没有记忆,添加第一条笔记时会创建。',
78
+ 'project.disabled': '该项目已关闭记忆,会话不会使用它;你仍可在此编辑。',
79
+ 'orphan.title': '是否为移动后文件夹的记忆?',
80
+ 'orphan.body': '“{name}”记录的文件夹已不存在,且与当前项目相似({reasons})。',
81
+ 'orphan.link': '沿用这份记忆',
82
+ 'orphan.create': '重新开始',
83
+ 'orphan.ignore': '忽略',
84
+ 'reason.same_directory_name': '文件夹同名',
85
+ 'reason.same_first_commit': '首次提交相同',
86
+ 'reason.same_path': '相同文件夹',
87
+ 'usage.title': '注入占用',
88
+ 'usage.value': '{tokens} / {budget} Token',
89
+ 'usage.over': '超出预算',
90
+ 'usage.near': '接近上限',
91
+ 'usage.omitted': '未注入:{titles}',
92
+ 'usage.curateHint': '如需腾出空间,可在会话中让 AI 使用 memory-curator 技能整理记忆,或把不太重要的笔记移出基础区。',
93
+ 'nodes.empty': '这里还没有记忆。',
94
+ 'node.add': '添加笔记',
95
+ 'node.edit': '编辑',
96
+ 'node.move': '移动',
97
+ 'node.delete': '删除',
98
+ 'node.base': '全文注入',
99
+ 'node.sourceAi': 'AI 写入',
100
+ 'node.sourceUser': '人工编辑',
101
+ 'node.updated': '更新于 {time}',
102
+ 'node.deleteConfirm': '删除“{title}”?',
103
+ 'node.deleteChildren': '其下 {count} 条子笔记也会一并删除。',
104
+ 'editor.addTitle': '添加笔记',
105
+ 'editor.editTitle': '编辑笔记',
106
+ 'editor.moveTitle': '移动笔记',
107
+ 'editor.scope': '层级',
108
+ 'editor.section': '分区',
109
+ 'editor.title': '标题',
110
+ 'editor.body': '内容',
111
+ 'file.edit': '编辑文件',
112
+ 'file.title': '编辑记忆文件',
113
+ 'file.hint': '请保留每个标题下的注释行,它记录笔记标识与时间。',
114
+ 'file.saved': '记忆文件已保存',
115
+ 'transfer.title': '导入与导出',
116
+ 'transfer.hint': '以 .zip 导出包在电脑之间迁移记忆,与 VulcanCode 的导出包互通。导入按笔记合并,冲突由你决定。',
117
+ 'transfer.export': '导出…',
118
+ 'transfer.import': '导入…',
119
+ 'transfer.exportTitle': '导出记忆',
120
+ 'transfer.exportHint': '选择要放入导出包的层,导出包会由浏览器下载。',
121
+ 'transfer.download': '下载导出包',
122
+ 'transfer.exported': '记忆已导出:{name}',
123
+ 'transfer.projectScope': '项目:{name}',
124
+ 'transfer.previewing': '正在读取导出包…',
125
+ 'transfer.importTitle': '导入记忆',
126
+ 'transfer.importHint': '导出于 {time}。应用前不会有任何改动;被替换的文件会先备份。',
127
+ 'transfer.projectsHint': '为包内每个项目选择去向:已有记忆项目、新建项目,或不导入。',
128
+ 'transfer.targetFor': '{name} 的去向',
129
+ 'transfer.targetUnset': '选择去向',
130
+ 'transfer.linkCandidate': '合并到“{name}”({reasons})',
131
+ 'transfer.link': '合并到“{name}”',
132
+ 'transfer.create': '新建记忆项目',
133
+ 'transfer.ignore': '不导入',
134
+ 'transfer.createRest': '其余新建',
135
+ 'transfer.ignoreRest': '其余不导入',
136
+ 'transfer.next': '检查变更',
137
+ 'transfer.back': '返回',
138
+ 'transfer.apply': '应用导入',
139
+ 'transfer.done': '完成',
140
+ 'transfer.nothing': '按当前去向,导出包中没有可导入的内容。',
141
+ 'transfer.counts': '{same} 条相同 · {added} 条新增 · {conflicts} 个冲突',
142
+ 'transfer.newSections': '新分区:{titles}',
143
+ 'transfer.duplicates': '本地已有同名但标识不同的笔记,两者都会保留:{titles}',
144
+ 'transfer.newerImported': '导入的较新',
145
+ 'transfer.newerLocal': '本地的较新',
146
+ 'transfer.choice.keep_local': '保留本地',
147
+ 'transfer.choice.take_imported': '使用导入',
148
+ 'transfer.choice.keep_both': '两者都保留',
149
+ 'transfer.compare': '对比',
150
+ 'transfer.local': '本地',
151
+ 'transfer.imported': '导入',
152
+ 'transfer.report': '新增 {added} 条,替换 {replaced} 条,未变 {unchanged} 条。',
153
+ 'transfer.reportCreated': '新建了 {count} 个记忆项目。',
154
+ 'transfer.reportBackup': '被替换的文件已备份到 ',
155
+ };
156
+ exports.en = {
157
+ 'panel': 'Long-term memory',
158
+ 'page.lead': 'Markdown long-term memory kept by the AI: your profile, space memory shared by every project, and each project\'s own memory.',
159
+ 'loading': 'Loading memory',
160
+ 'loadFailed': 'Could not load memory',
161
+ 'writeFailed': 'Could not change memory',
162
+ 'retry': 'Retry',
163
+ 'cancel': 'Cancel',
164
+ 'save': 'Save',
165
+ 'saved': 'Memory settings saved',
166
+ 'saveFailed': 'Could not save memory settings',
167
+ 'nav.mode': 'Memory mode',
168
+ 'nav.user': 'User profile',
169
+ 'nav.space': 'Space',
170
+ 'nav.project': 'Project',
171
+ 'detail.mode': 'Choose the long-term memory system and the Markdown injection budget.',
172
+ 'detail.user': 'Who you are and how you like to work. Injected in full into every conversation.',
173
+ 'detail.space': 'Rules and environment shared by every project. The base section is injected in full, the rest as titles.',
174
+ 'detail.project': 'Facts about one project. The base section is injected in full, the rest as titles.',
175
+ 'mode.title': 'Memory mode',
176
+ 'mode.hint': 'One memory system serves every project. Tools change on the next request, injected memory with new or compacted conversations.',
177
+ 'mode.off': 'Off',
178
+ 'mode.offHint': 'No long-term memory is injected or written.',
179
+ 'mode.vmm': 'VMM',
180
+ 'mode.vmmHint': 'VulcanMemoryMesh is not available in dsh yet: while it is selected, no long-term memory is injected or written.',
181
+ 'mode.markdown': 'Markdown',
182
+ 'mode.markdownHint': 'The AI keeps readable Markdown notes that you can review and edit here.',
183
+ 'budget.title': 'Injection budget',
184
+ 'budget.hint': 'Tokens per injected part. Parts over budget are shortened; catalogs drop deeper heading levels first.',
185
+ 'budget.invalid': 'Each part must be a whole number from 0 to {max}.',
186
+ 'budget.total': 'Total: {tokens} tokens',
187
+ 'budget.reset': 'Restore defaults',
188
+ 'part.user': 'User profile',
189
+ 'part.spaceBase': 'Space base',
190
+ 'part.projectBase': 'Project base',
191
+ 'part.spaceCatalog': 'Space catalog',
192
+ 'part.projectCatalog': 'Project catalog',
193
+ 'projects.title': 'Memory projects',
194
+ 'projects.hint': 'Turning memory off for a project hides user, space and project memory in its conversations. The files are kept.',
195
+ 'projects.empty': 'No memory project yet. One is created when the first project note is written.',
196
+ 'projects.enabled': 'Use memory in this project',
197
+ 'projects.orphaned': 'Folder missing',
198
+ 'project.select': 'Memory project',
199
+ 'project.otherGroup': 'Not linked to an open project',
200
+ 'project.none': 'Open a project to see its memory.',
201
+ 'project.unbound': 'This project has no memory yet. The first note creates it.',
202
+ 'project.disabled': 'Memory is off for this project, so conversations do not use it. You can still edit it here.',
203
+ 'orphan.title': 'Memory from a moved folder?',
204
+ 'orphan.body': '“{name}” was recorded at a folder that no longer exists and looks like this project ({reasons}).',
205
+ 'orphan.link': 'Use this memory',
206
+ 'orphan.create': 'Start fresh',
207
+ 'orphan.ignore': 'Dismiss',
208
+ 'reason.same_directory_name': 'same folder name',
209
+ 'reason.same_first_commit': 'same first commit',
210
+ 'reason.same_path': 'same folder',
211
+ 'usage.title': 'Injection usage',
212
+ 'usage.value': '{tokens} / {budget} tokens',
213
+ 'usage.over': 'Over budget',
214
+ 'usage.near': 'Nearly full',
215
+ 'usage.omitted': 'Left out: {titles}',
216
+ 'usage.curateHint': 'To make room, ask the AI in a conversation to tidy memory with the memory-curator skill, or move less important notes out of the base section.',
217
+ 'nodes.empty': 'Nothing remembered here yet.',
218
+ 'node.add': 'Add note',
219
+ 'node.edit': 'Edit',
220
+ 'node.move': 'Move',
221
+ 'node.delete': 'Delete',
222
+ 'node.base': 'Injected in full',
223
+ 'node.sourceAi': 'Written by AI',
224
+ 'node.sourceUser': 'Edited by you',
225
+ 'node.updated': 'Updated {time}',
226
+ 'node.deleteConfirm': 'Delete “{title}”?',
227
+ 'node.deleteChildren': 'Its {count} nested notes are deleted too.',
228
+ 'editor.addTitle': 'Add note',
229
+ 'editor.editTitle': 'Edit note',
230
+ 'editor.moveTitle': 'Move note',
231
+ 'editor.scope': 'Layer',
232
+ 'editor.section': 'Section',
233
+ 'editor.title': 'Title',
234
+ 'editor.body': 'Content',
235
+ 'file.edit': 'Edit file',
236
+ 'file.title': 'Edit memory file',
237
+ 'file.hint': 'Keep the comment line under each heading; it holds the note id and timestamps.',
238
+ 'file.saved': 'Memory file saved',
239
+ 'transfer.title': 'Import and export',
240
+ 'transfer.hint': 'Move memory between computers as a .zip package, compatible with VulcanCode packages. Imports merge note by note, and conflicts are yours to resolve.',
241
+ 'transfer.export': 'Export…',
242
+ 'transfer.import': 'Import…',
243
+ 'transfer.exportTitle': 'Export memory',
244
+ 'transfer.exportHint': 'Choose the layers to put in the package; the browser downloads it.',
245
+ 'transfer.download': 'Download package',
246
+ 'transfer.exported': 'Memory exported: {name}',
247
+ 'transfer.projectScope': 'Project: {name}',
248
+ 'transfer.previewing': 'Reading package…',
249
+ 'transfer.importTitle': 'Import memory',
250
+ 'transfer.importHint': 'Exported at {time}. Nothing changes until you apply; replaced files are backed up first.',
251
+ 'transfer.projectsHint': 'Choose where each packaged project goes: an existing memory project, a new one, or nowhere.',
252
+ 'transfer.targetFor': 'Target for {name}',
253
+ 'transfer.targetUnset': 'Choose a target',
254
+ 'transfer.linkCandidate': 'Merge into “{name}” ({reasons})',
255
+ 'transfer.link': 'Merge into “{name}”',
256
+ 'transfer.create': 'Create a new memory project',
257
+ 'transfer.ignore': 'Do not import',
258
+ 'transfer.createRest': 'Create the rest',
259
+ 'transfer.ignoreRest': 'Skip the rest',
260
+ 'transfer.next': 'Review changes',
261
+ 'transfer.back': 'Back',
262
+ 'transfer.apply': 'Apply import',
263
+ 'transfer.done': 'Done',
264
+ 'transfer.nothing': 'The package holds nothing to import with these targets.',
265
+ 'transfer.counts': '{same} unchanged · {added} to add · {conflicts} conflicts',
266
+ 'transfer.newSections': 'New sections: {titles}',
267
+ 'transfer.duplicates': 'Also present locally under another id, so both are kept: {titles}',
268
+ 'transfer.newerImported': 'Imported is newer',
269
+ 'transfer.newerLocal': 'Local is newer',
270
+ 'transfer.choice.keep_local': 'Keep local',
271
+ 'transfer.choice.take_imported': 'Use imported',
272
+ 'transfer.choice.keep_both': 'Keep both',
273
+ 'transfer.compare': 'Compare',
274
+ 'transfer.local': 'Local',
275
+ 'transfer.imported': 'Imported',
276
+ 'transfer.report': 'Added {added}, replaced {replaced}, unchanged {unchanged}.',
277
+ 'transfer.reportCreated': 'Created {count} memory projects.',
278
+ 'transfer.reportBackup': 'Replaced files were backed up to ',
279
+ };
280
+ /** Replace `{name}` placeholders. */
281
+ function fill(text, values) {
282
+ return text.replace(/\{(\w+)\}/g, (match, name) => (name in values ? String(values[name]) : match));
283
+ }
284
+ /** Translate a stable reason key, keeping unknown keys readable. */
285
+ function reasonLabel(t, reason) {
286
+ const key = `reason.${reason}`;
287
+ return key in exports.zh ? t(key) : reason;
288
+ }
289
+
290
+ },
291
+ "./index": function (module, exports, require) {
292
+ "use strict";
293
+ Object.defineProperty(exports, "__esModule", { value: true });
294
+ exports.inject = void 0;
295
+ exports.apply = apply;
296
+ const jsx_runtime_1 = require("react/jsx-runtime");
297
+ /**
298
+ * Browser half of the memory plugin: the long-term memory page in the sidebar's panel list. dsh
299
+ * loads these files as one lazy factory; they may import only modules dsh serves to plugins.
300
+ */
301
+ const i18n_1 = require("./i18n");
302
+ const page_1 = require("./page");
303
+ const shared_1 = require("./shared");
304
+ const style_1 = require("./style");
305
+ exports.inject = ['slots', 'locale'];
306
+ function apply(ctx) {
307
+ ctx.effect(() => (0, style_1.installStyle)(), 'vulcan-memory: stylesheet');
308
+ ctx.effect(() => ctx.locale.register(i18n_1.NAMESPACE, { zh: i18n_1.zh, en: i18n_1.en }), 'vulcan-memory: dictionaries');
309
+ ctx.slots.inject('main', () => ctx.slots.register({
310
+ name: 'main',
311
+ key: shared_1.PANEL_ID,
312
+ locale: i18n_1.NAMESPACE,
313
+ inject: () => ({}),
314
+ }, ({ t }) => (0, jsx_runtime_1.jsx)(page_1.MemoryPage, { t: t })));
315
+ // After Scheduled tasks (10) and LuaSkills (20).
316
+ ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({
317
+ name: 'sidebar.panellist',
318
+ id: shared_1.PANEL_ID,
319
+ order: 30,
320
+ locale: i18n_1.NAMESPACE,
321
+ label: () => ctx.locale.bind(i18n_1.NAMESPACE)('panel'),
322
+ }, PanelIcon));
323
+ }
324
+ /** A notebook with a bookmark: notes kept across conversations. */
325
+ function PanelIcon({ size }) {
326
+ return ((0, jsx_runtime_1.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("path", { d: "M6 3.5h11a1.5 1.5 0 0 1 1.5 1.5v14a1.5 1.5 0 0 1-1.5 1.5H6z" }), (0, jsx_runtime_1.jsx)("path", { d: "M6 3.5v17M9.5 8.5h5.5M9.5 12h5.5" }), (0, jsx_runtime_1.jsx)("path", { d: "M13 3.5v4l1.5-1 1.5 1v-4" })] }));
327
+ }
328
+
329
+ },
330
+ "./mode": function (module, exports, require) {
331
+ "use strict";
332
+ Object.defineProperty(exports, "__esModule", { value: true });
333
+ exports.ModeTab = ModeTab;
334
+ const jsx_runtime_1 = require("react/jsx-runtime");
335
+ /**
336
+ * The memory mode tab (`memory-mode-page.tsx`): the mode and the Markdown injection budget, the
337
+ * memory projects with their switch, and import/export.
338
+ */
339
+ const react_1 = require("react");
340
+ const i18n_1 = require("./i18n");
341
+ const parts_1 = require("./parts");
342
+ const shared_1 = require("./shared");
343
+ const transfer_1 = require("./transfer");
344
+ const MODES = ['off', 'vmm', 'markdown'];
345
+ function ModeTab({ t, onSettings }) {
346
+ const [settings, setSettings] = (0, react_1.useState)(undefined);
347
+ const [loadError, setLoadError] = (0, react_1.useState)(undefined);
348
+ const [mode, setMode] = (0, react_1.useState)('markdown');
349
+ const [budget, setBudget] = (0, react_1.useState)(() => draft({ user: 0, spaceBase: 0, projectBase: 0, spaceCatalog: 0, projectCatalog: 0 }));
350
+ const [saving, setSaving] = (0, react_1.useState)(false);
351
+ const [note, setNote] = (0, react_1.useState)(undefined);
352
+ const [generation, setGeneration] = (0, react_1.useState)(0);
353
+ const adopt = (0, react_1.useCallback)((next) => {
354
+ setSettings(next);
355
+ setMode(next.mode);
356
+ setBudget(draft(next.budget));
357
+ onSettings(next);
358
+ }, [onSettings]);
359
+ const load = (0, react_1.useCallback)(() => {
360
+ setLoadError(undefined);
361
+ (0, shared_1.callHost)('GET', 'settings').then(adopt, (failure) => { setLoadError((0, shared_1.messageOf)(failure)); });
362
+ }, [adopt]);
363
+ (0, react_1.useEffect)(() => { load(); }, [load]);
364
+ if (settings === undefined) {
365
+ return loadError === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('loading') }) : ((0, jsx_runtime_1.jsxs)("div", { className: "vac-alert bad vac-between", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('loadFailed'), ": ", loadError] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", onClick: load, children: t('retry') })] }));
366
+ }
367
+ const parsed = parseBudget(budget, settings.maxPartTokens);
368
+ const changed = mode !== settings.mode || (parsed !== null && shared_1.BUDGET_PARTS.some(part => parsed[part] !== settings.budget[part]));
369
+ const total = parsed === null ? null : shared_1.BUDGET_PARTS.reduce((sum, part) => sum + parsed[part], 0);
370
+ const save = () => {
371
+ if (parsed === null)
372
+ return;
373
+ setSaving(true);
374
+ setNote(undefined);
375
+ (0, shared_1.callHost)('POST', 'settings', { mode, budget: parsed }).then((next) => {
376
+ adopt(next);
377
+ setNote({ ok: true, text: t('saved') });
378
+ }, (failure) => { setNote({ ok: false, text: `${t('saveFailed')}: ${(0, shared_1.messageOf)(failure)}` }); }).finally(() => { setSaving(false); });
379
+ };
380
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(parts_1.Section, { title: t('mode.title'), lead: t('mode.hint'), actions: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm primary", disabled: saving || !changed || parsed === null, onClick: save, children: t('save') }), children: [(0, jsx_runtime_1.jsx)(parts_1.Segments, { value: mode, label: t('mode.title'), disabled: saving, onChange: setMode, options: MODES.map(value => ({ value, label: t(`mode.${value}`) })) }), (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t(`mode.${mode}Hint`) }), mode !== 'markdown' ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "vmm-grid", children: shared_1.BUDGET_PARTS.map(part => ((0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t(`part.${part}`), (0, jsx_runtime_1.jsx)("input", { className: "vmm-input", type: "number", inputMode: "numeric", min: 0, max: settings.maxPartTokens, step: 50, disabled: saving, value: budget[part], "aria-invalid": validPart(budget[part], settings.maxPartTokens) ? undefined : true, onChange: (event) => { setBudget({ ...budget, [part]: event.target.value }); } })] }, part))) }), (0, jsx_runtime_1.jsxs)("div", { className: "vac-between", children: [(0, jsx_runtime_1.jsxs)("span", { className: "vac-hint", children: [t('budget.hint'), " ", total === null ? (0, i18n_1.fill)(t('budget.invalid'), { max: settings.maxPartTokens }) : (0, i18n_1.fill)(t('budget.total'), { tokens: total })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: saving, onClick: () => { setBudget(draft(settings.defaultBudget)); }, children: t('budget.reset') })] })] })), note === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: note.ok ? 'vac-hint' : 'vac-error', role: "status", children: note.text })] }), settings.mode !== 'markdown' ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ProjectsSection, { t: t }, generation), (0, jsx_runtime_1.jsx)(transfer_1.TransferSection, { t: t, onImported: () => { setGeneration(value => value + 1); } })] }))] }));
381
+ }
382
+ function ProjectsSection({ t }) {
383
+ const [projects, setProjects] = (0, react_1.useState)(undefined);
384
+ const [error, setError] = (0, react_1.useState)(undefined);
385
+ const [busy, setBusy] = (0, react_1.useState)(undefined);
386
+ (0, react_1.useEffect)(() => {
387
+ let current = true;
388
+ (0, shared_1.loadOverview)({}).then((overview) => { if (current)
389
+ setProjects(overview.projects); }, (failure) => { if (current)
390
+ setError((0, shared_1.messageOf)(failure)); });
391
+ return () => { current = false; };
392
+ }, []);
393
+ const toggle = (project, enabled) => {
394
+ setBusy(project.id);
395
+ setError(undefined);
396
+ (0, shared_1.callHost)('POST', 'project', { action: 'set_enabled', memoryProjectId: project.id, enabled })
397
+ .then(setProjects, (failure) => { setError(`${t('saveFailed')}: ${(0, shared_1.messageOf)(failure)}`); })
398
+ .finally(() => { setBusy(undefined); });
399
+ };
400
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Section, { title: t('projects.title'), lead: t('projects.hint'), flush: true, children: [error === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-error vmm-pad", children: error }), projects === undefined ? (error === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint vmm-pad", children: t('loading') }) : null)
401
+ : projects.length === 0 ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint vmm-pad", children: t('projects.empty') }) : ((0, jsx_runtime_1.jsx)("ul", { className: "vmm-list", children: projects.map(project => ((0, jsx_runtime_1.jsxs)("li", { className: "vmm-row", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vmm-row-main", children: [(0, jsx_runtime_1.jsxs)("span", { className: "vac-inline", children: [(0, jsx_runtime_1.jsx)("strong", { children: project.name }), project.orphaned ? (0, jsx_runtime_1.jsx)("span", { className: "vac-badge warn", children: t('projects.orphaned') }) : null] }), project.paths.length === 0 ? null : (0, jsx_runtime_1.jsx)("span", { className: "vmm-mono", children: project.paths.join(' · ') })] }), (0, jsx_runtime_1.jsxs)("label", { className: "vmm-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: project.memoryEnabled, disabled: busy !== undefined, onChange: (event) => { toggle(project, event.target.checked); } }), t('projects.enabled')] })] }, project.id))) }))] }));
402
+ }
403
+ function draft(budget) {
404
+ return Object.fromEntries(shared_1.BUDGET_PARTS.map(part => [part, String(budget[part])]));
405
+ }
406
+ function validPart(text, max) {
407
+ const value = Number(text);
408
+ return text.trim() !== '' && Number.isInteger(value) && value >= 0 && value <= max;
409
+ }
410
+ function parseBudget(values, max) {
411
+ if (!shared_1.BUDGET_PARTS.every(part => validPart(values[part], max)))
412
+ return null;
413
+ return Object.fromEntries(shared_1.BUDGET_PARTS.map(part => [part, Number(values[part])]));
414
+ }
415
+
416
+ },
417
+ "./page": function (module, exports, require) {
418
+ "use strict";
419
+ Object.defineProperty(exports, "__esModule", { value: true });
420
+ exports.MemoryPage = MemoryPage;
421
+ const jsx_runtime_1 = require("react/jsx-runtime");
422
+ /**
423
+ * The long-term memory page the sidebar opens: memory mode, then one tab per scope, as
424
+ * VulcanCode's memory area (`markdown-memory-area.tsx`) arranges them. Scope tabs are shown only
425
+ * in Markdown mode, since only Markdown memory is kept in these files.
426
+ */
427
+ const react_1 = require("react");
428
+ const mode_1 = require("./mode");
429
+ const scope_1 = require("./scope");
430
+ const shared_1 = require("./shared");
431
+ function MemoryPage({ t }) {
432
+ const [tab, setTab] = (0, react_1.useState)('mode');
433
+ const [mode, setMode] = (0, react_1.useState)(undefined);
434
+ const tabs = mode === 'markdown' ? ['mode', ...shared_1.SCOPES] : ['mode'];
435
+ const onSettings = (settings) => {
436
+ setMode(settings.mode);
437
+ if (settings.mode !== 'markdown')
438
+ setTab('mode');
439
+ };
440
+ return ((0, jsx_runtime_1.jsx)("div", { className: "vac vmm-panel", children: (0, jsx_runtime_1.jsxs)("div", { className: "vac vac-page vmm-panel-inner", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vac-page-head", children: [(0, jsx_runtime_1.jsx)("h2", { children: t('panel') }), (0, jsx_runtime_1.jsx)("p", { className: "vac-lead", children: t('page.lead') })] }), (0, jsx_runtime_1.jsx)("div", { className: "vmm-tabs", role: "tablist", children: tabs.map(name => ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === name, className: tab === name ? 'vmm-tab on' : 'vmm-tab', onClick: () => { setTab(name); }, children: t(`nav.${name}`) }, name))) }), (0, jsx_runtime_1.jsx)("div", { hidden: tab !== 'mode', children: (0, jsx_runtime_1.jsx)(mode_1.ModeTab, { t: t, onSettings: onSettings }) }), tab === 'mode' ? null : (0, jsx_runtime_1.jsx)(scope_1.ScopeTab, { scope: tab, t: t }, tab)] }) }));
441
+ }
442
+
443
+ },
444
+ "./parts": function (module, exports, require) {
445
+ "use strict";
446
+ Object.defineProperty(exports, "__esModule", { value: true });
447
+ exports.Section = Section;
448
+ exports.Dialog = Dialog;
449
+ exports.Segments = Segments;
450
+ const jsx_runtime_1 = require("react/jsx-runtime");
451
+ /** Small building blocks of the memory page: a section, a modal dialog and segmented buttons. */
452
+ const react_1 = require("react");
453
+ function Section({ title, lead, actions, flush, children }) {
454
+ return ((0, jsx_runtime_1.jsxs)("section", { className: "vac-section", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vac-section-head", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "vac-section-title", children: title }), lead === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-section-lead", children: lead })] }), actions === undefined ? null : (0, jsx_runtime_1.jsx)("span", { className: "vac-inline", children: actions })] }), (0, jsx_runtime_1.jsx)("div", { className: flush === true ? 'vac-section-body flush' : 'vac-section-body', children: children })] }));
455
+ }
456
+ /** A modal dialog; Escape and the backdrop close it unless `busy`. */
457
+ function Dialog({ title, lead, wide, busy, close, footer, children }) {
458
+ (0, react_1.useEffect)(() => {
459
+ const onKey = (event) => { if (event.key === 'Escape' && busy !== true)
460
+ close(); };
461
+ window.addEventListener('keydown', onKey);
462
+ return () => { window.removeEventListener('keydown', onKey); };
463
+ }, [close, busy]);
464
+ return ((0, jsx_runtime_1.jsx)("div", { className: "vac vmm-dialog-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget && busy !== true)
465
+ close(); }, children: (0, jsx_runtime_1.jsxs)("div", { className: wide === true ? 'vmm-dialog wide' : 'vmm-dialog', role: "dialog", "aria-modal": "true", "aria-label": title, children: [(0, jsx_runtime_1.jsxs)("div", { className: "vmm-dialog-head", children: [(0, jsx_runtime_1.jsx)("h3", { children: title }), lead === undefined ? null : (0, jsx_runtime_1.jsx)("p", { children: lead })] }), (0, jsx_runtime_1.jsx)("div", { className: "vmm-dialog-body", children: children }), (0, jsx_runtime_1.jsx)("div", { className: "vmm-dialog-foot", children: footer })] }) }));
466
+ }
467
+ function Segments({ value, options, disabled, label, onChange }) {
468
+ return ((0, jsx_runtime_1.jsx)("span", { className: "vmm-segs", role: "radiogroup", "aria-label": label, children: options.map(option => ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": option.value === value, className: option.value === value ? 'vmm-seg on' : 'vmm-seg', disabled: disabled, onClick: () => { onChange(option.value); }, children: option.label }, option.value))) }));
469
+ }
470
+
471
+ },
472
+ "./scope": function (module, exports, require) {
473
+ "use strict";
474
+ Object.defineProperty(exports, "__esModule", { value: true });
475
+ exports.ScopeTab = ScopeTab;
476
+ const jsx_runtime_1 = require("react/jsx-runtime");
477
+ /**
478
+ * One memory scope (`markdown-scope-page.tsx` and `markdown-node-dialogs.tsx`): the project
479
+ * selector and orphan notice, injection usage, the node tree, the node editor and the whole-file
480
+ * editor. Host projects are dsh workspaces.
481
+ */
482
+ const react_1 = require("react");
483
+ const i18n_1 = require("./i18n");
484
+ const parts_1 = require("./parts");
485
+ const shared_1 = require("./shared");
486
+ const SCOPE_PARTS = {
487
+ user: ['user'],
488
+ space: ['spaceBase', 'spaceCatalog'],
489
+ project: ['projectBase', 'projectCatalog'],
490
+ };
491
+ function ScopeTab({ scope, t }) {
492
+ const [selection, setSelection] = (0, react_1.useState)(scope === 'project' ? undefined : null);
493
+ const target = scope !== 'project' || selection == null ? {}
494
+ : selection.kind === 'host' ? { projectId: selection.id } : { memoryProjectId: selection.id };
495
+ const targetKey = JSON.stringify(target);
496
+ const [overview, setOverview] = (0, react_1.useState)(undefined);
497
+ const [loadError, setLoadError] = (0, react_1.useState)(undefined);
498
+ const [busy, setBusy] = (0, react_1.useState)(false);
499
+ const [notice, setNotice] = (0, react_1.useState)(undefined);
500
+ const [editor, setEditor] = (0, react_1.useState)(undefined);
501
+ const [deleting, setDeleting] = (0, react_1.useState)(undefined);
502
+ const [file, setFile] = (0, react_1.useState)(undefined);
503
+ const load = (0, react_1.useCallback)(async () => {
504
+ setLoadError(undefined);
505
+ try {
506
+ const next = await (0, shared_1.loadOverview)(JSON.parse(targetKey));
507
+ setOverview(next);
508
+ // The project tab starts at the first dsh workspace, the closest to VulcanCode's selected project.
509
+ if (selection === undefined) {
510
+ const first = next.hostProjects[0];
511
+ setSelection(first === undefined ? null : { kind: 'host', id: first.id });
512
+ }
513
+ }
514
+ catch (error) {
515
+ setLoadError((0, shared_1.messageOf)(error));
516
+ }
517
+ }, [targetKey, selection]);
518
+ (0, react_1.useEffect)(() => {
519
+ setOverview(undefined);
520
+ void load();
521
+ }, [targetKey]);
522
+ const fail = (error) => { setNotice({ ok: false, text: `${t('writeFailed')}: ${(0, shared_1.messageOf)(error)}` }); };
523
+ /** Apply operations as the user and keep the refreshed page; `undefined` when nothing was written. */
524
+ const write = async (operations) => {
525
+ setBusy(true);
526
+ setNotice(undefined);
527
+ try {
528
+ const next = await (0, shared_1.callHost)('POST', 'write', { ...target, operations });
529
+ setOverview(next);
530
+ return next;
531
+ }
532
+ catch (error) {
533
+ fail(error);
534
+ return undefined;
535
+ }
536
+ finally {
537
+ setBusy(false);
538
+ }
539
+ };
540
+ const projectAction = async (action) => {
541
+ setBusy(true);
542
+ setNotice(undefined);
543
+ try {
544
+ await (0, shared_1.callHost)('POST', 'project', action);
545
+ await load();
546
+ }
547
+ catch (error) {
548
+ fail(error);
549
+ }
550
+ finally {
551
+ setBusy(false);
552
+ }
553
+ };
554
+ const shownProjectId = overview?.memoryProjectId ?? null;
555
+ const fileProject = scope === 'project' && shownProjectId !== null ? `&memoryProjectId=${encodeURIComponent(shownProjectId)}` : '';
556
+ const openFile = () => {
557
+ (0, shared_1.callHost)('GET', `file?scope=${scope}${fileProject}`).then(setFile, (error) => {
558
+ setNotice({ ok: false, text: `${t('loadFailed')}: ${(0, shared_1.messageOf)(error)}` });
559
+ });
560
+ };
561
+ const saveFile = async (expected, text) => {
562
+ try {
563
+ await (0, shared_1.callHost)('POST', 'file', { scope, ...scope === 'project' && shownProjectId !== null ? { memoryProjectId: shownProjectId } : {}, expected, text });
564
+ setNotice({ ok: true, text: t('file.saved') });
565
+ await load();
566
+ return true;
567
+ }
568
+ catch (error) {
569
+ fail(error);
570
+ return false;
571
+ }
572
+ };
573
+ if (loadError !== undefined) {
574
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "vac-alert bad vac-between", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('loadFailed'), ": ", loadError] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", onClick: () => { void load(); }, children: t('retry') })] }));
575
+ }
576
+ if (overview === undefined)
577
+ return (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('loading') });
578
+ if (scope === 'project' && selection === null && overview.projects.length === 0) {
579
+ return (0, jsx_runtime_1.jsx)(parts_1.Section, { title: t('nav.project'), children: (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('project.none') }) });
580
+ }
581
+ const nodes = overview[scope];
582
+ const shownProject = overview.projects.find(project => project.id === shownProjectId);
583
+ // Writing the project scope needs a bound or chosen memory project, or a host project to bind.
584
+ const projectAvailable = shownProjectId !== null || selection?.kind === 'host';
585
+ const canWrite = scope !== 'project' || projectAvailable;
586
+ const hostIds = new Set(overview.hostProjects.map(host => host.id));
587
+ const unhosted = overview.projects.filter(project => !project.hostProjectIds.some(id => hostIds.has(id)));
588
+ const orphan = scope === 'project' && selection?.kind === 'host' ? overview.orphanCandidates[0] : undefined;
589
+ const orphanProject = orphan === undefined ? undefined : overview.projects.find(project => project.id === orphan.memoryProjectId);
590
+ const usage = overview.usage.filter(row => SCOPE_PARTS[scope].includes(row.part));
591
+ const warningPercent = overview.settings.warningPercent;
592
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [scope !== 'project' ? null : ((0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t('project.select'), (0, jsx_runtime_1.jsxs)("select", { className: "vmm-select", disabled: busy, value: selection == null ? '' : `${selection.kind}:${selection.id}`, onChange: (event) => {
593
+ const [kind, ...rest] = event.target.value.split(':');
594
+ setSelection({ kind: kind === 'memory' ? 'memory' : 'host', id: rest.join(':') });
595
+ }, children: [selection == null ? (0, jsx_runtime_1.jsx)("option", { value: "", disabled: true, children: t('project.select') }) : null, overview.hostProjects.map(host => (0, jsx_runtime_1.jsxs)("option", { value: `host:${host.id}`, children: [host.title, " \u2014 ", host.path] }, host.id)), unhosted.length === 0 ? null : ((0, jsx_runtime_1.jsx)("optgroup", { label: t('project.otherGroup'), children: unhosted.map(project => (0, jsx_runtime_1.jsx)("option", { value: `memory:${project.id}`, children: project.name }, project.id)) }))] })] })), orphan !== undefined && orphanProject !== undefined && selection?.kind === 'host' ? ((0, jsx_runtime_1.jsxs)("div", { className: "vac-alert info", children: [(0, jsx_runtime_1.jsx)("strong", { children: t('orphan.title') }), (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: (0, i18n_1.fill)(t('orphan.body'), { name: orphanProject.name, reasons: orphan.reasons.map(reason => (0, i18n_1.reasonLabel)(t, reason)).join(', ') }) }), (0, jsx_runtime_1.jsxs)("span", { className: "vac-inline", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm primary", disabled: busy, onClick: () => { void projectAction({ action: 'link', projectId: selection.id, memoryProjectId: orphan.memoryProjectId }); }, children: t('orphan.link') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: busy, onClick: () => { void projectAction({ action: 'create', projectId: selection.id }); }, children: t('orphan.create') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm ghost", disabled: busy, onClick: () => { void projectAction({ action: 'ignore', projectId: selection.id }); }, children: t('orphan.ignore') })] })] })) : null, scope === 'project' && shownProject !== undefined && !shownProject.memoryEnabled ? (0, jsx_runtime_1.jsx)("div", { className: "vac-alert warn", children: t('project.disabled') }) : null, notice === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: notice.ok ? 'vac-alert ok' : 'vac-alert bad', role: "status", children: notice.text }), usage.length === 0 ? null : ((0, jsx_runtime_1.jsx)(parts_1.Section, { title: t('usage.title'), children: (0, jsx_runtime_1.jsxs)("div", { className: "vmm-usage", children: [usage.map((row) => {
596
+ const percent = row.budget > 0 ? Math.round(row.tokens * 100 / row.budget) : 0;
597
+ const tone = row.tokens > row.budget ? 'bad' : percent >= warningPercent ? 'warn' : 'ok';
598
+ const width = Math.min(100, row.tokens * 100 / Math.max(row.budget, row.tokens, 1));
599
+ return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "vmm-usage-head", children: [(0, jsx_runtime_1.jsx)("span", { children: t(`part.${row.part}`) }), (0, jsx_runtime_1.jsx)("span", { children: (0, i18n_1.fill)(t('usage.value'), { tokens: row.tokens, budget: row.budget }) }), tone === 'bad' ? (0, jsx_runtime_1.jsx)("span", { className: "vac-badge bad", children: t('usage.over') })
600
+ : tone === 'warn' ? (0, jsx_runtime_1.jsx)("span", { className: "vac-badge warn", children: t('usage.near') }) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "vmm-bar", role: "meter", "aria-label": t(`part.${row.part}`), "aria-valuemin": 0, "aria-valuemax": Math.max(row.budget, row.tokens, 1), "aria-valuenow": row.tokens, children: (0, jsx_runtime_1.jsx)("div", { className: `vmm-bar-fill ${tone}`, style: { width: `${width}%` } }) }), row.omitted.length === 0 ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: (0, i18n_1.fill)(t('usage.omitted'), { titles: row.omitted.join(', ') }) })] }, row.part));
601
+ }), usage.some(row => row.tokens > row.budget || (row.budget > 0 && row.tokens * 100 >= row.budget * warningPercent))
602
+ ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('usage.curateHint') }) : null] }) })), (0, jsx_runtime_1.jsx)(parts_1.Section, { title: t(`nav.${scope}`), lead: scope === 'project' && projectAvailable && shownProjectId === null ? t('project.unbound') : t(`detail.${scope}`), flush: true, actions: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: busy || !canWrite, onClick: () => { setEditor({ kind: 'add' }); }, children: t('node.add') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm ghost", disabled: busy || (scope === 'project' && shownProjectId === null), onClick: openFile, children: t('file.edit') })] })), children: nodes.length === 0 ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint vmm-pad", children: t('nodes.empty') }) : ((0, jsx_runtime_1.jsx)("ol", { className: "vmm-tree", children: nodes.map(node => ((0, jsx_runtime_1.jsx)(NodeRow, { t: t, node: node, busy: busy || !canWrite, onEdit: () => { setEditor({ kind: 'edit', node }); }, onMove: () => { setEditor({ kind: 'move', node }); }, onDelete: () => { setDeleting(node); } }, `${node.id}:${node.revision}`))) })) }), editor === undefined ? null : ((0, jsx_runtime_1.jsx)(NodeEditor, { t: t, state: editor, scope: scope, overview: overview, projectAvailable: projectAvailable, close: () => { setEditor(undefined); }, submit: write })), file === undefined ? null : (0, jsx_runtime_1.jsx)(FileEditor, { t: t, file: file, close: () => { setFile(undefined); }, save: saveFile }), deleting === undefined ? null : ((0, jsx_runtime_1.jsx)(parts_1.Dialog, { title: (0, i18n_1.fill)(t('node.deleteConfirm'), { title: (0, shared_1.nodeTitle)(deleting) }), lead: deleting.descendants > 0 ? (0, i18n_1.fill)(t('node.deleteChildren'), { count: deleting.descendants }) : undefined, close: () => { setDeleting(undefined); }, footer: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", onClick: () => { setDeleting(undefined); }, children: t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary danger", onClick: () => {
603
+ const node = deleting;
604
+ setDeleting(undefined);
605
+ void write([{ action: 'delete', id: node.id, expectedRevision: node.revision, withChildren: node.descendants > 0 }]);
606
+ }, children: t('node.delete') })] })), children: (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: deleting.path.join(' › ') }) }))] }));
607
+ }
608
+ function NodeRow({ t, node, busy, onEdit, onMove, onDelete }) {
609
+ // The base section anchors the injected part and cannot be moved or deleted.
610
+ const fixed = node.level === 1 && node.section === 'base';
611
+ return ((0, jsx_runtime_1.jsxs)("li", { className: "vmm-node", "data-level": node.level, children: [(0, jsx_runtime_1.jsxs)("div", { className: "vmm-node-head", children: [(0, jsx_runtime_1.jsx)("strong", { children: (0, shared_1.nodeTitle)(node) }), fixed ? (0, jsx_runtime_1.jsx)("span", { className: "vac-badge outline", children: t('node.base') }) : null, node.source !== null && node.level > 1 ? ((0, jsx_runtime_1.jsx)("span", { className: "vac-badge outline", title: node.session ?? undefined, children: t(node.source === 'user' ? 'node.sourceUser' : 'node.sourceAi') })) : null, node.updated === null ? null : (0, jsx_runtime_1.jsx)("small", { children: (0, i18n_1.fill)(t('node.updated'), { time: node.updated }) }), (0, jsx_runtime_1.jsxs)("span", { className: "vmm-node-acts", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm ghost", disabled: busy, onClick: onEdit, children: t('node.edit') }), fixed ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm ghost", disabled: busy, onClick: onMove, children: t('node.move') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm ghost danger", disabled: busy, onClick: onDelete, children: t('node.delete') })] }))] })] }), node.body === '' ? null : (0, jsx_runtime_1.jsx)("p", { className: "vmm-node-body", children: node.body })] }));
612
+ }
613
+ /** Destination sections of a scope: existing H1 nodes (`node:<id>`), then presets the file lacks (`preset:<key>`). */
614
+ function sectionChoices(overview, scope) {
615
+ const roots = overview[scope].filter(node => node.level === 1);
616
+ const existing = new Set(roots.map(node => node.section).filter((key) => key !== null));
617
+ return [
618
+ ...roots.map(node => ({ value: `node:${node.id}`, title: node.path[0] ?? '' })),
619
+ ...overview.presetSections.filter(section => section.scope === scope && !existing.has(section.key))
620
+ .map(section => ({ value: `preset:${section.key}`, title: section.title })),
621
+ ];
622
+ }
623
+ function placement(value) {
624
+ return value.startsWith('node:') ? { parentId: value.slice('node:'.length) } : { section: value.slice('preset:'.length) };
625
+ }
626
+ function NodeEditor({ t, state, scope, overview, projectAvailable, close, submit }) {
627
+ const currentTitle = state.kind === 'add' ? '' : (0, shared_1.nodeTitle)(state.node);
628
+ const [title, setTitle] = (0, react_1.useState)(currentTitle);
629
+ const [body, setBody] = (0, react_1.useState)(state.kind === 'edit' ? state.node.body : '');
630
+ const [targetScope, setTargetScope] = (0, react_1.useState)(scope);
631
+ const [section, setSection] = (0, react_1.useState)(() => sectionChoices(overview, scope)[0]?.value ?? '');
632
+ const [saving, setSaving] = (0, react_1.useState)(false);
633
+ const choices = sectionChoices(overview, targetScope).filter(choice => state.kind !== 'move' || choice.value !== `node:${state.node.id}`);
634
+ const scopes = shared_1.SCOPES.filter(item => item !== 'project' || projectAvailable);
635
+ const titleChanged = state.kind === 'edit' && title.trim() !== currentTitle;
636
+ const bodyChanged = state.kind === 'edit' && body !== state.node.body;
637
+ const valid = state.kind === 'move' ? section !== ''
638
+ : title.trim() !== '' && (state.kind === 'add' ? section !== '' : titleChanged || bodyChanged);
639
+ // An edit that changes both parts writes the body first and then renames the refreshed node,
640
+ // because the revision covers the title and the body.
641
+ const save = async () => {
642
+ setSaving(true);
643
+ try {
644
+ if (state.kind === 'add') {
645
+ if (await submit([{ action: 'create', scope, title: title.trim(), body, ...placement(section) }]) !== undefined)
646
+ close();
647
+ return;
648
+ }
649
+ if (state.kind === 'move') {
650
+ if (await submit([{ action: 'move', id: state.node.id, expectedRevision: state.node.revision, scope: targetScope, ...placement(section) }]) !== undefined)
651
+ close();
652
+ return;
653
+ }
654
+ let node = state.node;
655
+ if (bodyChanged) {
656
+ const next = await submit([{ action: 'replace', id: node.id, expectedRevision: node.revision, body }]);
657
+ if (next === undefined)
658
+ return;
659
+ const path = JSON.stringify(node.path);
660
+ node = next[scope].find(candidate => JSON.stringify(candidate.path) === path);
661
+ }
662
+ if (titleChanged && node !== undefined) {
663
+ if (await submit([{ action: 'rename', id: node.id, expectedRevision: node.revision, title: title.trim() }]) === undefined)
664
+ return;
665
+ }
666
+ close();
667
+ }
668
+ finally {
669
+ setSaving(false);
670
+ }
671
+ };
672
+ const heading = state.kind === 'add' ? t('editor.addTitle') : state.kind === 'move' ? t('editor.moveTitle') : t('editor.editTitle');
673
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Dialog, { title: heading, lead: state.kind === 'move' ? currentTitle : undefined, busy: saving, close: close, footer: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", disabled: saving, onClick: close, children: t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", disabled: saving || !valid, onClick: () => { void save(); }, children: t('save') })] })), children: [state.kind !== 'move' ? null : ((0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t('editor.scope'), (0, jsx_runtime_1.jsx)("select", { className: "vmm-select", value: targetScope, disabled: saving, onChange: (event) => {
674
+ const next = event.target.value;
675
+ setTargetScope(next);
676
+ setSection(sectionChoices(overview, next)[0]?.value ?? '');
677
+ }, children: scopes.map(item => (0, jsx_runtime_1.jsx)("option", { value: item, children: t(`nav.${item}`) }, item)) })] })), state.kind === 'edit' ? null : ((0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t('editor.section'), (0, jsx_runtime_1.jsx)("select", { className: "vmm-select", value: section, disabled: saving || choices.length === 0, onChange: (event) => { setSection(event.target.value); }, children: choices.map(choice => (0, jsx_runtime_1.jsx)("option", { value: choice.value, children: choice.title }, choice.value)) })] })), state.kind === 'move' ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t('editor.title'), (0, jsx_runtime_1.jsx)("input", { className: "vmm-input", value: title, disabled: saving, maxLength: 120, onChange: (event) => { setTitle(event.target.value); } })] }), (0, jsx_runtime_1.jsxs)("label", { className: "vmm-field", children: [t('editor.body'), (0, jsx_runtime_1.jsx)("textarea", { className: "vmm-textarea", rows: 8, value: body, disabled: saving, onChange: (event) => { setBody(event.target.value); } })] })] }))] }));
678
+ }
679
+ function FileEditor({ t, file, close, save }) {
680
+ const [text, setText] = (0, react_1.useState)(file.text);
681
+ const [saving, setSaving] = (0, react_1.useState)(false);
682
+ const run = async () => {
683
+ setSaving(true);
684
+ try {
685
+ if (await save(file.text, text))
686
+ close();
687
+ }
688
+ finally {
689
+ setSaving(false);
690
+ }
691
+ };
692
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Dialog, { title: t('file.title'), lead: t('file.hint'), wide: true, busy: saving, close: close, footer: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", disabled: saving, onClick: close, children: t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", disabled: saving || text === file.text, onClick: () => { void run(); }, children: t('save') })] })), children: [(0, jsx_runtime_1.jsx)("code", { className: "vmm-mono", children: file.path }), (0, jsx_runtime_1.jsx)("textarea", { className: "vmm-textarea file", spellCheck: false, value: text, disabled: saving, "aria-label": t('file.title'), onChange: (event) => { setText(event.target.value); } })] }));
693
+ }
694
+
695
+ },
696
+ "./shared": function (module, exports, require) {
697
+ "use strict";
698
+ /** Facts the browser half shares with the Host half (`src/page.ts`). */
699
+ Object.defineProperty(exports, "__esModule", { value: true });
700
+ exports.BUDGET_PARTS = exports.SCOPES = exports.PANEL_ID = exports.PACKAGE_NAME = void 0;
701
+ exports.callHost = callHost;
702
+ exports.loadOverview = loadOverview;
703
+ exports.messageOf = messageOf;
704
+ exports.nodeTitle = nodeTitle;
705
+ exports.toBase64 = toBase64;
706
+ exports.fromBase64 = fromBase64;
707
+ exports.download = download;
708
+ /** Checked against `package.json` by `scripts/bundle-client.mjs`. */
709
+ exports.PACKAGE_NAME = '@openvulcan/dsh-vulcan-memory';
710
+ /** The main panel the sidebar entry opens. */
711
+ exports.PANEL_ID = 'vulcan-memory';
712
+ /** Mirrors `ROUTE_PREFIX` in `src/index.ts`, below the page's origin. */
713
+ const ROUTE_BASE = '/api/vulcan-memory/';
714
+ exports.SCOPES = ['user', 'space', 'project'];
715
+ /** Full-text parts first, then catalogs, as VulcanCode orders the budget fields. */
716
+ exports.BUDGET_PARTS = ['user', 'spaceBase', 'projectBase', 'spaceCatalog', 'projectCatalog'];
717
+ async function callHost(method, route, body) {
718
+ const response = await fetch(`${ROUTE_BASE}${route}`, {
719
+ method,
720
+ ...body === undefined ? {} : { body: JSON.stringify(body), headers: { 'content-type': 'application/json' } },
721
+ });
722
+ const answer = await response.json().catch(() => undefined);
723
+ if (answer?.ok !== true)
724
+ throw new Error(answer?.error?.message ?? `HTTP ${response.status}`);
725
+ return answer.value;
726
+ }
727
+ function loadOverview(target) {
728
+ const params = new URLSearchParams();
729
+ if (target.projectId !== undefined)
730
+ params.set('projectId', target.projectId);
731
+ if (target.memoryProjectId !== undefined)
732
+ params.set('memoryProjectId', target.memoryProjectId);
733
+ return callHost('GET', `overview?${params.toString()}`);
734
+ }
735
+ function messageOf(error) {
736
+ return error instanceof Error ? error.message : String(error);
737
+ }
738
+ function nodeTitle(node) {
739
+ return node.path[node.path.length - 1] ?? '';
740
+ }
741
+ /** Base64 of bytes, in chunks small enough for `String.fromCharCode`. */
742
+ function toBase64(bytes) {
743
+ let binary = '';
744
+ for (let start = 0; start < bytes.length; start += 0x8000)
745
+ binary += String.fromCharCode(...bytes.subarray(start, start + 0x8000));
746
+ return btoa(binary);
747
+ }
748
+ function fromBase64(text) {
749
+ const binary = atob(text);
750
+ const bytes = new Uint8Array(binary.length);
751
+ for (let index = 0; index < binary.length; index++)
752
+ bytes[index] = binary.charCodeAt(index);
753
+ return bytes;
754
+ }
755
+ /** Save bytes as a file through the browser. */
756
+ function download(fileName, bytes) {
757
+ const url = URL.createObjectURL(new Blob([bytes], { type: 'application/zip' }));
758
+ const link = document.createElement('a');
759
+ link.href = url;
760
+ link.download = fileName;
761
+ document.body.appendChild(link);
762
+ link.click();
763
+ link.remove();
764
+ setTimeout(() => { URL.revokeObjectURL(url); }, 0);
765
+ }
766
+
767
+ },
768
+ "./style": function (module, exports, require) {
769
+ "use strict";
770
+ /**
771
+ * The plugin's look, after VulcanCode's ui-kit (`apps/vulcan-code-web/src/ui-kit/styles`): the
772
+ * same `.vac` tokens and base rules as the LuaSkills plugin, plus the memory page under `vmm-*`.
773
+ * Every rule is scoped under `.vac` or `vmm-`, so nothing leaks into dsh; dark values follow
774
+ * dsh's `body[data-ds-dark-theme]`.
775
+ */
776
+ Object.defineProperty(exports, "__esModule", { value: true });
777
+ exports.installStyle = installStyle;
778
+ const CSS = String.raw `
779
+ .vac {
780
+ --vk-u: 4px;
781
+ --vk-panel: #fff; --vk-sunken: #f6f7f9; --vk-hover: #eef1f4; --vk-select: #e3e9f8;
782
+ --vk-line: #dde1e6; --vk-line-strong: #c3c9d1; --vk-line-soft: #eaecef;
783
+ --vk-ink: #14171c; --vk-ink-2: #4a515c; --vk-ink-3: #626973;
784
+ --vk-ember: #e2511a; --vk-signal: #2e58cc; --vk-ok: #18774a; --vk-warn: #945d09; --vk-bad: #c3303d;
785
+ --vk-shadow-1: 0 1px 2px rgba(20, 23, 28, 0.06);
786
+ --vk-shadow-2: 0 16px 40px rgba(20, 23, 28, 0.18), 0 2px 8px rgba(20, 23, 28, 0.08);
787
+ --vk-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
788
+ font-size: 13px; line-height: 1.5; color: var(--vk-ink);
789
+ }
790
+ body[data-ds-dark-theme] .vac {
791
+ --vk-panel: #15181d; --vk-sunken: #0f1215; --vk-hover: #1e2329; --vk-select: #1c2537;
792
+ --vk-line: #23282f; --vk-line-strong: #343b45; --vk-line-soft: #1a1e24;
793
+ --vk-ink: #e6e9ed; --vk-ink-2: #a2aab5; --vk-ink-3: #848c97;
794
+ --vk-ember: #ff6a2b; --vk-signal: #7d9dff; --vk-ok: #3fc48a; --vk-warn: #e0a83a; --vk-bad: #f0646f;
795
+ --vk-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
796
+ --vk-shadow-2: 0 16px 40px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
797
+ }
798
+ .vac *, .vac *::before, .vac *::after { box-sizing: border-box; }
799
+ .vac-page { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 4); counter-reset: vac-section; padding-bottom: calc(var(--vk-u) * 2); }
800
+ .vac-page-head h2 { margin: 0; font-size: 16px; font-weight: 600; }
801
+ .vac-lead { margin: calc(var(--vk-u) * 1) 0 0; color: var(--vk-ink-2); }
802
+ .vac-hint { margin: 0; color: var(--vk-ink-3); font-size: 12px; }
803
+ .vac-error { margin: 0; color: var(--vk-bad); font-size: 12px; overflow-wrap: anywhere; }
804
+
805
+ .vac-section { position: relative; border: 1px solid var(--vk-line); background: var(--vk-panel); box-shadow: var(--vk-shadow-1); }
806
+ .vac-section::before { content: ""; position: absolute; left: -1px; top: -1px; width: calc(var(--vk-u) * 7); height: 2px; background: var(--vk-ember); }
807
+ .vac-section-head { display: flex; align-items: flex-start; justify-content: space-between; gap: calc(var(--vk-u) * 3); padding: calc(var(--vk-u) * 3.5) calc(var(--vk-u) * 4) 0; }
808
+ .vac-section-title { display: flex; align-items: center; gap: calc(var(--vk-u) * 2); margin: 0; font-size: 15px; font-weight: 600; }
809
+ .vac-section-title::before { counter-increment: vac-section; content: counter(vac-section, decimal-leading-zero); border: 1px solid var(--vk-line); color: var(--vk-ink-3); font-family: var(--vk-mono); font-size: 10px; font-weight: 400; letter-spacing: .06em; padding: 0 5px; line-height: 16px; }
810
+ .vac-section-lead { margin: calc(var(--vk-u) * 1) 0 0; color: var(--vk-ink-3); font-size: 12px; }
811
+ .vac-section-body { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 3); padding: calc(var(--vk-u) * 3) calc(var(--vk-u) * 4) calc(var(--vk-u) * 4); }
812
+ .vac-section-body.flush { padding: calc(var(--vk-u) * 3) 0 0; gap: 0; }
813
+
814
+ .vac-inline { display: flex; align-items: center; gap: calc(var(--vk-u) * 2); flex-wrap: wrap; }
815
+ .vac-between { display: flex; align-items: center; justify-content: space-between; gap: calc(var(--vk-u) * 3); flex-wrap: wrap; }
816
+
817
+ .vac-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 28px; padding: 0 calc(var(--vk-u) * 3); border: 1px solid var(--vk-line-strong); background: var(--vk-panel); color: var(--vk-ink); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; white-space: nowrap; border-radius: 0; }
818
+ .vac-btn:hover:not(:disabled) { background: var(--vk-hover); }
819
+ .vac-btn:disabled { cursor: default; opacity: .55; }
820
+ .vac-btn.sm { height: 24px; padding: 0 calc(var(--vk-u) * 2); }
821
+ .vac-btn.primary { background: var(--vk-ink); border-color: var(--vk-ink); color: var(--vk-panel); }
822
+ .vac-btn.primary:hover:not(:disabled) { background: var(--vk-ink-2); }
823
+ .vac-btn.ghost { border-color: transparent; background: transparent; }
824
+ .vac-btn.ghost:hover:not(:disabled) { background: var(--vk-hover); }
825
+ .vac-btn.danger { color: var(--vk-bad); }
826
+ .vac-btn.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--vk-bad) 9%, transparent); }
827
+ .vac-btn:focus-visible { outline: 2px solid var(--vk-signal); outline-offset: 1px; }
828
+
829
+ .vac-badge { display: inline-flex; align-items: center; height: 18px; padding: 0 6px; border: 1px solid color-mix(in srgb, var(--tone, var(--vk-ink-3)) 32%, transparent); background: color-mix(in srgb, var(--tone, var(--vk-ink-3)) 9%, transparent); color: var(--tone, var(--vk-ink-2)); font-size: 11px; font-weight: 500; white-space: nowrap; }
830
+ .vac-badge.ok { --tone: var(--vk-ok); }
831
+ .vac-badge.warn { --tone: var(--vk-warn); }
832
+ .vac-badge.bad { --tone: var(--vk-bad); }
833
+ .vac-badge.info { --tone: var(--vk-signal); }
834
+ .vac-badge.outline { --tone: var(--vk-ink-3); background: transparent; }
835
+
836
+ .vac-alert { padding: calc(var(--vk-u) * 2.5) calc(var(--vk-u) * 3); border: 1px solid var(--vk-line); border-left: 3px solid var(--tone, var(--vk-ink-3)); background: var(--vk-sunken); color: var(--vk-ink-2); font-size: 12px; overflow-wrap: anywhere; }
837
+ .vac-alert.warn { --tone: var(--vk-warn); }
838
+ .vac-alert.bad { --tone: var(--vk-bad); }
839
+ .vac-alert.info { --tone: var(--vk-signal); }
840
+ .vac-alert.ok { --tone: var(--vk-ok); }
841
+
842
+ /* The memory page fills dsh's main column and scrolls on its own. */
843
+ .vmm-panel { height: 100%; overflow-y: auto; background: var(--vk-sunken); }
844
+ .vmm-panel-inner { max-width: 960px; margin: 0 auto; padding: calc(var(--vk-u) * 6) calc(var(--vk-u) * 6) calc(var(--vk-u) * 8); }
845
+ .vmm-pad { padding: 0 calc(var(--vk-u) * 4) calc(var(--vk-u) * 3); }
846
+ .vmm-mono { font-family: var(--vk-mono); font-size: 11px; color: var(--vk-ink-3); overflow-wrap: anywhere; }
847
+
848
+ .vmm-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--vk-line); flex-wrap: wrap; }
849
+ .vmm-tab { height: 34px; padding: 0 calc(var(--vk-u) * 4); border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; background: transparent; color: var(--vk-ink-3); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer; }
850
+ .vmm-tab:hover { color: var(--vk-ink); }
851
+ .vmm-tab.on { color: var(--vk-ink); border-bottom-color: var(--vk-ember); }
852
+ .vmm-tab:focus-visible, .vmm-seg:focus-visible { outline: 2px solid var(--vk-signal); outline-offset: 1px; }
853
+
854
+ .vmm-segs { display: inline-flex; }
855
+ .vmm-seg { height: 26px; padding: 0 calc(var(--vk-u) * 3); border: 1px solid var(--vk-line-strong); background: var(--vk-panel); color: var(--vk-ink-2); font: inherit; font-size: 12px; cursor: pointer; }
856
+ .vmm-seg + .vmm-seg { margin-left: -1px; }
857
+ .vmm-seg.on { border-color: var(--vk-ink); background: var(--vk-ink); color: var(--vk-panel); position: relative; }
858
+ .vmm-seg:disabled { cursor: default; opacity: .55; }
859
+
860
+ .vmm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: calc(var(--vk-u) * 3); }
861
+ .vmm-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; font-size: 12px; color: var(--vk-ink-2); }
862
+ .vmm-input, .vmm-select, .vmm-textarea { border: 1px solid var(--vk-line-strong); border-radius: 0; background: var(--vk-panel); color: var(--vk-ink); font: inherit; font-size: 12px; min-width: 0; }
863
+ .vmm-input, .vmm-select { height: 28px; padding: 0 calc(var(--vk-u) * 2); }
864
+ .vmm-textarea { padding: calc(var(--vk-u) * 2); resize: vertical; line-height: 1.5; }
865
+ .vmm-textarea.file { font-family: var(--vk-mono); min-height: 360px; }
866
+ .vmm-input:focus, .vmm-select:focus, .vmm-textarea:focus { outline: 2px solid var(--vk-signal); outline-offset: -1px; }
867
+ .vmm-input[aria-invalid="true"] { border-color: var(--vk-bad); }
868
+ .vmm-check { display: flex; align-items: center; gap: calc(var(--vk-u) * 2); font-size: 12px; color: var(--vk-ink-2); cursor: pointer; }
869
+ .vmm-check input { margin: 0; accent-color: var(--vk-ember); }
870
+
871
+ .vmm-list { margin: 0; padding: 0; list-style: none; }
872
+ .vmm-row { display: flex; align-items: center; justify-content: space-between; gap: calc(var(--vk-u) * 3); padding: calc(var(--vk-u) * 3) calc(var(--vk-u) * 4); border-top: 1px solid var(--vk-line-soft); }
873
+ .vmm-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
874
+
875
+ .vmm-usage { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 3); }
876
+ .vmm-usage-head { display: flex; align-items: center; gap: calc(var(--vk-u) * 2); font-size: 12px; }
877
+ .vmm-usage-head span:first-child { font-weight: 500; }
878
+ .vmm-usage-head span:nth-child(2) { color: var(--vk-ink-3); font-family: var(--vk-mono); font-size: 11px; }
879
+ .vmm-bar { position: relative; height: 4px; margin-top: 4px; overflow: hidden; background: var(--vk-line-soft); }
880
+ .vmm-bar-fill { position: absolute; inset: 0 auto 0 0; background: var(--vk-ok); }
881
+ .vmm-bar-fill.warn { background: var(--vk-warn); }
882
+ .vmm-bar-fill.bad { background: var(--vk-bad); }
883
+
884
+ .vmm-tree { margin: 0; padding: 0; list-style: none; }
885
+ .vmm-node { padding: calc(var(--vk-u) * 2.5) calc(var(--vk-u) * 4); border-top: 1px solid var(--vk-line-soft); }
886
+ .vmm-node[data-level="1"] { background: var(--vk-sunken); }
887
+ .vmm-node[data-level="2"] { padding-left: calc(var(--vk-u) * 8); }
888
+ .vmm-node[data-level="3"] { padding-left: calc(var(--vk-u) * 12); }
889
+ .vmm-node[data-level="4"] { padding-left: calc(var(--vk-u) * 16); }
890
+ .vmm-node-head { display: flex; align-items: center; gap: calc(var(--vk-u) * 2); flex-wrap: wrap; }
891
+ .vmm-node-head small { color: var(--vk-ink-3); font-size: 11px; }
892
+ .vmm-node-acts { margin-left: auto; display: inline-flex; gap: 2px; }
893
+ .vmm-node-body { margin: 4px 0 0; color: var(--vk-ink-2); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
894
+
895
+ .vmm-dialog-backdrop { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: calc(var(--vk-u) * 6); background: rgba(10, 12, 15, 0.42); }
896
+ .vmm-dialog { display: flex; flex-direction: column; width: min(560px, 100%); max-height: 100%; border: 1px solid var(--vk-line); background: var(--vk-panel); box-shadow: var(--vk-shadow-2); }
897
+ .vmm-dialog.wide { width: min(820px, 100%); }
898
+ .vmm-dialog-head { padding: calc(var(--vk-u) * 4) calc(var(--vk-u) * 4) 0; }
899
+ .vmm-dialog-head h3 { margin: 0; font-size: 15px; font-weight: 600; }
900
+ .vmm-dialog-head p { margin: calc(var(--vk-u) * 1) 0 0; color: var(--vk-ink-3); font-size: 12px; }
901
+ .vmm-dialog-body { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: calc(var(--vk-u) * 3); padding: calc(var(--vk-u) * 4); }
902
+ .vmm-dialog-foot { display: flex; justify-content: flex-end; gap: calc(var(--vk-u) * 2); padding: calc(var(--vk-u) * 3) calc(var(--vk-u) * 4); border-top: 1px solid var(--vk-line-soft); }
903
+
904
+ .vmm-import-project { display: flex; align-items: center; justify-content: space-between; gap: calc(var(--vk-u) * 3); padding: calc(var(--vk-u) * 2) 0; border-top: 1px solid var(--vk-line-soft); }
905
+ .vmm-import-project > div { display: flex; flex-direction: column; min-width: 0; }
906
+ .vmm-import-scope { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 2); padding-top: calc(var(--vk-u) * 2); border-top: 1px solid var(--vk-line-soft); }
907
+ .vmm-import-scope h4 { margin: 0; font-size: 13px; }
908
+ .vmm-conflict { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 2); padding: calc(var(--vk-u) * 2.5); border: 1px solid var(--vk-line); }
909
+ .vmm-conflict-sides { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--vk-u) * 2); }
910
+ .vmm-conflict-side { display: flex; flex-direction: column; gap: 4px; min-width: 0; font-size: 12px; }
911
+ .vmm-conflict-side span { color: var(--vk-ink-3); font-size: 11px; }
912
+ .vmm-conflict-side pre { margin: 0; padding: calc(var(--vk-u) * 2); border: 1px solid var(--vk-line-soft); background: var(--vk-sunken); font-family: var(--vk-mono); font-size: 11px; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 220px; overflow-y: auto; }
913
+ `;
914
+ /** Add the stylesheet once per page; returns a function that removes it again. */
915
+ function installStyle() {
916
+ const existing = document.querySelector('style[data-plugin="vulcan-memory"]');
917
+ if (existing !== null)
918
+ existing.remove();
919
+ const tag = document.createElement('style');
920
+ tag.dataset['plugin'] = 'vulcan-memory';
921
+ tag.textContent = CSS;
922
+ document.head.appendChild(tag);
923
+ return () => { tag.remove(); };
924
+ }
925
+
926
+ },
927
+ "./transfer": function (module, exports, require) {
928
+ "use strict";
929
+ Object.defineProperty(exports, "__esModule", { value: true });
930
+ exports.TransferSection = TransferSection;
931
+ const jsx_runtime_1 = require("react/jsx-runtime");
932
+ /**
933
+ * Import and export (`markdown-memory-transfer.tsx`). VulcanCode picks native folders and files on
934
+ * the Host; the dsh page may run on another machine, so the package is downloaded by the browser
935
+ * on export and read from a browser file on import. The archive bytes go with both the preview and
936
+ * the apply request, and the preview's SHA-256 still binds them together.
937
+ */
938
+ const react_1 = require("react");
939
+ const i18n_1 = require("./i18n");
940
+ const parts_1 = require("./parts");
941
+ const shared_1 = require("./shared");
942
+ const CHOICES = ['keep_local', 'take_imported', 'keep_both'];
943
+ function TransferSection({ t, onImported }) {
944
+ const [exporting, setExporting] = (0, react_1.useState)(false);
945
+ const [openImport, setOpenImport] = (0, react_1.useState)(undefined);
946
+ const [reading, setReading] = (0, react_1.useState)(false);
947
+ const [note, setNote] = (0, react_1.useState)(undefined);
948
+ const picker = (0, react_1.useRef)(null);
949
+ const inspect = (event) => {
950
+ const file = event.target.files?.[0];
951
+ event.target.value = '';
952
+ if (file === undefined)
953
+ return;
954
+ setReading(true);
955
+ setNote(undefined);
956
+ void (async () => {
957
+ const archive = (0, shared_1.toBase64)(new Uint8Array(await file.arrayBuffer()));
958
+ const [preview, overview] = await Promise.all([
959
+ (0, shared_1.callHost)('POST', 'import/preview', { archive, name: file.name, projects: [] }),
960
+ (0, shared_1.loadOverview)({}),
961
+ ]);
962
+ setOpenImport({ name: file.name, archive, preview, localProjects: overview.projects });
963
+ })().catch((failure) => { setNote({ ok: false, text: (0, shared_1.messageOf)(failure) }); }).finally(() => { setReading(false); });
964
+ };
965
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Section, { title: t('transfer.title'), lead: t('transfer.hint'), actions: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", onClick: () => { setExporting(true); }, children: t('transfer.export') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: reading, onClick: () => { picker.current?.click(); }, children: reading ? t('transfer.previewing') : t('transfer.import') }), (0, jsx_runtime_1.jsx)("input", { ref: picker, type: "file", accept: ".zip,application/zip", hidden: true, onChange: inspect })] })), children: [note === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: note.ok ? 'vac-hint' : 'vac-error', role: "status", children: note.text }), exporting ? ((0, jsx_runtime_1.jsx)(ExportDialog, { t: t, close: () => { setExporting(false); }, done: (name) => { setExporting(false); setNote({ ok: true, text: (0, i18n_1.fill)(t('transfer.exported'), { name }) }); } })) : null, openImport === undefined ? null : ((0, jsx_runtime_1.jsx)(ImportDialog, { t: t, open: openImport, close: () => { setOpenImport(undefined); }, onImported: onImported }, openImport.preview.sha256))] }));
966
+ }
967
+ function ExportDialog({ t, close, done }) {
968
+ const [user, setUser] = (0, react_1.useState)(true);
969
+ const [space, setSpace] = (0, react_1.useState)(true);
970
+ const [projectIds, setProjectIds] = (0, react_1.useState)([]);
971
+ const [projects, setProjects] = (0, react_1.useState)(undefined);
972
+ const [busy, setBusy] = (0, react_1.useState)(false);
973
+ const [error, setError] = (0, react_1.useState)(undefined);
974
+ (0, react_1.useEffect)(() => {
975
+ let current = true;
976
+ (0, shared_1.loadOverview)({}).then((overview) => { if (current)
977
+ setProjects(overview.projects); }, (failure) => { if (current)
978
+ setError((0, shared_1.messageOf)(failure)); });
979
+ return () => { current = false; };
980
+ }, []);
981
+ const empty = !user && !space && projectIds.length === 0;
982
+ const run = () => {
983
+ setBusy(true);
984
+ setError(undefined);
985
+ (0, shared_1.callHost)('POST', 'export', { user, space, memoryProjectIds: projectIds }).then((exported) => {
986
+ (0, shared_1.download)(exported.fileName, (0, shared_1.fromBase64)(exported.archive));
987
+ done(exported.fileName);
988
+ }, (failure) => { setError((0, shared_1.messageOf)(failure)); }).finally(() => { setBusy(false); });
989
+ };
990
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Dialog, { title: t('transfer.exportTitle'), lead: t('transfer.exportHint'), busy: busy, close: close, footer: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", disabled: busy, onClick: close, children: t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", disabled: busy || empty, onClick: run, children: t('transfer.download') })] })), children: [(0, jsx_runtime_1.jsxs)("label", { className: "vmm-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: user, onChange: (event) => { setUser(event.target.checked); } }), t('nav.user')] }), (0, jsx_runtime_1.jsxs)("label", { className: "vmm-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: space, onChange: (event) => { setSpace(event.target.checked); } }), t('nav.space')] }), projects === undefined ? (error === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('loading') }) : null) : projects.map(project => ((0, jsx_runtime_1.jsxs)("label", { className: "vmm-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: projectIds.includes(project.id), onChange: (event) => { setProjectIds(event.target.checked ? [...projectIds, project.id] : projectIds.filter(id => id !== project.id)); } }), (0, jsx_runtime_1.jsxs)("span", { children: [(0, i18n_1.fill)(t('transfer.projectScope'), { name: project.name }), project.paths[0] === undefined ? null : (0, jsx_runtime_1.jsxs)("span", { className: "vmm-mono", children: [" ", project.paths[0]] })] })] }, project.id))), error === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-error", role: "alert", children: error })] }));
991
+ }
992
+ function ImportDialog({ t, open, close, onImported }) {
993
+ const { archive, name, preview: initial, localProjects } = open;
994
+ const hasProjects = initial.projects.length > 0;
995
+ // Each packaged project's choice: `link:<id>`, `create`, `ignore`, or '' while unset. Candidates
996
+ // are only listed first as hints; nothing is preselected.
997
+ const [targets, setTargets] = (0, react_1.useState)(() => Object.fromEntries(initial.projects.map(entry => [entry.project.id, ''])));
998
+ const [review, setReview] = (0, react_1.useState)(hasProjects ? undefined : initial);
999
+ const [choices, setChoices] = (0, react_1.useState)(() => (hasProjects ? {} : defaultChoices(initial)));
1000
+ const [report, setReport] = (0, react_1.useState)(undefined);
1001
+ const [busy, setBusy] = (0, react_1.useState)(false);
1002
+ const [error, setError] = (0, react_1.useState)(undefined);
1003
+ const mappings = () => initial.projects.map(({ project }) => {
1004
+ const value = targets[project.id] ?? '';
1005
+ return {
1006
+ packageProjectId: project.id,
1007
+ target: value.startsWith('link:') ? { action: 'link', memoryProjectId: value.slice('link:'.length) }
1008
+ : value === 'create' ? { action: 'create' } : { action: 'ignore' },
1009
+ };
1010
+ });
1011
+ const run = (task) => {
1012
+ setBusy(true);
1013
+ setError(undefined);
1014
+ task().catch((failure) => { setError((0, shared_1.messageOf)(failure)); }).finally(() => { setBusy(false); });
1015
+ };
1016
+ const toReview = () => {
1017
+ run(async () => {
1018
+ const next = await (0, shared_1.callHost)('POST', 'import/preview', { archive, name, projects: mappings() });
1019
+ setChoices(defaultChoices(next));
1020
+ setReview(next);
1021
+ });
1022
+ };
1023
+ const apply = () => {
1024
+ run(async () => {
1025
+ if (review === undefined)
1026
+ return;
1027
+ const resolutions = review.scopes.flatMap(scope => scope.conflicts.map(conflict => ({
1028
+ scope: scope.scope,
1029
+ ...scope.packageProjectId === null ? {} : { packageProjectId: scope.packageProjectId },
1030
+ nodeId: conflict.id,
1031
+ choice: choices[conflictKey(scope, conflict)] ?? 'keep_local',
1032
+ })));
1033
+ setReport(await (0, shared_1.callHost)('POST', 'import', { archive, name, expectedSha256: review.sha256, projects: mappings(), resolutions }));
1034
+ onImported();
1035
+ });
1036
+ };
1037
+ const fillUnset = (value) => { setTargets(Object.fromEntries(Object.entries(targets).map(([id, current]) => [id, current || value]))); };
1038
+ const unset = Object.values(targets).some(value => value === '');
1039
+ const projectName = (packageProjectId) => initial.projects.find(entry => entry.project.id === packageProjectId)?.project.name ?? packageProjectId ?? '';
1040
+ let body;
1041
+ let footer;
1042
+ if (report !== undefined) {
1043
+ body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("p", { children: (0, i18n_1.fill)(t('transfer.report'), { added: report.added, replaced: report.replaced, unchanged: report.unchanged }) }), report.createdProjects.length === 0 ? null : (0, jsx_runtime_1.jsx)("p", { children: (0, i18n_1.fill)(t('transfer.reportCreated'), { count: report.createdProjects.length }) }), report.backupDirectory === null ? null : (0, jsx_runtime_1.jsxs)("p", { children: [t('transfer.reportBackup'), (0, jsx_runtime_1.jsx)("code", { className: "vmm-mono", children: report.backupDirectory })] })] }));
1044
+ footer = (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", onClick: close, children: t('transfer.done') });
1045
+ }
1046
+ else if (review === undefined) {
1047
+ body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('transfer.projectsHint') }), initial.projects.map(({ project, candidates }) => {
1048
+ const candidateIds = new Set(candidates.map(candidate => candidate.memoryProjectId));
1049
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "vmm-import-project", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: project.name }), project.paths[0] === undefined ? null : (0, jsx_runtime_1.jsx)("span", { className: "vmm-mono", children: project.paths[0] })] }), (0, jsx_runtime_1.jsxs)("select", { className: "vmm-select", "aria-label": (0, i18n_1.fill)(t('transfer.targetFor'), { name: project.name }), disabled: busy, value: targets[project.id] ?? '', onChange: (event) => { setTargets({ ...targets, [project.id]: event.target.value }); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "", disabled: true, children: t('transfer.targetUnset') }), candidates.map(candidate => ((0, jsx_runtime_1.jsx)("option", { value: `link:${candidate.memoryProjectId}`, children: (0, i18n_1.fill)(t('transfer.linkCandidate'), { name: candidate.name, reasons: candidate.reasons.map(reason => (0, i18n_1.reasonLabel)(t, reason)).join(', ') }) }, candidate.memoryProjectId))), localProjects.filter(local => !candidateIds.has(local.id)).map(local => ((0, jsx_runtime_1.jsx)("option", { value: `link:${local.id}`, children: (0, i18n_1.fill)(t('transfer.link'), { name: local.name }) }, local.id))), (0, jsx_runtime_1.jsx)("option", { value: "create", children: t('transfer.create') }), (0, jsx_runtime_1.jsx)("option", { value: "ignore", children: t('transfer.ignore') })] })] }, project.id));
1050
+ }), unset ? ((0, jsx_runtime_1.jsxs)("span", { className: "vac-inline", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: busy, onClick: () => { fillUnset('create'); }, children: t('transfer.createRest') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn sm", disabled: busy, onClick: () => { fillUnset('ignore'); }, children: t('transfer.ignoreRest') })] })) : null] }));
1051
+ footer = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", disabled: busy, onClick: close, children: t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", disabled: busy || unset, onClick: toReview, children: t('transfer.next') })] }));
1052
+ }
1053
+ else {
1054
+ body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [review.scopes.length === 0 ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('transfer.nothing') }) : null, review.scopes.map(scope => ((0, jsx_runtime_1.jsxs)("div", { className: "vmm-import-scope", children: [(0, jsx_runtime_1.jsx)("h4", { children: scope.scope === 'project' ? (0, i18n_1.fill)(t('transfer.projectScope'), { name: projectName(scope.packageProjectId) }) : t(`nav.${scope.scope}`) }), (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: (0, i18n_1.fill)(t('transfer.counts'), { same: scope.same, added: scope.added.length, conflicts: scope.conflicts.length }) }), scope.newSections.length === 0 ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: (0, i18n_1.fill)(t('transfer.newSections'), { titles: scope.newSections.join(', ') }) }), scope.duplicates.length === 0 ? null : ((0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: (0, i18n_1.fill)(t('transfer.duplicates'), { titles: scope.duplicates.map(node => node.path.join(' › ')).join(', ') }) })), scope.conflicts.map((conflict) => {
1055
+ const key = conflictKey(scope, conflict);
1056
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "vmm-conflict", children: [(0, jsx_runtime_1.jsxs)("span", { className: "vac-inline", children: [(0, jsx_runtime_1.jsx)("strong", { children: conflict.path.join(' › ') }), conflict.newer === 'unknown' ? null : ((0, jsx_runtime_1.jsx)("span", { className: "vac-badge info", children: t(conflict.newer === 'imported' ? 'transfer.newerImported' : 'transfer.newerLocal') }))] }), (0, jsx_runtime_1.jsx)(parts_1.Segments, { value: choices[key] ?? 'keep_local', label: conflict.path.join(' › '), disabled: busy, onChange: (value) => { setChoices({ ...choices, [key]: value }); }, options: CHOICES.map(value => ({ value, label: t(`transfer.choice.${value}`) })) }), (0, jsx_runtime_1.jsxs)("details", { children: [(0, jsx_runtime_1.jsx)("summary", { className: "vac-hint", children: t('transfer.compare') }), (0, jsx_runtime_1.jsxs)("div", { className: "vmm-conflict-sides", children: [(0, jsx_runtime_1.jsx)(ConflictSide, { t: t, label: t('transfer.local'), side: conflict.local }), (0, jsx_runtime_1.jsx)(ConflictSide, { t: t, label: t('transfer.imported'), side: conflict.imported })] })] })] }, key));
1057
+ })] }, `${scope.scope}:${scope.packageProjectId ?? ''}`)))] }));
1058
+ footer = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn", disabled: busy, onClick: hasProjects ? () => { setReview(undefined); } : close, children: hasProjects ? t('transfer.back') : t('cancel') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", disabled: busy || review.scopes.length === 0, onClick: apply, children: t('transfer.apply') })] }));
1059
+ }
1060
+ return ((0, jsx_runtime_1.jsxs)(parts_1.Dialog, { title: t('transfer.importTitle'), lead: (0, i18n_1.fill)(t('transfer.importHint'), { time: initial.exportedAt }), wide: true, busy: busy, close: close, footer: footer, children: [(0, jsx_runtime_1.jsx)("code", { className: "vmm-mono", children: name }), body, error === undefined ? null : (0, jsx_runtime_1.jsx)("p", { className: "vac-error", role: "alert", children: error })] }));
1061
+ }
1062
+ function ConflictSide({ t, label, side }) {
1063
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "vmm-conflict-side", children: [(0, jsx_runtime_1.jsxs)("span", { children: [label, side.updated === null ? '' : ` · ${(0, i18n_1.fill)(t('node.updated'), { time: side.updated })}`] }), (0, jsx_runtime_1.jsx)("strong", { children: side.title }), (0, jsx_runtime_1.jsx)("pre", { children: side.body })] }));
1064
+ }
1065
+ function conflictKey(scope, conflict) {
1066
+ return `${scope.scope}:${scope.packageProjectId ?? ''}:${conflict.id}`;
1067
+ }
1068
+ /** Every conflict starts at its newer side, keeping the local note when unknown. */
1069
+ function defaultChoices(preview) {
1070
+ return Object.fromEntries(preview.scopes.flatMap(scope => scope.conflicts.map(conflict => [
1071
+ conflictKey(scope, conflict),
1072
+ conflict.newer === 'imported' ? 'take_imported' : 'keep_local',
1073
+ ])));
1074
+ }
1075
+
1076
+ },
1077
+ };
1078
+ var cache = {};
1079
+ function load(request) {
1080
+ if (!(request in definitions)) return platformRequire(request);
1081
+ if (!(request in cache)) {
1082
+ var module = { exports: {} };
1083
+ cache[request] = module;
1084
+ definitions[request](module, module.exports, load);
1085
+ }
1086
+ return cache[request].exports;
1087
+ }
1088
+ return load('./index');
1089
+ }
1090
+ });