@icen.ai/ui 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/AGENTS.md +138 -80
  2. package/README.md +66 -91
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +1 -1
  6. package/dist/behaviors/ai-branch.mjs +1010 -94
  7. package/dist/behaviors/ai-chat.d.mts +7 -7
  8. package/dist/behaviors/ai-chat.mjs +1071 -161
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +1997 -524
  11. package/dist/behaviors/ai-core.d.mts +1 -1
  12. package/dist/behaviors/ai-core.mjs +1016 -107
  13. package/dist/behaviors/ai-diff.mjs +1019 -127
  14. package/dist/behaviors/ai-feedback.d.mts +1 -1
  15. package/dist/behaviors/ai-feedback.mjs +1035 -126
  16. package/dist/behaviors/ai-panel.d.mts +8 -8
  17. package/dist/behaviors/ai-panel.mjs +1284 -266
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +1 -1
  20. package/dist/behaviors/ai-threads.mjs +1068 -154
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2037 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4448 -372
  25. package/dist/behaviors/autocomplete.d.mts +53 -0
  26. package/dist/behaviors/autocomplete.mjs +1600 -0
  27. package/dist/behaviors/back-top.d.mts +1 -1
  28. package/dist/behaviors/back-top.mjs +951 -14
  29. package/dist/behaviors/carousel.d.mts +1 -1
  30. package/dist/behaviors/carousel.mjs +906 -11
  31. package/dist/behaviors/cascader.d.mts +48 -0
  32. package/dist/behaviors/cascader.mjs +1853 -0
  33. package/dist/behaviors/charts.d.mts +72 -8
  34. package/dist/behaviors/charts.mjs +1901 -256
  35. package/dist/behaviors/code-block.d.mts +36 -0
  36. package/dist/behaviors/code-block.mjs +1611 -0
  37. package/dist/behaviors/command-palette.d.mts +9 -6
  38. package/dist/behaviors/command-palette.mjs +161 -59
  39. package/dist/behaviors/context-menu.d.mts +9 -8
  40. package/dist/behaviors/context-menu.mjs +1112 -44
  41. package/dist/behaviors/controls.d.mts +13 -13
  42. package/dist/behaviors/controls.mjs +989 -130
  43. package/dist/behaviors/copy.d.mts +21 -2
  44. package/dist/behaviors/copy.mjs +1206 -42
  45. package/dist/behaviors/datatable.d.mts +53 -3
  46. package/dist/behaviors/datatable.mjs +1963 -207
  47. package/dist/behaviors/date-picker.d.mts +0 -1
  48. package/dist/behaviors/date-picker.mjs +1340 -293
  49. package/dist/behaviors/design.d.mts +54 -0
  50. package/dist/behaviors/design.mjs +109 -0
  51. package/dist/behaviors/dropdown.d.mts +5 -6
  52. package/dist/behaviors/dropdown.mjs +262 -110
  53. package/dist/behaviors/events.d.mts +120 -3
  54. package/dist/behaviors/events.mjs +47 -2
  55. package/dist/behaviors/feedback.d.mts +30 -0
  56. package/dist/behaviors/feedback.mjs +296 -0
  57. package/dist/behaviors/focus.d.mts +8 -0
  58. package/dist/behaviors/focus.mjs +24 -0
  59. package/dist/behaviors/form-validate.d.mts +102 -0
  60. package/dist/behaviors/form-validate.mjs +6091 -0
  61. package/dist/behaviors/i18n.d.mts +789 -0
  62. package/dist/behaviors/i18n.mjs +1711 -0
  63. package/dist/behaviors/icons.d.mts +93 -0
  64. package/dist/behaviors/icons.mjs +113 -0
  65. package/dist/behaviors/input.d.mts +9 -1
  66. package/dist/behaviors/input.mjs +1056 -60
  67. package/dist/behaviors/kb-access.d.mts +1 -1
  68. package/dist/behaviors/kb-access.mjs +2129 -239
  69. package/dist/behaviors/kb-acl.d.mts +6 -6
  70. package/dist/behaviors/kb-acl.mjs +1119 -179
  71. package/dist/behaviors/kb-answer.mjs +2070 -418
  72. package/dist/behaviors/kb-audit.d.mts +1 -1
  73. package/dist/behaviors/kb-audit.mjs +1076 -154
  74. package/dist/behaviors/kb-canvas.mjs +1100 -177
  75. package/dist/behaviors/kb-chain.mjs +1022 -100
  76. package/dist/behaviors/kb-checkpoint.d.mts +1 -1
  77. package/dist/behaviors/kb-checkpoint.mjs +1319 -169
  78. package/dist/behaviors/kb-chunks.mjs +1152 -213
  79. package/dist/behaviors/kb-citation.d.mts +1 -1
  80. package/dist/behaviors/kb-citation.mjs +1201 -179
  81. package/dist/behaviors/kb-clarify.mjs +1058 -130
  82. package/dist/behaviors/kb-conflict.mjs +1041 -119
  83. package/dist/behaviors/kb-connector.mjs +1104 -176
  84. package/dist/behaviors/kb-core.d.mts +6 -1
  85. package/dist/behaviors/kb-core.mjs +1077 -162
  86. package/dist/behaviors/kb-eval.mjs +1971 -144
  87. package/dist/behaviors/kb-explain.d.mts +1 -1
  88. package/dist/behaviors/kb-explain.mjs +1024 -102
  89. package/dist/behaviors/kb-filter.mjs +2008 -178
  90. package/dist/behaviors/kb-gap.d.mts +1 -1
  91. package/dist/behaviors/kb-gap.mjs +1043 -121
  92. package/dist/behaviors/kb-hittest.d.mts +1 -1
  93. package/dist/behaviors/kb-hittest.mjs +1093 -150
  94. package/dist/behaviors/kb-metadata.mjs +1052 -130
  95. package/dist/behaviors/kb-passage.d.mts +2 -2
  96. package/dist/behaviors/kb-passage.mjs +1101 -161
  97. package/dist/behaviors/kb-pipeline.d.mts +2 -2
  98. package/dist/behaviors/kb-pipeline.mjs +1082 -162
  99. package/dist/behaviors/kb-qa.mjs +1088 -160
  100. package/dist/behaviors/kb-rerank.mjs +1047 -114
  101. package/dist/behaviors/kb-retrieval.mjs +2063 -224
  102. package/dist/behaviors/kb-review.mjs +1990 -159
  103. package/dist/behaviors/kb-sandbox.d.mts +1 -1
  104. package/dist/behaviors/kb-sandbox.mjs +1026 -104
  105. package/dist/behaviors/kb-segment.mjs +1111 -179
  106. package/dist/behaviors/kb-select.d.mts +24 -0
  107. package/dist/behaviors/kb-select.mjs +1989 -0
  108. package/dist/behaviors/kb-sources.d.mts +1 -1
  109. package/dist/behaviors/kb-sources.mjs +1084 -170
  110. package/dist/behaviors/kb-sql.d.mts +1 -1
  111. package/dist/behaviors/kb-sql.mjs +1143 -226
  112. package/dist/behaviors/kb-trace.d.mts +1 -1
  113. package/dist/behaviors/kb-trace.mjs +1042 -128
  114. package/dist/behaviors/kb-visibility.mjs +1086 -158
  115. package/dist/behaviors/kb-who-can.mjs +1974 -137
  116. package/dist/behaviors/mentions.d.mts +93 -0
  117. package/dist/behaviors/mentions.mjs +2152 -0
  118. package/dist/behaviors/modal.d.mts +4 -3
  119. package/dist/behaviors/modal.mjs +1055 -33
  120. package/dist/behaviors/nav.d.mts +1 -1
  121. package/dist/behaviors/nav.mjs +6 -6
  122. package/dist/behaviors/notification.d.mts +16 -8
  123. package/dist/behaviors/notification.mjs +1615 -265
  124. package/dist/behaviors/panel.d.mts +4 -0
  125. package/dist/behaviors/panel.mjs +994 -0
  126. package/dist/behaviors/popover.d.mts +28 -5
  127. package/dist/behaviors/popover.mjs +225 -24
  128. package/dist/behaviors/select.d.mts +5 -2
  129. package/dist/behaviors/select.mjs +1410 -117
  130. package/dist/behaviors/sidebar.d.mts +1 -1
  131. package/dist/behaviors/sidebar.mjs +13 -13
  132. package/dist/behaviors/slider.d.mts +1 -1
  133. package/dist/behaviors/slider.mjs +9 -9
  134. package/dist/behaviors/split-pane.d.mts +1 -1
  135. package/dist/behaviors/split-pane.mjs +7 -7
  136. package/dist/behaviors/suggestions.d.mts +27 -0
  137. package/dist/behaviors/suggestions.mjs +1089 -0
  138. package/dist/behaviors/svg-parse.d.mts +14 -0
  139. package/dist/behaviors/svg-parse.mjs +37 -0
  140. package/dist/behaviors/tabs.d.mts +2 -2
  141. package/dist/behaviors/tabs.mjs +25 -25
  142. package/dist/behaviors/tag-input.d.mts +23 -5
  143. package/dist/behaviors/tag-input.mjs +1359 -104
  144. package/dist/behaviors/theme-switcher.d.mts +24 -0
  145. package/dist/behaviors/theme-switcher.mjs +1981 -0
  146. package/dist/behaviors/theme.d.mts +15 -8
  147. package/dist/behaviors/theme.mjs +390 -28
  148. package/dist/behaviors/toast.d.mts +24 -25
  149. package/dist/behaviors/toast.mjs +1266 -88
  150. package/dist/behaviors/tooltip.d.mts +4 -0
  151. package/dist/behaviors/tooltip.mjs +378 -0
  152. package/dist/behaviors/tree-select.d.mts +69 -0
  153. package/dist/behaviors/tree-select.mjs +1907 -0
  154. package/dist/behaviors/tree.d.mts +1 -1
  155. package/dist/behaviors/tree.mjs +15 -15
  156. package/dist/behaviors/upload.d.mts +3 -2
  157. package/dist/behaviors/upload.mjs +1076 -66
  158. package/dist/cli.mjs +5 -6
  159. package/dist/components/ai-chat.css +666 -573
  160. package/dist/components/ai-diff.css +153 -132
  161. package/dist/components/ai-panel.css +351 -343
  162. package/dist/components/ai-tool.css +393 -238
  163. package/dist/components/autocomplete.css +61 -0
  164. package/dist/components/autocomplete.mjs +3 -0
  165. package/dist/components/back-top.css +34 -31
  166. package/dist/components/badge.css +44 -41
  167. package/dist/components/breadcrumb.css +67 -46
  168. package/dist/components/btn.css +80 -74
  169. package/dist/components/card.css +117 -106
  170. package/dist/components/cascader.css +311 -0
  171. package/dist/components/cascader.mjs +3 -0
  172. package/dist/components/chart-area.css +8 -2
  173. package/dist/components/chart-bar.css +95 -33
  174. package/dist/components/chart-gauge.css +14 -11
  175. package/dist/components/chart-graph.css +20 -17
  176. package/dist/components/chart-heatmap.css +96 -90
  177. package/dist/components/chart-line.css +8 -3
  178. package/dist/components/chart-pie.css +9 -6
  179. package/dist/components/chart-radar.css +8 -5
  180. package/dist/components/chart-scatter.css +9 -6
  181. package/dist/components/chart-sparkline.css +5 -2
  182. package/dist/components/chart-stack.css +14 -11
  183. package/dist/components/charts.css +164 -73
  184. package/dist/components/code-block.css +418 -0
  185. package/dist/components/code-block.mjs +4 -0
  186. package/dist/components/command-palette.css +138 -117
  187. package/dist/components/content.css +257 -251
  188. package/dist/components/copy.css +72 -44
  189. package/dist/components/datatable.css +423 -181
  190. package/dist/components/date-picker.css +135 -104
  191. package/dist/components/empty.css +68 -65
  192. package/dist/components/feedback.css +201 -183
  193. package/dist/components/form-validate.css +120 -0
  194. package/dist/components/form-validate.mjs +3 -0
  195. package/dist/components/form.css +150 -127
  196. package/dist/components/icon.css +102 -0
  197. package/dist/components/icon.mjs +3 -0
  198. package/dist/components/input.css +159 -104
  199. package/dist/components/kb-agent.css +188 -173
  200. package/dist/components/kb-data.css +257 -253
  201. package/dist/components/kb-ground.css +197 -185
  202. package/dist/components/kb-ingest.css +334 -350
  203. package/dist/components/kb-ops.css +265 -259
  204. package/dist/components/kb-perm.css +260 -271
  205. package/dist/components/kb-search.css +317 -360
  206. package/dist/components/kb.css +120 -109
  207. package/dist/components/layout.css +85 -82
  208. package/dist/components/material-chinese.css +73 -0
  209. package/dist/components/material-soviet.css +76 -0
  210. package/dist/components/material.css +275 -0
  211. package/dist/components/media.css +220 -166
  212. package/dist/components/mentions.css +189 -0
  213. package/dist/components/mentions.mjs +3 -0
  214. package/dist/components/menu.css +113 -105
  215. package/dist/components/modal.css +107 -112
  216. package/dist/components/nav.css +118 -115
  217. package/dist/components/notification.css +238 -174
  218. package/dist/components/pagination.css +56 -53
  219. package/dist/components/panel.css +66 -63
  220. package/dist/components/panel.mjs +1 -0
  221. package/dist/components/pill.css +81 -71
  222. package/dist/components/popover.css +67 -53
  223. package/dist/components/scroll-area.css +113 -110
  224. package/dist/components/segmented.css +146 -143
  225. package/dist/components/select.css +206 -186
  226. package/dist/components/sidebar.css +137 -127
  227. package/dist/components/slider.css +117 -114
  228. package/dist/components/split-pane.css +112 -109
  229. package/dist/components/stat.css +112 -106
  230. package/dist/components/steps.css +109 -103
  231. package/dist/components/switch.css +138 -134
  232. package/dist/components/table.css +131 -107
  233. package/dist/components/tabs.css +114 -108
  234. package/dist/components/tag-input.css +48 -133
  235. package/dist/components/tag.css +81 -71
  236. package/dist/components/theme-switcher.css +105 -0
  237. package/dist/components/theme-switcher.mjs +3 -0
  238. package/dist/components/toast.css +100 -86
  239. package/dist/components/toolbar.css +45 -42
  240. package/dist/components/tooltip.css +102 -43
  241. package/dist/components/tooltip.mjs +1 -0
  242. package/dist/components/tree-select.css +416 -0
  243. package/dist/components/tree-select.mjs +3 -0
  244. package/dist/components/upload.css +135 -92
  245. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  246. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  247. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  248. package/dist/fonts/OFL.txt +96 -0
  249. package/dist/fonts/README.md +14 -0
  250. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  251. package/dist/index.d.mts +28 -14
  252. package/dist/index.mjs +20705 -9601
  253. package/dist/react.d.mts +91 -0
  254. package/dist/react.mjs +8208 -0
  255. package/dist/registry.json +130 -13
  256. package/dist/tokens/chinese.css +44 -0
  257. package/dist/tokens/colors.css +365 -1222
  258. package/dist/tokens/design.css +109 -0
  259. package/dist/tokens/dtcg.json +11198 -0
  260. package/dist/tokens/figma.variables.json +7017 -0
  261. package/dist/tokens/icon-masks.css +78 -0
  262. package/dist/tokens/pixel.css +50 -0
  263. package/dist/tokens/soviet.css +44 -0
  264. package/dist/tokens/style-profiles.css +36 -547
  265. package/dist/tokens/themes.json +308 -0
  266. package/dist/tokens/tokens-studio.json +6728 -0
  267. package/dist/tokens/typography.css +21 -18
  268. package/dist/tokens.css +1555 -1801
  269. package/dist/ui.css +11975 -9831
  270. package/dist/vue.d.mts +344 -0
  271. package/dist/vue.mjs +7781 -0
  272. package/docs/spec/ai-input-experience.md +75 -0
  273. package/docs/spec/ai-native.md +238 -147
  274. package/docs/spec/design-philosophy.md +189 -0
  275. package/docs/spec/design-primitives.md +196 -0
  276. package/docs/spec/kb-family.md +147 -115
  277. package/package.json +43 -8
  278. package/dist/retro-effects.css +0 -255
  279. package/dist/tokens/retro-effects.css +0 -255
  280. package/docs/research/2026-10-03-ai-kb-components.md +0 -392
  281. package/docs/research/2026-10-03-kb-permissions.md +0 -992
@@ -1,3 +1,989 @@
1
+ // src/behaviors/icons.ts
2
+ var ICON_PATHS = {
3
+ file: "M6 2.5h5.5L16 7v9.5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-12a2 2 0 0 1 2-2ZM11.5 2.5V7H16",
4
+ folder: "M3 5a1.5 1.5 0 0 1 1.5-1.5H8l2 2h5.5A1.5 1.5 0 0 1 17 7v8a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 3 15Z",
5
+ globe: "M17.5 10a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0ZM2.5 10h15M10 2.5c-4 4-4 11 0 15 4-4 4-11 0-15Z",
6
+ document: "M6 2.5h5.5L16 7v9.5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-12a2 2 0 0 1 2-2ZM11.5 2.5V7H16M7 10.5h6M7 13.5h6",
7
+ agent: "M10 6V3M8 3h2M5 6h10a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2ZM1.5 10h1.5M17 10h1.5M7 10v2M13 10v2M8 14h4",
8
+ paperclip: "M14 7 8.3 12.7a1.6 1.6 0 0 1-2.3-2.3l6.3-6.3a3.2 3.2 0 0 1 4.5 4.5l-7 7a4.7 4.7 0 0 1-6.7-6.7l7-7",
9
+ stop: "M6.5 5h7A1.5 1.5 0 0 1 15 6.5v7a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 5 13.5v-7A1.5 1.5 0 0 1 6.5 5Z",
10
+ terminal: "M5 3.5h10a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2ZM6 7l3 3-3 3M11 13h3",
11
+ branch: "M6 6v8M14 6v2a4 4 0 0 1-4 4H6M8 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 16a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM16 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z",
12
+ "line-numbers": "M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9",
13
+ wrap: "M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5",
14
+ calendar: "M5.5 4h9A2.5 2.5 0 0 1 17 6.5v8a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 3 14.5v-8A2.5 2.5 0 0 1 5.5 4ZM6.5 2.5v3M13.5 2.5v3M3 8h14M6.5 11h.1M10 11h.1M13.5 11h.1M6.5 14h.1M10 14h.1",
15
+ close: "M6 6l8 8M14 6l-8 8",
16
+ check: "M4.5 10.2l3.6 3.6 7.4-7.6",
17
+ "chevron-down": "M5.5 7.75 10 12.25l4.5-4.5",
18
+ "chevron-up": "M5.5 12.25 10 7.75l4.5 4.5",
19
+ "chevron-right": "M7.75 5.5 12.25 10l-4.5 4.5",
20
+ "chevron-left": "M12.25 5.5 7.75 10l4.5 4.5",
21
+ "arrow-right": "M3.5 10h13M11 4.5l5.5 5.5-5.5 5.5",
22
+ "arrow-left": "M16.5 10h-13M9 4.5 3.5 10 9 15.5",
23
+ "arrow-up": "M10 16.5v-13M4.5 9 10 3.5 15.5 9",
24
+ "arrow-down": "M10 3.5v13M4.5 11l5.5 5.5 5.5-5.5",
25
+ plus: "M10 4v12M4 10h12",
26
+ minus: "M4 10h12",
27
+ sort: "M6 15V4M3 7l3-3 3 3M14 5v11M11 13l3 3 3-3",
28
+ search: "M13.3 13.3 17 17M14.5 8.5a6 6 0 1 1-12 0 6 6 0 0 1 12 0",
29
+ sun: "M13.5 10a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0M10 1.75v1.5M10 16.75v1.5M1.75 10h1.5M16.75 10h1.5M4.17 4.17l1.06 1.06M14.77 14.77l1.06 1.06M4.17 15.83l1.06-1.06M14.77 5.23l1.06-1.06",
30
+ moon: "M17.25 11.15A7.5 7.5 0 0 1 8.85 2.75a7.5 7.5 0 1 0 8.4 8.4Z",
31
+ modern: "M6.5 3h7A3.5 3.5 0 0 1 17 6.5v7a3.5 3.5 0 0 1-3.5 3.5h-7A3.5 3.5 0 0 1 3 13.5v-7A3.5 3.5 0 0 1 6.5 3ZM7 10l3-3 3 3-3 3Z",
32
+ pixel: "M3 3h5v5H3ZM12 3h5v5h-5ZM3 12h5v5H3ZM12 12h5v5h-5Z",
33
+ chinese: "M3 8c3 0 5-2 7-5 2 3 4 5 7 5M4 8h12M6 8v8m8-8v8M3 16h14M8 16v-5h4v5",
34
+ soviet: "M3 17h14M5 17V8h4v9m0-12h6v12M7 8V5h2m2 3h2m-2 3h2m-2 3h2M10 5V3h4v2",
35
+ more: "M4 9.9v.2M10 9.9v.2M16 9.9v.2",
36
+ copy: "M7 7h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7ZM13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2",
37
+ edit: "M11.5 4.5l4 4M4 12l9-9a1.4 1.4 0 0 1 2 0l2 2a1.4 1.4 0 0 1 0 2l-9 9-5 1Z",
38
+ link: "M8 6l2-2a4.2 4.2 0 0 1 6 6l-2 2M12 14l-2 2a4.2 4.2 0 0 1-6-6l2-2M7 13l6-6",
39
+ trash: "M3.5 5.5h13M7 5.5V3h6v2.5M5 5.5l.7 11.5h8.6L15 5.5M8 9v5M12 9v5",
40
+ spinner: "M10 3a7 7 0 1 1-7 7",
41
+ "circle-check": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM6 10l2.7 2.7 5.3-5.4",
42
+ "circle-error": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM7.2 7.2l5.6 5.6M12.8 7.2l-5.6 5.6",
43
+ "circle-info": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM10 9v5M10 6.1v.15",
44
+ "triangle-alert": "M8.7 3.5a1.5 1.5 0 0 1 2.6 0l6.4 11.3a1.5 1.5 0 0 1-1.3 2.2H3.6a1.5 1.5 0 0 1-1.3-2.2ZM10 7.2v4.4M10 14v.15",
45
+ refresh: "M16.5 7.2A7 7 0 0 0 4.3 5.9L2.5 8M2.5 3.5V8H7M3.5 12.8a7 7 0 0 0 12.2 1.3l1.8-2.1M13 12h4.5v4.5",
46
+ external: "M11.5 3H17v5.5M17 3l-8 8M8 3H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3",
47
+ home: "M3.5 10.25 10 4l6.5 6.25M5.75 8.75v7a.75.75 0 0 0 .75.75h7.5a.75.75 0 0 0 .75-.75v-7",
48
+ settings: "M3.5 5.5h13M3.5 10h13M3.5 14.5h13M7 4v3M12.5 8.5v3M8 13v3",
49
+ star: "M10 2.5l2.4 5.1 5.6.4-4.2 3.7 1.3 5.5-5.1-3-5.1 3 1.3-5.5L1.8 8l5.6-.4Z",
50
+ eye: "M2.2 10s3-5.4 7.8-5.4S17.8 10 17.8 10s-3 5.4-7.8 5.4S2.2 10 2.2 10ZM10 12.4a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8Z",
51
+ "eye-off": "M2.2 10s3-5.4 7.8-5.4S17.8 10 17.8 10s-3 5.4-7.8 5.4S2.2 10 2.2 10ZM10 12.4a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8ZM3.5 3.5l13 13",
52
+ inbox: "M3.5 12.25h3.7l1.5 2.5h2.6l1.5-2.5h3.7M4.7 5.5l-1.2 6.75v3.25a1.5 1.5 0 0 0 1.5 1.5h10a1.5 1.5 0 0 0 1.5-1.5v-3.25L15.3 5.5a1.3 1.3 0 0 0-1.25-1H5.95a1.3 1.3 0 0 0-1.25 1Z",
53
+ bell: "M10 3.5a4.5 4.5 0 0 1 4.5 4.5c0 4.5 1.8 5.7 2.5 6.5H3c.7-.8 2.5-2 2.5-6.5A4.5 4.5 0 0 1 10 3.5ZM8.5 16.5a1.6 1.6 0 0 0 3 0",
54
+ users: "M13.8 16.5v-1.5a3.5 3.5 0 0 0-3.5-3.5H6.3a3.5 3.5 0 0 0-3.5 3.5v1.5M8.3 8.9a2.7 2.7 0 1 0 0-5.4 2.7 2.7 0 0 0 0 5.4ZM16.5 16.5v-1.3a3.3 3.3 0 0 0-2.3-3.1M12.3 3.7a2.7 2.7 0 0 1 0 5.2",
55
+ activity: "M17 10h-2.6l-2 5.8L8 4.2 6 10H3",
56
+ "trend-up": "M3.5 14.5l4.2-4.2 2.8 2.8 5.9-5.9M12.4 7.2h4.2v4.2",
57
+ "trend-down": "M3.5 5.5l4.2 4.2 2.8-2.8 5.9 5.9M12.4 12.8h4.2V8.6",
58
+ rocket: "M10 2.5c2 1.5 3.2 4 3.2 6.8 0 1.5-.3 2.9-.8 4.2H7.6c-.5-1.3-.8-2.7-.8-4.2 0-2.8 1.2-5.3 3.2-6.8ZM10 8.6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM7.6 12.5 5.6 15.4c-.9.3-1.6 1-2 2M12.4 12.5l2 2.9c.9.3 1.6 1 2 2M8.7 13.8c0 1.8.5 3.1 1.3 4.2.8-1.1 1.3-2.4 1.3-4.2",
59
+ bold: "M6.5 4h4.2a3 3 0 0 1 0 6H6.5M6.5 10h5a3 3 0 0 1 0 6H6.5M6.5 4v12",
60
+ italic: "M8.5 4.5h7M4.5 15.5h7M12.5 4.5l-5 11",
61
+ underline: "M6 4v5.5a4 4 0 0 0 8 0V4M4.5 16h11",
62
+ "align-left": "M3.5 4.5h13M3.5 10h8.5M3.5 15.5h11",
63
+ "align-center": "M3.5 4.5h13M5.75 10h8.5M4.5 15.5h11",
64
+ "align-right": "M3.5 4.5h13M8 10h8.5M5.5 15.5h11",
65
+ list: "M6.5 5h10M6.5 10h10M6.5 15h10M3.5 5h.01M3.5 10h.01M3.5 15h.01",
66
+ server: "M3 3h14a1.5 1.5 0 0 1 1.5 1.5v2A1.5 1.5 0 0 1 17 8H3a1.5 1.5 0 0 1-1.5-1.5v-2A1.5 1.5 0 0 1 3 3ZM3 12h14a1.5 1.5 0 0 1 1.5 1.5v2A1.5 1.5 0 0 1 17 17H3a1.5 1.5 0 0 1-1.5-1.5v-2A1.5 1.5 0 0 1 3 12ZM5 5.5h.01M5 14.5h.01",
67
+ sparkle: "M10 2.5l1.6 4.3 4.3 1.6-4.3 1.6L10 14.3l-1.6-4.3-4.3-1.6 4.3-1.6ZM15.5 12.5l.8 2.1 2.1.8-2.1.8-.8 2.1-.8-2.1-2.1-.8 2.1-.8Z",
68
+ play: "M6.5 4.8v10.4c0 .9 1 1.5 1.8 1l8.3-5.2a1.2 1.2 0 0 0 0-2L8.3 3.8a1.2 1.2 0 0 0-1.8 1Z",
69
+ "chart-line": "M3.5 3.5v13h13M6.5 12l2.8-2.8 2.2 2.2 4-4.4",
70
+ layers: "M10 3.2 3.6 6.5 10 9.8l6.4-3.3ZM4.5 10.4 10 13.2l5.5-2.8M4.5 13.6 10 16.4l5.5-2.8",
71
+ box: "M17 6 10 3.2 3 6v8l7 2.8 7-2.8ZM3 6l7 2.8L17 6M10 9v7.8",
72
+ filter: "M4 4.5h12l-4.4 5.1v5l-3.2-1.6V9.6Z",
73
+ columns: "M3.5 4h13v12h-13ZM8.2 4v12M12.8 4v12",
74
+ download: "M10 3.5v8M6.8 8.3 10 11.5l3.2-3.2M4 15v2.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5V15"
75
+ };
76
+ var custom = /* @__PURE__ */ new Map();
77
+ var escape = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
78
+ function iconPath(name) {
79
+ return Object.hasOwn(ICON_PATHS, name) ? ICON_PATHS[name] : custom.get(name) ?? null;
80
+ }
81
+ function iconAttributes(o = {}) {
82
+ const size = Number.isFinite(o.size) && o.size > 0 ? o.size : 16;
83
+ return { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", width: String(size), height: String(size), fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round", focusable: "false", class: ["icen-symbol", o.className].filter(Boolean).join(" "), ...o.label ? { role: "img", "aria-label": o.label } : { "aria-hidden": "true" } };
84
+ }
85
+ function iconHTML(name, o = {}) {
86
+ const path = iconPath(name);
87
+ if (!path) return "";
88
+ return `<svg${Object.entries(iconAttributes(o)).map(([k, v]) => ` ${k}="${escape(v)}"`).join("")}><path d="${escape(path)}"></path></svg>`;
89
+ }
90
+ function createIcon(name, o = {}) {
91
+ if (typeof document === "undefined") return null;
92
+ const path = iconPath(name);
93
+ if (!path) return null;
94
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
95
+ for (const [key, value] of Object.entries(iconAttributes(o))) svg.setAttribute(key, value);
96
+ const line = document.createElementNS(svg.namespaceURI, "path");
97
+ line.setAttribute("d", path);
98
+ svg.append(line);
99
+ return svg;
100
+ }
101
+
102
+ // src/behaviors/events.ts
103
+ function emitIcen(source, type, detail) {
104
+ return source.dispatchEvent(
105
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
106
+ );
107
+ }
108
+
109
+ // src/behaviors/i18n.ts
110
+ var ICEN_LOCALE_ZH_CN = {
111
+ /* ── aiBranch ── */
112
+ "aiBranch.nextVersion": "\u4E0B\u4E00\u7248\u672C",
113
+ "aiBranch.prevVersion": "\u4E0A\u4E00\u7248\u672C",
114
+ "aiBranch.version": "\u5206\u652F\u7248\u672C",
115
+ /* ── aiChat ── */
116
+ "aiChat.backToBottom": "\u56DE\u5230\u5E95\u90E8",
117
+ "aiChat.backToBottomCount": "\u56DE\u5230\u5E95\u90E8 \xB7 {p0} \u6761\u65B0\u6D88\u606F",
118
+ "aiChat.continue": "\u7EE7\u7EED\u751F\u6210",
119
+ "aiChat.download": "\u4E0B\u8F7D",
120
+ "aiChat.file": "\u6587\u4EF6",
121
+ "aiChat.roleSystem": "\u7CFB",
122
+ "aiChat.roleTool": "\u5DE5",
123
+ "aiChat.roleUser": "\u6211",
124
+ "aiChat.thinking": "\u601D\u8003 {p0}",
125
+ /* ── aiComposer ── */
126
+ "aiComposer.attachAgent": "\u4EE3\u7406",
127
+ "aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
128
+ "aiComposer.searchLibrary": "\u641C\u7D22\u6280\u80FD\u3001\u5DE5\u5177\u3001\u6587\u4EF6\u4E0E\u547D\u4EE4",
129
+ "aiComposer.all": "\u5168\u90E8",
130
+ "aiComposer.skills": "\u6280\u80FD",
131
+ "aiComposer.tools": "\u5DE5\u5177",
132
+ "aiComposer.libraryHint": "\u4E0A\u4E0B\u6587\u968F\u6D88\u606F\u53D1\u9001 \xB7 \u547D\u4EE4\u5148\u586B\u5165\u8F93\u5165\u6846",
133
+ "aiComposer.commandHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u6267\u884C \xB7 Esc \u8FD4\u56DE",
134
+ "aiComposer.refHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u5F15\u7528 \xB7 Esc \u8FD4\u56DE",
135
+ "aiComposer.noLibraryMatch": "\u6CA1\u6709\u5339\u914D\u7684\u9879\u76EE\uFF0C\u8BD5\u8BD5\u5176\u4ED6\u5173\u952E\u8BCD",
136
+ "aiComposer.filesNeedAdapter": "\u9644\u4EF6\u5C1A\u672A\u63A5\u901A\u4E0A\u4F20\u3002\u8BF7\u914D\u7F6E prepareMessage \u540E\u91CD\u8BD5\uFF0C\u6587\u4EF6\u4ECD\u4FDD\u7559\u5728\u6B64\u8BF7\u6C42\u4E2D\u3002",
137
+ "aiComposer.attachDoc": "\u6587\u6863",
138
+ "aiComposer.attachFile": "\u6587\u4EF6",
139
+ "aiComposer.attachFolder": "\u6587\u4EF6\u5939",
140
+ "aiComposer.cacheInvalidHint": "\u5207\u6362\u6A21\u578B\u4F1A\u4F7F prompt \u7F13\u5B58\u5931\u6548",
141
+ "aiComposer.commands": "\u547D\u4EE4",
142
+ "aiComposer.currentTodos": "\u5F53\u524D\u5F85\u529E",
143
+ "aiComposer.done": "\u5B8C\u6210",
144
+ "aiComposer.justNow": "\u521A\u521A \xB7 {p0}",
145
+ "aiComposer.modelAria": "\u6A21\u578B {p0}\uFF08{p1}\uFF09",
146
+ "aiComposer.noCommandMatch": "\u65E0\u5339\u914D\u547D\u4EE4",
147
+ "aiComposer.noModelMatch": "\u65E0\u5339\u914D\u6A21\u578B",
148
+ "aiComposer.noRefMatch": "\u65E0\u5339\u914D\u5F15\u7528",
149
+ "aiComposer.placeholder": "\u8F93\u5165\u6D88\u606F\uFF0C/ \u547D\u4EE4 \xB7 @ \u5F15\u7528 \xB7 Enter \u53D1\u9001",
150
+ "aiComposer.refs": "\u5F15\u7528",
151
+ "aiComposer.removeAttachment": "\u79FB\u9664\u9644\u4EF6 {p0}",
152
+ "aiComposer.removeQueued": "\u79FB\u9664\u6392\u961F\u6D88\u606F {p0}",
153
+ "aiComposer.removeRef": "\u79FB\u9664\u5F15\u7528 {p0}",
154
+ "aiComposer.searchModels": "\u641C\u7D22\u6A21\u578B",
155
+ "aiComposer.selectModel": "\u9009\u62E9\u6A21\u578B",
156
+ "aiComposer.send": "\u53D1\u9001",
157
+ "aiComposer.stop": "\u505C\u6B62\u751F\u6210",
158
+ "aiComposer.stopped": "\u5DF2\u505C\u6B62",
159
+ "aiComposer.todoChip": "\u5F53\u524D\u5F85\u529E {p0}/{p1}{p2}",
160
+ "aiComposer.todoHasError": "\uFF08\u6709\u5931\u8D25\u9879\uFF09",
161
+ /* ── aiCore ── */
162
+ "aiCore.kindBrowser": "\u6D4F\u89C8",
163
+ "aiCore.kindChart": "\u56FE\u8868",
164
+ "aiCore.kindDelete": "\u5220\u9664",
165
+ "aiCore.kindEdit": "\u7F16\u8F91",
166
+ "aiCore.kindFetch": "\u8BF7\u6C42",
167
+ "aiCore.kindGlob": "\u5339\u914D",
168
+ "aiCore.kindGrep": "\u641C\u7D22",
169
+ "aiCore.kindNote": "\u7B14\u8BB0",
170
+ "aiCore.kindPlan": "\u8BA1\u5212",
171
+ "aiCore.kindRead": "\u8BFB\u53D6",
172
+ "aiCore.kindSearch": "\u68C0\u7D22",
173
+ "aiCore.kindShell": "\u547D\u4EE4",
174
+ "aiCore.kindSkill": "\u6280\u80FD",
175
+ "aiCore.kindSubagent": "\u5B50\u4EE3\u7406",
176
+ "aiCore.kindTodo": "\u5F85\u529E",
177
+ "aiCore.kindWrite": "\u5199\u5165",
178
+ "aiCore.statusApproval": "\u5F85\u786E\u8BA4",
179
+ "aiCore.statusCancelled": "\u5DF2\u53D6\u6D88",
180
+ "aiCore.statusDone": "\u5B8C\u6210",
181
+ "aiCore.statusError": "\u5931\u8D25",
182
+ "aiCore.statusPending": "\u7B49\u5F85",
183
+ "aiCore.statusRunning": "\u6267\u884C\u4E2D",
184
+ "aiCore.statusStreaming": "\u6D41\u5F0F\u4E2D",
185
+ "aiCore.todoProgress": "{p0}/{p1} \u5B8C\u6210",
186
+ /* ── aiDiff ── */
187
+ "aiDiff.accepted": "\u5DF2\u63A5\u53D7",
188
+ "aiDiff.binary": "\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u663E\u793A\u6587\u672C\u5DEE\u5F02",
189
+ "aiDiff.empty": "\u65E0\u5DEE\u5F02\u5185\u5BB9",
190
+ "aiDiff.rejected": "\u5DF2\u62D2\u7EDD",
191
+ /* ── aiFeedback ── */
192
+ "aiFeedback.helpful": "\u6709\u5E2E\u52A9",
193
+ "aiFeedback.notHelpful": "\u65E0\u5E2E\u52A9",
194
+ "aiFeedback.reasonCitationMismatch": "\u5F15\u7528\u4E0D\u7B26",
195
+ "aiFeedback.reasonOutdated": "\u5185\u5BB9\u8FC7\u65F6",
196
+ "aiFeedback.reasonUnauthorized": "\u8D8A\u6743\u4FE1\u606F",
197
+ "aiFeedback.reasonUnsourced": "\u65E0\u5F15\u7528\u4F9D\u636E",
198
+ "aiFeedback.reasonWrong": "\u7B54\u6848\u6709\u8BEF",
199
+ "aiFeedback.thanks": "\u611F\u8C22\u53CD\u9988\uFF0C\u5DF2\u5173\u8054\u5230\u8BE5\u6761\u56DE\u7B54\u7684\u8FFD\u8E2A\u8BB0\u5F55",
200
+ /* ── aiPanel ── */
201
+ "aiPanel.audit": "\u5BA1\u8BA1",
202
+ "aiPanel.cacheRead": "\u7F13\u5B58\u8BFB",
203
+ "aiPanel.cacheWrite": "\u7F13\u5B58\u5199",
204
+ "aiPanel.callsTokens": "{p0} \u6B21 \xB7 {p1} tok{p2}",
205
+ "aiPanel.clearAudit": "\u6E05\u9664\u5BA1\u8BA1",
206
+ "aiPanel.context": "\u4E0A\u4E0B\u6587",
207
+ "aiPanel.contextWindow": "\u4E0A\u4E0B\u6587\u7A97\u53E3",
208
+ "aiPanel.errorsCount": "{p0} \u5931\u8D25",
209
+ "aiPanel.files": "\u6587\u4EF6 ({p0})",
210
+ "aiPanel.itemStatus": "{p0}\uFF0C\u72B6\u6001\uFF1A{p1}",
211
+ "aiPanel.noAudit": "\u6682\u65E0\u5BA1\u8BA1\u8BB0\u5F55",
212
+ "aiPanel.noUsage": "\u6682\u65E0\u7528\u91CF",
213
+ "aiPanel.reasoning": "\u63A8\u7406",
214
+ "aiPanel.requestsCount": "{p0} \u6B21\u8BF7\u6C42",
215
+ "aiPanel.ringPercent": "\u4E0A\u4E0B\u6587\u7A97\u53E3\u5360\u7528 {p0}%\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
216
+ "aiPanel.ringUsed": "\u5DF2\u7528 {p0}\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
217
+ "aiPanel.taskProgress": "\u4EFB\u52A1\u8FDB\u5EA6",
218
+ "aiPanel.toolsCount": "{p0} \u5DE5\u5177",
219
+ "aiPanel.usage": "\u7528\u91CF",
220
+ "aiPanel.usageAria": "\u7528\u91CF\uFF1A{p0}",
221
+ /* ── aiProvider ── */
222
+ "aiProvider.audioUrlOnly": "\u4EC5 URL \u7684\u97F3\u9891\uFF08input_audio \u9700 base64\uFF0C\u8BF7\u5148\u53D6\u56DE\u5185\u8054\uFF09",
223
+ "aiProvider.audioUrlOnlyAnthropic": "\u4EC5 URL \u7684\u97F3\u9891\uFF08audio \u5757\u9700 base64 source\uFF09",
224
+ "aiProvider.base64File": "base64 \u6587\u4EF6\uFF08file \u90E8\u4EF6\u9700 Files API file_id\uFF0C\u8BF7\u7F6E\u4E8E url \u69FD\uFF09",
225
+ "aiProvider.corsHint": "\uFF1B\u6D4F\u89C8\u5668\u76F4\u8FDE {p0}\uFF08{p1}\uFF09\u5F88\u53EF\u80FD\u88AB CORS \u62E6\u622A\uFF0C\u5EFA\u8BAE\u7ECF\u4EE3\u7406/\u81EA\u5EFA\u7F51\u5173\u8F6C\u53D1",
226
+ "aiProvider.emptyResponse": "\u54CD\u5E94\u4E3A\u7A7A",
227
+ "aiProvider.glmLabel": "GLM\uFF08\u667A\u8C31\uFF09",
228
+ "aiProvider.invalidJson": "\u54CD\u5E94\u4E0D\u662F\u5408\u6CD5 JSON",
229
+ "aiProvider.kimiLabel": "Kimi\uFF08\u6708\u4E4B\u6697\u9762\uFF09",
230
+ "aiProvider.networkFailed": "\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF1A{p0}{p1}",
231
+ "aiProvider.nonPdfDocument": "\u975E PDF \u6587\u4EF6\u5757\uFF08document \u5757\u4E3A application/pdf\uFF0C\u6536\u5230 {p0}\uFF09",
232
+ "aiProvider.requestCancelled": "\u8BF7\u6C42\u5DF2\u53D6\u6D88",
233
+ "aiProvider.resourceRef": "[\u8D44\u6E90] {p0} ({p1})",
234
+ "aiProvider.streamError": "\u6D41\u5F0F\u54CD\u5E94\u9519\u8BEF",
235
+ "aiProvider.toolRoleNoCallId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08openai \u65CF\u9700 tool_call_id\uFF09",
236
+ "aiProvider.toolRoleNoUseId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08anthropic \u65CF\u6620\u5C04 tool_result \u9700 tool_use_id\uFF09",
237
+ "aiProvider.unsupported": "{p0} \u7EBF\u534F\u8BAE\u4E0D\u652F\u6301 {p1}",
238
+ "aiProvider.videoUnsupported": "video \u8F93\u5165\uFF08\u65E0\u4E3B\u6D41 chat API \u652F\u6301\uFF09",
239
+ "aiProvider.videoUnsupportedAnthropic": "video \u8F93\u5165",
240
+ /* ── aiThreads ── */
241
+ "aiThreads.actionsAria": "\u4F1A\u8BDD\u64CD\u4F5C",
242
+ "aiThreads.archived": "\u5DF2\u5F52\u6863",
243
+ /* ── aiTool ── */
244
+ "aiTool.allow": "\u5141\u8BB8",
245
+ "aiTool.reject": "\u62D2\u7EDD",
246
+ /* ── aiTools ── */
247
+ "aiTools.chart.area": "line \u591A\u7CFB\u5217\u586B\u5145\u9762\u79EF",
248
+ "aiTools.chart.data": "\u4EFB\u610F\u7EF4\u5EA6\u539F\u59CB\u8BB0\u5F55 + dims \u5B57\u6BB5\u6620\u5C04\uFF08\u81EA\u52A8\u900F\u89C6\u6210 \u7C7B\u76EE \xD7 \u7CFB\u5217\uFF09",
249
+ "aiTools.chart.dates": "\u8D21\u732E\u65E5\u5386 ISO \u65E5\u671F\uFF08\u4E0E values \u914D\u5BF9\uFF09",
250
+ "aiTools.chart.description": "\u5728\u7528\u6237\u754C\u9762\u6E32\u67D3\u4E00\u5F20\u56FE\u8868\u53EF\u89C6\u5316\u3002\u8F93\u5165\u4E00\u4EFD\u56FE\u8868\u89C4\u683C\uFF08\u7EAF JSON\uFF09\uFF1Alabels+values\uFF08\u7C7B\u76EE\u6570\u503C\uFF09\u3001series\uFF08\u591A\u7CFB\u5217\uFF09\u3001segments\uFF08\u5360\u6BD4\uFF09\u3001points\uFF08\u6563\u70B9\u6C14\u6CE1\uFF09\u3001dates\uFF08\u8D21\u732E\u65E5\u5386\uFF09\u4EFB\u7ED9\u5176\u4E00\uFF1Btype \u7F3A\u7701\u65F6\u6309\u6570\u636E\u5F62\u6001\u81EA\u52A8\u9009\u578B\uFF08\u65F6\u95F4\u5E8F\u5217\u2192\u6298\u7EBF\u3001\u5360\u6BD4\u2192\u73AF\u5F62\u3001\u65E5\u671F\u2192\u8D21\u732E\u65E5\u5386\u3001\u5750\u6807\u70B9\u2192\u6563\u70B9\uFF09\u3002\u56FE\u8868\u5E26\u4EA4\u4E92\uFF08\u60AC\u505C\u63D0\u793A/\u70B9\u51FB/\u53F3\u952E\uFF09\u4E0E\u53EF\u9690\u85CF\u56FE\u4F8B\u3002",
251
+ "aiTools.chart.dims": "data[] \u7684\u5B57\u6BB5\u6620\u5C04\uFF1A{label?, series?, value?, x?, y?}",
252
+ "aiTools.chart.format": "\u6570\u503C\u683C\u5F0F\u5316\uFF1A{notation?: compact|percent|ms, unit?}",
253
+ "aiTools.chart.labels": "\u7C7B\u76EE\u8F74\uFF08line/vbar/hbar\uFF09",
254
+ "aiTools.chart.legend": "false = \u521D\u59CB\u9690\u85CF\u56FE\u4F8B",
255
+ "aiTools.chart.lttb": "\u5927\u6570\u636E\u91CF\u964D\u91C7\u6837\uFF1Atrue=\u81EA\u52A8\u4E0A\u9650\uFF0C\u6570\u5B57=\u6700\u5927\u4FDD\u7559\u70B9\u6570\uFF08\u6E32\u67D3\u5C42\u5951\u7EA6\uFF0C\u968F\u56FE\u8868\u91CD\u5EA6\u573A\u666F\u843D\u5730\uFF09",
256
+ "aiTools.chart.marks": "\u6807\u8BB0\u5C42\uFF08\u9608\u503C\u7EBF/\u53C2\u8003\u533A\u95F4/\u5F3A\u8C03\u70B9\uFF09\uFF0C\u5143\u7D20\u4E3A\u7EAF JSON \u63CF\u8FF0\u5B50\uFF08\u6E32\u67D3\u5C42\u5951\u7EA6\uFF0C\u968F\u56FE\u8868\u91CD\u5EA6\u573A\u666F\u843D\u5730\uFF09",
257
+ "aiTools.chart.points": "\u6563\u70B9/\u6C14\u6CE1\uFF1A[{x,y,size?,label?}]",
258
+ "aiTools.chart.radarAxes": "\u96F7\u8FBE\u8F74\u540D",
259
+ "aiTools.chart.segments": "\u5360\u6BD4\u578B\uFF08donut/stack\uFF09\uFF1A[{label,value}]",
260
+ "aiTools.chart.series": "\u591A\u7CFB\u5217\uFF08line/vbar/radar\uFF09",
261
+ "aiTools.chart.stacked": "vbar \u591A\u7CFB\u5217\u5806\u53E0\uFF08\u9ED8\u8BA4\u5206\u7EC4\uFF09",
262
+ "aiTools.chart.title": "\u56FE\u8868\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5934\u90E8\u884C\u5DE6\u4FA7\uFF09",
263
+ "aiTools.chart.tooltip": "false = \u5173\u95ED\u5185\u7F6E\u63D0\u793A",
264
+ "aiTools.chart.type": "\u56FE\u8868\u7C7B\u578B\uFF0C\u7F3A\u7701\u81EA\u52A8\u63A8\u65AD",
265
+ "aiTools.chart.values": "\u5355\u7CFB\u5217\u6570\u503C\uFF08\u4E0E labels \u914D\u5BF9\uFF09",
266
+ "aiTools.chart.zoom": "\u7F29\u653E\u4EA4\u4E92\uFF1A'x'=\u7C7B\u76EE\u8F74\u533A\u95F4\u7F29\u653E\uFF0C'brush'=\u5237\u9009\u7F29\u653E",
267
+ "aiTools.datatable.columns": "\u5217\u5B9A\u4E49\u6570\u7EC4\uFF1A\u6BCF\u9879 { key, title, sortable?, filterable? }\uFF08key \u5BF9\u5E94 rows \u5143\u7D20\u5B57\u6BB5\u540D\uFF09",
268
+ "aiTools.datatable.description": "\u5728\u7528\u6237\u754C\u9762\u6E32\u67D3\u4E00\u4E2A\u53EF\u641C\u7D22\u3001\u5206\u9875\u3001\u53EF\u5BFC\u51FA CSV \u7684\u6570\u636E\u8868\u683C\u3002\u8F93\u5165\u7EAF JSON\uFF1Acolumns \u4E3A\u5217\u5B9A\u4E49\uFF08key/title \u5FC5\u586B\uFF09\uFF0Crows \u4E3A\u884C\u5BF9\u8C61\u6570\u7EC4\uFF08\u5B57\u6BB5\u540D\u5BF9\u9F50\u5217 key\uFF09\u3002\u8FD4\u56DE { title, rows: \u884C\u6570 } \u6458\u8981\u3002",
269
+ "aiTools.datatable.rows": "\u884C\u6570\u636E\u6570\u7EC4\uFF1A\u6BCF\u9879\u4E3A\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u5B57\u6BB5\u540D\u5BF9\u9F50 columns[].key",
270
+ "aiTools.datatable.title": "\u8868\u683C\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5DE5\u5177\u680F\u5DE6\u4FA7\uFF09",
271
+ "aiTools.execFailed": "\u5DE5\u5177\u6267\u884C\u5931\u8D25\uFF1A{p0}",
272
+ "aiTools.execTimeout": "\u6267\u884C\u8D85\u65F6\uFF08{p0}ms\uFF09",
273
+ "aiTools.manifestHeader": "\u53EF\u7528 UI \u5DE5\u5177\uFF08\u8C03\u7528\u540E\u5728\u7528\u6237\u754C\u9762\u6302\u8F7D\u53EF\u89C6\u5316\u7ED3\u679C\uFF09\uFF1A",
274
+ "aiTools.missingRequired": "\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5",
275
+ "aiTools.none": "\uFF08\u65E0\u5DF2\u6CE8\u518C\u7684 UI \u5DE5\u5177\uFF09",
276
+ "aiTools.notInEnum": "\u4E0D\u5728\u5141\u8BB8\u53D6\u503C\u5185\uFF08{p0}\uFF09",
277
+ "aiTools.numMax": "\u6570\u503C {p0} \u5927\u4E8E\u6700\u5927\u503C {p1}",
278
+ "aiTools.numMin": "\u6570\u503C {p0} \u5C0F\u4E8E\u6700\u5C0F\u503C {p1}",
279
+ "aiTools.removeMount": "\u79FB\u9664 {p0} \u6302\u8F7D",
280
+ "aiTools.strMax": "\u957F\u5EA6 {p0} \u8D85\u8FC7\u4E0A\u9650 {p1}",
281
+ "aiTools.strMin": "\u957F\u5EA6 {p0} \u5C0F\u4E8E\u4E0B\u9650 {p1}",
282
+ "aiTools.typeMismatch": "\u671F\u671B {p0}\uFF0C\u5B9E\u5F97 {p1}",
283
+ "aiTools.validationFailed": "\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF08{p0} \u9879\uFF09",
284
+ /* ── autocomplete ── */
285
+ "suggestions.loading": "\u6B63\u5728\u67E5\u627E\u5EFA\u8BAE\u2026",
286
+ "suggestions.error": "\u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D\u5EFA\u8BAE\uFF0C\u8BF7\u91CD\u8BD5\u3002",
287
+ "suggestions.options": "\u5EFA\u8BAE",
288
+ "suggestions.empty": "\u6CA1\u6709\u5339\u914D\u7684\u5EFA\u8BAE",
289
+ "autocomplete.empty": "\u65E0\u5339\u914D\u7ED3\u679C",
290
+ /* ── backTop ── */
291
+ "backTop.label": "\u56DE\u5230\u9876\u90E8",
292
+ /* ── carousel ── */
293
+ "carousel.item": "\u7B2C {p0} \u5F20",
294
+ "carousel.region": "\u8F6E\u64AD",
295
+ "carousel.slide": "\u5E7B\u706F\u7247",
296
+ "carousel.status": "\u7B2C {p0} / {p1} \u5F20",
297
+ /* ── charts ── */
298
+ "charts.calendarWeekdays": ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"],
299
+ "charts.few": "\u5C11",
300
+ "charts.hideLegend": "\u9690\u85CF\u56FE\u4F8B",
301
+ "charts.loading": "\u52A0\u8F7D\u4E2D",
302
+ "charts.many": "\u591A",
303
+ "charts.monthLabel": "{p0}\u6708",
304
+ "charts.noData": "\u6682\u65E0\u6570\u636E",
305
+ "charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
306
+ "charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
307
+ "charts.type.area": "\u9762\u79EF\u56FE",
308
+ "charts.type.donut": "\u73AF\u5F62\u56FE",
309
+ "charts.type.gauge": "\u4EEA\u8868\u76D8",
310
+ "charts.type.graph": "\u77E5\u8BC6\u56FE\u8C31",
311
+ "charts.type.heatmap": "\u70ED\u529B\u56FE",
312
+ "charts.type.line": "\u6298\u7EBF\u56FE",
313
+ "charts.type.map": "\u5730\u56FE",
314
+ "charts.type.radar": "\u96F7\u8FBE\u56FE",
315
+ "charts.type.scatter": "\u6563\u70B9\u56FE",
316
+ "charts.type.sparkline": "\u8FF7\u4F60\u8D8B\u52BF",
317
+ "charts.seriesName": "\u7CFB\u5217{p0}",
318
+ "charts.showLegend": "\u663E\u793A\u56FE\u4F8B",
319
+ "charts.value": "\u6570\u503C",
320
+ /* ── themeSwitcher ── */
321
+ "themeSwitcher.appearance": "\u5916\u89C2",
322
+ "themeSwitcher.theme": "\u4E3B\u9898",
323
+ "themeSwitcher.primitive": "\u539F\u8BED",
324
+ "themeSwitcher.mode": "\u660E\u6697",
325
+ "themeSwitcher.light": "\u6D45\u8272",
326
+ "themeSwitcher.dark": "\u6DF1\u8272",
327
+ "themeSwitcher.modern": "\u73B0\u4EE3",
328
+ "themeSwitcher.dongyun": "\u4E1C\u97F5",
329
+ "themeSwitcher.jugou": "\u5DE8\u6784",
330
+ "themeSwitcher.piano": "\u94A2\u7434",
331
+ "themeSwitcher.monet": "\u83AB\u5948",
332
+ "themeSwitcher.vangogh": "\u68B5\u9AD8",
333
+ "themeSwitcher.chinese": "\u4E2D\u5F0F",
334
+ "themeSwitcher.soviet": "\u82CF\u5F0F",
335
+ "themeSwitcher.pixel": "\u50CF\u7D20",
336
+ /* ── codeBlock ── */
337
+ "codeBlock.collapse": "\u6536\u8D77",
338
+ "codeBlock.region": "\u4EE3\u7801\u5185\u5BB9",
339
+ "codeBlock.copyFailed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
340
+ "codeBlock.copyRetry": "\u91CD\u8BD5\u590D\u5236",
341
+ "codeBlock.copy": "\u590D\u5236",
342
+ "codeBlock.foldCollapse": "\u6298\u53E0\u4EE3\u7801",
343
+ "codeBlock.foldExpand": "\u5C55\u5F00\u4EE3\u7801",
344
+ "codeBlock.linesTotal": "\u5171 {p0} \u884C",
345
+ "codeBlock.numHide": "\u9690\u85CF\u884C\u53F7",
346
+ "codeBlock.numShow": "\u663E\u793A\u884C\u53F7",
347
+ "codeBlock.tailAria": "\u5C55\u5F00\u6216\u6536\u8D77\u4EE3\u7801",
348
+ "codeBlock.wrapOff": "\u5173\u95ED\u6362\u884C",
349
+ "codeBlock.wrapOn": "\u81EA\u52A8\u6362\u884C",
350
+ /* ── common ── */
351
+ "common.accept": "\u63A5\u53D7",
352
+ "common.add": "\u65B0\u589E",
353
+ "common.all": "\u5168\u90E8",
354
+ "common.approve": "\u6279\u51C6",
355
+ "common.cancel": "\u53D6\u6D88",
356
+ "common.actionFailed": "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5\u3002",
357
+ "common.close": "\u5173\u95ED",
358
+ "common.confirm": "\u786E\u8BA4",
359
+ "common.copied": "\u5DF2\u590D\u5236",
360
+ "common.copy": "\u590D\u5236",
361
+ "common.delete": "\u5220\u9664",
362
+ "common.enable": "\u542F\u7528",
363
+ "common.exportCsv": "\u5BFC\u51FA CSV",
364
+ "common.healthy": "\u6B63\u5E38",
365
+ "common.input": "\u8F93\u5165",
366
+ "common.loadingEllipsis": "\u52A0\u8F7D\u4E2D\u2026",
367
+ "common.official": "\u5B98\u65B9",
368
+ "common.output": "\u8F93\u51FA",
369
+ "common.page": "\u7B2C {p0} \u9875",
370
+ "common.reject": "\u62D2\u7EDD",
371
+ "common.remove": "\u79FB\u9664",
372
+ "common.requestAccess": "\u7533\u8BF7\u8BBF\u95EE",
373
+ "common.requestAccessFor": "\u7533\u8BF7\u8BBF\u95EE {p0}",
374
+ "common.restricted": "\u53D7\u9650",
375
+ "common.retry": "\u91CD\u8BD5",
376
+ "common.rows": "{p0} \u884C",
377
+ "common.run": "\u8FD0\u884C",
378
+ "common.steps": "{p0} \u6B65",
379
+ "common.submit": "\u63D0\u4EA4",
380
+ "common.unknown": "\u672A\u77E5",
381
+ /* ── contextMenu ── */
382
+ "contextMenu.back": "\u540E\u9000",
383
+ "contextMenu.copyPageLink": "\u590D\u5236\u9875\u9762\u94FE\u63A5",
384
+ "contextMenu.forward": "\u524D\u8FDB",
385
+ "contextMenu.refresh": "\u5237\u65B0",
386
+ /* ── copy ── */
387
+ "copy.copied": "\u5DF2\u590D\u5236",
388
+ "copy.failed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
389
+ "copy.denied": "clipboard API \u88AB\u62D2\uFF08{p0}\uFF09\u4E14\u56DE\u9000 execCommand \u5931\u8D25",
390
+ "copy.unavailable": "\u526A\u8D34\u677F\u4E0D\u53EF\u7528\uFF08\u65E0 clipboard API \u4E14 execCommand \u56DE\u9000\u5931\u8D25\uFF09",
391
+ "copy.unknown": "\u672A\u77E5\u9519\u8BEF",
392
+ /* ── datatable ── */
393
+ "datatable.clear": "\u6E05\u7A7A",
394
+ "datatable.collapseGroup": "\u6298\u53E0\u5206\u7EC4 {p0}",
395
+ "datatable.columnSettingsAria": "\u5217\u8BBE\u7F6E",
396
+ "datatable.countUnit": "{p0} \u6761",
397
+ "datatable.editHint": "\u7F16\u8F91\u300C{p0}\u300D\uFF1A\u53CC\u51FB\u6216\u6309 Enter",
398
+ "datatable.editHintWithValue": "{p0}\uFF08\u53CC\u51FB\u6216\u6309 Enter \u7F16\u8F91\u300C{p1}\u300D\uFF09",
399
+ "datatable.empty": "\u6682\u65E0\u6570\u636E",
400
+ "datatable.emptyValue": "\uFF08\u7A7A\uFF09",
401
+ "datatable.expandGroup": "\u5C55\u5F00\u5206\u7EC4 {p0}",
402
+ "datatable.expandRow": "\u5C55\u5F00\u884C",
403
+ "datatable.filterChipAria": "\u7B5B\u9009\u300C{p0}\u300D",
404
+ "datatable.pageSizeAria": "\u6BCF\u9875\u6761\u6570",
405
+ "datatable.paginationAria": "\u5206\u9875",
406
+ "datatable.reset": "\u91CD\u7F6E",
407
+ "datatable.resizeAria": "\u8C03\u6574\u300C{p0}\u300D\u5217\u5BBD",
408
+ "datatable.searchAria": "\u641C\u7D22\u8868\u683C",
409
+ "datatable.searchPlaceholder": "\u641C\u7D22\u2026",
410
+ "datatable.selectAll": "\u5168\u9009",
411
+ "datatable.selectRow": "\u9009\u62E9\u8BE5\u884C",
412
+ "datatable.selectedCount": "\u5DF2\u9009 {p0} \u9879",
413
+ "datatable.totalCount": "\u5171 {p0} \u6761",
414
+ "datatable.totalLabel": "\u5408\u8BA1",
415
+ /* ── datePicker ── */
416
+ "datePicker.noDates": "\u6CA1\u6709\u53EF\u9009\u65E5\u671F",
417
+ "datePicker.clear": "\u6E05\u9664",
418
+ "datePicker.nextMonth": "\u4E0B\u4E00\u6708",
419
+ "datePicker.prevMonth": "\u4E0A\u4E00\u6708",
420
+ "datePicker.selectDate": "\u9009\u62E9\u65E5\u671F",
421
+ "datePicker.today": "\u4ECA\u5929",
422
+ "datePicker.weekdays": ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"],
423
+ "datePicker.weekdaysSunFirst": ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"],
424
+ "datePicker.yearMonth": "{p0} \u5E74 {p1} \u6708",
425
+ /* ── formValidate ── */
426
+ "formValidate.email": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
427
+ "formValidate.invalid": "\u8F93\u5165\u4E0D\u5408\u6CD5",
428
+ "formValidate.matches": "\u4E24\u6B21\u8F93\u5165\u4E0D\u4E00\u81F4",
429
+ "formValidate.max": "\u4E0D\u80FD\u5927\u4E8E {max}",
430
+ "formValidate.maxlength": "\u6700\u591A\u8F93\u5165 {max} \u4E2A\u5B57\u7B26",
431
+ "formValidate.min": "\u4E0D\u80FD\u5C0F\u4E8E {min}",
432
+ "formValidate.minlength": "\u81F3\u5C11\u8F93\u5165 {min} \u4E2A\u5B57\u7B26",
433
+ "formValidate.number": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57",
434
+ "formValidate.pattern": "\u8F93\u5165\u683C\u5F0F\u4E0D\u6B63\u786E",
435
+ "formValidate.required": "\u6B64\u9879\u4E3A\u5FC5\u586B\u9879",
436
+ "formValidate.selectPlaceholder": "\u8BF7\u9009\u62E9",
437
+ "formValidate.step": "\u6570\u503C\u5FC5\u987B\u662F {step} \u7684\u500D\u6570",
438
+ "formValidate.summary": "\u8868\u5355\u6709 {p0} \u9879\u5185\u5BB9\u9700\u8981\u4FEE\u6B63\uFF0C\u8BF7\u4FEE\u6539\u540E\u91CD\u65B0\u63D0\u4EA4",
439
+ "formValidate.tool.description": "\u5411\u7528\u6237\u6E32\u67D3\u4E00\u4E2A\u7ED3\u6784\u5316\u8868\u5355\u6536\u96C6\u8F93\u5165",
440
+ "formValidate.tool.fields": "\u5B57\u6BB5\u5217\u8868",
441
+ "formValidate.tool.help": "\u5B57\u6BB5\u4E0B\u65B9\u7684\u5E2E\u52A9\u6587\u6848",
442
+ "formValidate.tool.label": "\u5B57\u6BB5\u6807\u7B7E",
443
+ "formValidate.tool.max": "\u6700\u5927\u503C\uFF08number \u7528\uFF09",
444
+ "formValidate.tool.min": "\u6700\u5C0F\u503C\uFF08number \u7528\uFF09",
445
+ "formValidate.tool.name": "\u5B57\u6BB5\u540D\uFF08\u63D0\u4EA4\u503C\u7684 key\uFF09",
446
+ "formValidate.tool.options": "select \u9009\u9879\uFF08\u5B57\u7B26\u4E32\u6216 {label,value}\uFF09",
447
+ "formValidate.tool.pattern": "\u6B63\u5219\u7EA6\u675F\uFF08HTML pattern \u8BED\u6CD5\uFF0C\u6574\u4E32\u5339\u914D\uFF09",
448
+ "formValidate.tool.required": "\u662F\u5426\u5FC5\u586B",
449
+ "formValidate.tool.submitText": "\u63D0\u4EA4\u6309\u94AE\u6587\u6848\uFF0C\u9ED8\u8BA4\u300C\u63D0\u4EA4\u300D",
450
+ "formValidate.tool.title": "\u8868\u5355\u6807\u9898\uFF08\u53EF\u9009\uFF09",
451
+ "formValidate.tool.type": "\u63A7\u4EF6\u7C7B\u578B\uFF0C\u9ED8\u8BA4 text",
452
+ "formValidate.url": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u94FE\u63A5\u5730\u5740",
453
+ /* ── input ── */
454
+ "input.clearSearch": "\u6E05\u7A7A\u641C\u7D22",
455
+ "input.hidePassword": "\u9690\u85CF\u5BC6\u7801",
456
+ "input.otpPosition": "\u7B2C {p0} \u4F4D\uFF0C\u5171 {p1} \u4F4D",
457
+ "panel.expand": "\u5C55\u5F00",
458
+ "panel.collapse": "\u6298\u53E0",
459
+ "input.showPassword": "\u663E\u793A\u5BC6\u7801",
460
+ /* ── kbAccess ── */
461
+ "kbAccess.approveTip": "\u6279\u51C6\u8BE5\u8BBF\u95EE\u7533\u8BF7",
462
+ "kbAccess.approvedBadge": "\u5DF2\u6279\u51C6 \xB7 {p0}",
463
+ "kbAccess.approverNotePlaceholder": "\u7ED9\u8BF7\u6C42\u4EBA\u7684\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
464
+ "kbAccess.daysLeft": "{p0} \u5929\u540E",
465
+ "kbAccess.defaultSubject": "\u8BE5\u5185\u5BB9",
466
+ "kbAccess.deniedHint": "\u672A\u901A\u8FC7\u2014\u2014\u53EF\u8C03\u6574\u7406\u7531\u540E\u91CD\u65B0\u7533\u8BF7",
467
+ "kbAccess.denyNoteLabel": "\u62D2\u7EDD\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
468
+ "kbAccess.duration30d": "30 \u5929",
469
+ "kbAccess.duration7d": "7 \u5929",
470
+ "kbAccess.durationForever": "\u6C38\u4E45",
471
+ "kbAccess.durationLabel": "\u8BBF\u95EE\u65F6\u957F",
472
+ "kbAccess.expired": "\u5DF2\u5230\u671F",
473
+ "kbAccess.expiresAt": "{p0}\u5230\u671F",
474
+ "kbAccess.grantAsPrefix": "\u6279\u51C6\u4E3A{p0}",
475
+ "kbAccess.grantRoleLabel": "\u6279\u51C6\u89D2\u8272",
476
+ "kbAccess.hoursLeft": "{p0} \u5C0F\u65F6\u540E",
477
+ "kbAccess.minutesLeft": "{p0} \u5206\u949F\u540E",
478
+ "kbAccess.ownerLabel": "\u5185\u5BB9\u8D1F\u8D23\u4EBA",
479
+ "kbAccess.ownerPrefix": "\u8D1F\u8D23\u4EBA\uFF1A{p0}",
480
+ "kbAccess.reapply": "\u91CD\u65B0\u7533\u8BF7",
481
+ "kbAccess.reasonLabel": "\u7533\u8BF7\u7406\u7531",
482
+ "kbAccess.reasonPlaceholder": "\u8BF4\u660E\u4E1A\u52A1\u539F\u56E0\uFF08\u5BA1\u6279\u4EBA\u53EF\u89C1\uFF09",
483
+ "kbAccess.rejectTip": "\u62D2\u7EDD\u8BE5\u8BBF\u95EE\u7533\u8BF7",
484
+ "kbAccess.requestRenewal": "\u7533\u8BF7\u7EED\u671F",
485
+ "kbAccess.requestToPrefix": "\u8BF7\u6C42\u5C06\u53D1\u9001\u7ED9\uFF1A{p0}",
486
+ "kbAccess.restrictedContent": "\u53D7\u9650\u5185\u5BB9",
487
+ "kbAccess.restrictedHint": "\u6B64\u5185\u5BB9\u4EC5\u5BF9\u6388\u6743\u8005\u5F00\u653E",
488
+ "kbAccess.revokedHint": "\u8BBF\u95EE\u5DF2\u88AB\u6536\u56DE",
489
+ "kbAccess.roleLabel": "\u7533\u8BF7\u89D2\u8272",
490
+ "kbAccess.submitRequest": "\u63D0\u4EA4\u7533\u8BF7",
491
+ "kbAccess.switchIdentity": "\u5207\u6362\u8EAB\u4EFD",
492
+ "kbAccess.switchIdentityTip": "\u5207\u6362\u8EAB\u4EFD\u540E\u91CD\u65B0\u68C0\u67E5\u6743\u9650",
493
+ "kbAccess.unknownRequester": "\u672A\u77E5\u8BF7\u6C42\u4EBA",
494
+ "kbAccess.waitingApproval": "\u7B49\u5F85 {p0} \u5BA1\u6279",
495
+ /* ── kbAcl ── */
496
+ "kbAcl.breakHint": "\u505C\u6B62\u4ECE\u7236\u7EA7\u7EE7\u627F\uFF0C\u6539\u4E3A\u72EC\u7ACB\u7BA1\u7406\u672C\u9879\u6743\u9650",
497
+ "kbAcl.breakInheritance": "\u505C\u6B62\u7EE7\u627F",
498
+ "kbAcl.brokenNotice": "\u5DF2\u505C\u6B62\u7EE7\u627F\u2014\u2014\u672C\u9879\u6743\u9650\u72EC\u7ACB\u4E8E\u7236\u7EA7",
499
+ "kbAcl.detachedChildren": "{p0}\uFF1A{p1} \u4E2A\u5B50\u9879\u5DF2\u8131\u79BB",
500
+ "kbAcl.directAccess": "\u76F4\u63A5\u8BBF\u95EE",
501
+ "kbAcl.exception": "\u4F8B\u5916",
502
+ "kbAcl.expiredOn": "\u5DF2\u8FC7\u671F\uFF08{p0}\uFF09",
503
+ "kbAcl.expiresIn": "{p0} \u5230\u671F",
504
+ "kbAcl.grantAccess": "\u6388\u4E88\u8BBF\u95EE",
505
+ "kbAcl.inheritedFrom": "\u7EE7\u627F\u81EA {p0}",
506
+ "kbAcl.inheritedFromLabel": "\u7EE7\u627F\u81EA",
507
+ "kbAcl.linkAccess": "\u94FE\u63A5\u8BBF\u95EE",
508
+ "kbAcl.noDirect": "\u6682\u65E0\u76F4\u63A5\u6388\u6743",
509
+ "kbAcl.noLinks": "\u65E0\u94FE\u63A5",
510
+ "kbAcl.removeEntry": "\u79FB\u9664 {p0}",
511
+ "kbAcl.restoreHint": "\u6062\u590D\u7EE7\u627F\u5C06\u4E22\u5F03\u672C\u9879\u81EA\u5B9A\u4E49\u7684\u6743\u9650",
512
+ "kbAcl.restoreInheritance": "\u6062\u590D\u7EE7\u627F",
513
+ "kbAcl.rootNotice": "\u6839\u7EA7\u8D44\u6E90\u2014\u2014\u65E0\u7236\u7EA7\u7EE7\u627F",
514
+ "kbAcl.systemBadge": "\u7CFB\u7EDF\u7BA1\u7406",
515
+ "kbAcl.systemNote": "\u7CFB\u7EDF\u81EA\u52A8\uFF0C\u4E0D\u53EF\u624B\u5DE5\u79FB\u9664",
516
+ "kbAcl.unnamedResource": "\u672A\u547D\u540D\u8D44\u6E90",
517
+ "kbAcl.visibleToCount": "\u5BF9 {p0} \u4EBA\u53EF\u89C1",
518
+ /* ── kbAnswer ── */
519
+ "kbAnswer.chartBar": "\u67F1\u72B6",
520
+ "kbAnswer.chartLine": "\u6298\u7EBF",
521
+ "kbAnswer.chartPie": "\u997C\u56FE",
522
+ "kbAnswer.exportNoun": "\u5BFC\u51FA {p0}",
523
+ "kbAnswer.filtered": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4",
524
+ "kbAnswer.filteredDetail": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4\uFF1A{p0}",
525
+ "kbAnswer.followups": "\u8FFD\u95EE\u5EFA\u8BAE",
526
+ "kbAnswer.hitAssets": "\u547D\u4E2D\u8D44\u4EA7\uFF1A{p0}",
527
+ "kbAnswer.kindCurated": "\u4EBA\u5DE5\u7B56\u5C55",
528
+ "kbAnswer.kindFunction": "\u51FD\u6570",
529
+ "kbAnswer.kindParameterized": "\u53C2\u6570\u5316\u67E5\u8BE2",
530
+ "kbAnswer.maskedPrefix": "\u5DF2\u8131\u654F\u5217\uFF1A",
531
+ "kbAnswer.rerunTip": "\u53C2\u6570\u53EF\u6539\u91CD\u8DD1",
532
+ "kbAnswer.truncated": "\u5DF2\u622A\u65AD\uFF1A\u663E\u793A {p0}/{p1} \u884C{p2}",
533
+ "kbAnswer.verifiedBadge": "\u8BA4\u8BC1\u7B54\u6848",
534
+ /* ── kbAudit ── */
535
+ "kbAudit.actionDetail": "\u5904\u7F6E\uFF1A{p0} \xB7 {p1}",
536
+ "kbAudit.actionLabel": "\u5904\u7F6E",
537
+ "kbAudit.alertedNote": "\u5DF2\u5B9E\u65F6\u544A\u8B66\u5B89\u5168\u56E2\u961F",
538
+ "kbAudit.channelFilterAria": "\u5BA1\u8BA1\u901A\u9053\u8FC7\u6EE4",
539
+ "kbAudit.channelPermission": "\u6743\u9650\u53D8\u66F4",
540
+ "kbAudit.channelRead": "\u8BFB\u53D6",
541
+ "kbAudit.channelRequest": "\u7533\u8BF7",
542
+ "kbAudit.channelSystem": "\u7CFB\u7EDF",
543
+ "kbAudit.empty": "\u6682\u65E0\u5BA1\u8BA1\u4E8B\u4EF6",
544
+ "kbAudit.exportTip": "\u5BFC\u51FA\u5F53\u524D\u8FC7\u6EE4\u89C6\u56FE\u4E3A CSV",
545
+ "kbAudit.hygieneLabel": "\u6743\u9650\u5F02\u5473",
546
+ "kbAudit.noHygiene": "\u672A\u53D1\u73B0\u6743\u9650\u5F02\u5473",
547
+ /* ── kbCanvas ── */
548
+ "kbCanvas.splitterAria": "\u8C03\u6574\u753B\u5E03\u53CC\u680F\u6BD4\u4F8B",
549
+ "kbCanvas.splitterTip": "\u62D6\u62FD\u8C03\u6574\u6BD4\u4F8B\uFF08\u2190/\u2192 \u5FAE\u8C03\uFF0C\u53CC\u51FB\u590D\u4F4D\uFF09",
550
+ /* ── kbCheckpoint ── */
551
+ "kbCheckpoint.chatAuditNote": "\u8BA8\u8BBA\u5386\u53F2\u6709\u5BA1\u8BA1\u4EF7\u503C",
552
+ "kbCheckpoint.label": "\u68C0\u67E5\u70B9",
553
+ "kbCheckpoint.restore": "\u6062\u590D\u68C0\u67E5\u70B9",
554
+ "kbCheckpoint.scopeBoth": "\u5185\u5BB9\u4E0E\u5BF9\u8BDD",
555
+ "kbCheckpoint.scopeChat": "\u4EC5\u5BF9\u8BDD",
556
+ "kbCheckpoint.scopeContent": "\u4EC5\u5185\u5BB9\uFF08\u4FDD\u7559\u5BF9\u8BDD\uFF09",
557
+ "kbCheckpoint.scopeMissing": "\u8BE5\u68C0\u67E5\u70B9\u672A\u5305\u542B\u6B64\u6062\u590D\u8303\u56F4",
558
+ /* ── kbChunks ── */
559
+ "kbChunks.addKeyword": "+ \u5173\u952E\u8BCD",
560
+ "kbChunks.chunkAddKeywordAria": "chunk {p0} \u65B0\u589E\u5173\u952E\u8BCD",
561
+ "kbChunks.chunkContentAria": "chunk {p0} \u5185\u5BB9",
562
+ "kbChunks.chunkEnableAria": "chunk {p0} \u542F\u7528",
563
+ "kbChunks.deleteChunkAria": "\u5220\u9664 chunk {p0}",
564
+ "kbChunks.disableNote": "\u5173\u95ED\u540E\u4E0D\u5220\u9664\uFF0C\u4EC5\u6392\u9664\u51FA\u68C0\u7D22",
565
+ "kbChunks.edited": "\u5DF2\u7F16\u8F91",
566
+ "kbChunks.empty": "\u6682\u65E0 chunk",
567
+ "kbChunks.modeFull": "\u5168\u6587",
568
+ "kbChunks.modeLabel": "\u641C\u7D22\u6A21\u5F0F",
569
+ "kbChunks.modeVector": "\u8BED\u4E49",
570
+ "kbChunks.removeKeyword": "\u79FB\u9664\u5173\u952E\u8BCD {p0}",
571
+ "kbChunks.searchAria": "\u641C\u7D22 chunk",
572
+ "kbChunks.searchFullPlaceholder": "\u5168\u6587\u641C\u7D22 chunk\u2026",
573
+ "kbChunks.searchVectorPlaceholder": "\u8BED\u4E49\u641C\u7D22 chunk\u2026",
574
+ /* ── kbCitation ── */
575
+ "kbCitation.sourceN": "\u6765\u6E90 {p0}",
576
+ "kbCitation.unavailable": "\u6765\u6E90\u5DF2\u4E0D\u53EF\u7528",
577
+ /* ── kbClarify ── */
578
+ "kbClarify.parsedPrefix": "\u5DF2\u89E3\u6790\uFF1A",
579
+ /* ── kbConflict ── */
580
+ "kbConflict.owner": "\u8D23\u4EFB\u4EBA {p0}",
581
+ /* ── kbConnector ── */
582
+ "kbConnector.changeRate": "\u53D8\u66F4\u7387",
583
+ "kbConnector.crawlRate": "\u6293\u53D6\u7387",
584
+ "kbConnector.credentials": "\u51ED\u8BC1",
585
+ "kbConnector.credentialsExpired": "\u51ED\u8BC1\u8FC7\u671F",
586
+ "kbConnector.editSchedule": "\u7F16\u8F91\u8BA1\u5212",
587
+ "kbConnector.empty": "\u6682\u65E0\u8FDE\u63A5\u5668",
588
+ "kbConnector.failing": "\u5F02\u5E38",
589
+ "kbConnector.lastSync": "\u6700\u8FD1\u540C\u6B65 {p0}",
590
+ "kbConnector.neverSynced": "\u4ECE\u672A\u540C\u6B65",
591
+ "kbConnector.notConfigured": "\u672A\u914D\u7F6E",
592
+ "kbConnector.off": "\u5DF2\u505C\u7528",
593
+ "kbConnector.perDay": "{p0}/\u5929",
594
+ "kbConnector.reauth": "\u91CD\u65B0\u6388\u6743",
595
+ "kbConnector.stale": "\u505C\u6EDE",
596
+ "kbConnector.syncNow": "\u7ACB\u5373\u540C\u6B65",
597
+ "kbConnector.synced": "\u5DF2\u540C\u6B65",
598
+ /* ── kbCore ── */
599
+ "kbCore.accessDenied": "\u672A\u901A\u8FC7",
600
+ "kbCore.accessExpired": "\u5DF2\u8FC7\u671F",
601
+ "kbCore.accessExpiring": "\u5373\u5C06\u5230\u671F",
602
+ "kbCore.accessGranted": "\u5DF2\u5F00\u901A",
603
+ "kbCore.accessNotRequested": "\u672A\u7533\u8BF7",
604
+ "kbCore.accessPending": "\u5F85\u5BA1\u6279",
605
+ "kbCore.accessRequested": "\u5DF2\u63D0\u4EA4",
606
+ "kbCore.accessRevoked": "\u5DF2\u6536\u56DE",
607
+ "kbCore.auditApprove": "\u6279\u51C6",
608
+ "kbCore.auditBreakGlass": "\u5E94\u6025\u8BBF\u95EE",
609
+ "kbCore.auditDelegation": "\u59D4\u6D3E",
610
+ "kbCore.auditDeny": "\u62D2\u7EDD",
611
+ "kbCore.auditGrant": "\u6388\u4E88",
612
+ "kbCore.auditPermissionChange": "\u6743\u9650\u53D8\u66F4",
613
+ "kbCore.auditPreview": "\u9884\u89C8",
614
+ "kbCore.auditRead": "\u8BFB\u53D6",
615
+ "kbCore.auditRequest": "\u7533\u8BF7",
616
+ "kbCore.auditRevoke": "\u6536\u56DE",
617
+ "kbCore.auditSync": "\u6743\u9650\u540C\u6B65",
618
+ "kbCore.daysAgo": "{p0} \u5929\u524D",
619
+ "kbCore.hoursAgo": "{p0} \u5C0F\u65F6\u524D",
620
+ "kbCore.hygieneBroadGroup": "\u8FC7\u5BBD\u7EC4\u6388\u6743",
621
+ "kbCore.hygieneBrokenInheritance": "\u65AD\u7EE7\u627F",
622
+ "kbCore.hygieneOrgWideLink": "\u7EC4\u7EC7\u7EA7\u94FE\u63A5",
623
+ "kbCore.hygieneOrphanedOwner": "\u8D1F\u8D23\u4EBA\u7F3A\u4F4D",
624
+ "kbCore.hygieneOverexposed": "\u8FC7\u5EA6\u66B4\u9732",
625
+ "kbCore.hygieneSensitiveMismatch": "\u654F\u611F\u5EA6\u9519\u914D",
626
+ "kbCore.justNow": "\u521A\u521A",
627
+ "kbCore.levelComment": "\u53EF\u8BC4\u8BBA",
628
+ "kbCore.levelEdit": "\u53EF\u7F16\u8F91",
629
+ "kbCore.levelNone": "\u65E0\u6743\u9650",
630
+ "kbCore.levelOwner": "\u8D1F\u8D23\u4EBA",
631
+ "kbCore.levelView": "\u53EF\u67E5\u770B",
632
+ "kbCore.minutesAgo": "{p0} \u5206\u949F\u524D",
633
+ "kbCore.runCancelled": "\u5DF2\u53D6\u6D88",
634
+ "kbCore.runDone": "\u5DF2\u5B8C\u6210",
635
+ "kbCore.runFailed": "\u5931\u8D25",
636
+ "kbCore.runQueued": "\u6392\u961F\u4E2D",
637
+ "kbCore.runRunning": "\u8FDB\u884C\u4E2D",
638
+ "kbCore.runUnstarted": "\u672A\u5F00\u59CB",
639
+ "kbCore.stageAgent": "\u4EE3\u7406",
640
+ "kbCore.stageChunk": "\u5207\u7247",
641
+ "kbCore.stageEmbed": "\u5411\u91CF\u5316",
642
+ "kbCore.stageGeneration": "\u751F\u6210",
643
+ "kbCore.stageGuard": "\u62A4\u680F",
644
+ "kbCore.stageQuery": "\u67E5\u8BE2",
645
+ "kbCore.stageRerank": "\u91CD\u6392",
646
+ "kbCore.stageRetrieval": "\u68C0\u7D22",
647
+ "kbCore.stageTool": "\u5DE5\u5177",
648
+ "kbCore.typeApi": "\u63A5\u53E3",
649
+ "kbCore.typeCode": "\u4EE3\u7801",
650
+ "kbCore.typeDatabase": "\u6570\u636E\u5E93",
651
+ "kbCore.typeDocument": "\u6587\u6863",
652
+ "kbCore.typeEmail": "\u90AE\u4EF6",
653
+ "kbCore.typeImage": "\u56FE\u7247",
654
+ "kbCore.typeKb": "\u77E5\u8BC6\u5E93",
655
+ "kbCore.typeTable": "\u8868\u683C",
656
+ "kbCore.typeTicket": "\u5DE5\u5355",
657
+ "kbCore.typeWebpage": "\u7F51\u9875",
658
+ "kbCore.unnamedSource": "\u672A\u547D\u540D\u6765\u6E90",
659
+ "kbCore.unnamedSubject": "\u672A\u547D\u540D\u4E3B\u4F53",
660
+ "kbCore.visHidden": "\u4E0D\u53EF\u89C1",
661
+ "kbCore.visMetadataOnly": "\u4EC5\u5143\u6570\u636E",
662
+ "kbCore.visSummaryOnly": "\u4EC5\u6458\u8981",
663
+ "kbCore.visVisible": "\u53EF\u89C1",
664
+ /* ── kbEval ── */
665
+ "kbEval.avgA": "A \u5747\u5206 {p0}",
666
+ "kbEval.avgB": "B \u5747\u5206 {p0}",
667
+ "kbEval.compare": "\u5BF9\u6BD4",
668
+ "kbEval.runSelectAria": "\u9009\u62E9\u5BF9\u6BD4\u8FD0\u884C",
669
+ "kbEval.expected": "\u671F\u671B",
670
+ "kbEval.gradeImprovement": "\u63D0\u5347",
671
+ "kbEval.gradeRegression": "\u56DE\u5F52",
672
+ "kbEval.gradeTie": "\u6301\u5E73",
673
+ "kbEval.gradeTradeoff": "\u6743\u8861",
674
+ "kbEval.noData": "\u6CA1\u6709\u5BF9\u6BD4\u6570\u636E",
675
+ "kbEval.onlyRegression": "\u4EC5\u770B\u56DE\u5F52",
676
+ "kbEval.outputA": "\u8F93\u51FA A",
677
+ "kbEval.outputB": "\u8F93\u51FA B",
678
+ "kbEval.outputN": "\u8F93\u51FA {p0}",
679
+ /* ── kbExplain ── */
680
+ "kbExplain.empty": "\u6682\u65E0\u53E3\u5F84\u4FE1\u606F",
681
+ "kbExplain.groupAgg": "\u805A\u5408",
682
+ "kbExplain.groupColumn": "\u5217",
683
+ "kbExplain.groupFilter": "\u7B5B\u9009",
684
+ "kbExplain.groupTable": "\u8868",
685
+ "kbExplain.trigger": "\u8FD9\u4E2A\u6570\u5B57\u662F\u600E\u4E48\u7B97\u51FA\u6765\u7684",
686
+ "kbExplain.viewDefinition": "\u67E5\u770B\u53E3\u5F84",
687
+ /* ── kbFilter ── */
688
+ "kbFilter.addGroup": "+ \u7EC4",
689
+ "kbFilter.addRule": "+ \u89C4\u5219",
690
+ "kbFilter.and": "\u4E14",
691
+ "kbFilter.combineMode": "\u7EC4\u5408\u65B9\u5F0F {p0}",
692
+ "kbFilter.copyDsl": "\u590D\u5236 DSL",
693
+ "kbFilter.deleteGroup": "\u5220\u9664\u7EC4",
694
+ "kbFilter.deleteRule": "\u5220\u9664\u89C4\u5219",
695
+ "kbFilter.fieldLabel": "\u5B57\u6BB5",
696
+ "kbFilter.fieldPlaceholder": "\u8FC7\u6EE4\u5B57\u6BB5",
697
+ "kbFilter.noValueNeeded": "\uFF08\u65E0\u9700\u503C\uFF09",
698
+ "kbFilter.none": "\uFF08\u65E0\u8FC7\u6EE4\uFF09",
699
+ "kbFilter.opContains": "\u5305\u542B",
700
+ "kbFilter.opEq": "\u7B49\u4E8E",
701
+ "kbFilter.opExists": "\u5B58\u5728",
702
+ "kbFilter.opGt": "\u5927\u4E8E",
703
+ "kbFilter.opGte": "\u5927\u4E8E\u7B49\u4E8E",
704
+ "kbFilter.opIn": "\u5C5E\u4E8E",
705
+ "kbFilter.opLt": "\u5C0F\u4E8E",
706
+ "kbFilter.opLte": "\u5C0F\u4E8E\u7B49\u4E8E",
707
+ "kbFilter.opNe": "\u4E0D\u7B49\u4E8E",
708
+ "kbFilter.operatorLabel": "\u64CD\u4F5C\u7B26",
709
+ "kbFilter.or": "\u6216",
710
+ "kbFilter.toggleAndOr": "\u5207\u6362 AND / OR",
711
+ "kbFilter.valueAria": "\u8FC7\u6EE4\u503C",
712
+ "kbFilter.valueLabel": "\u503C",
713
+ "kbFilter.valuePlaceholder": "\u503C 1, \u503C 2\uFF08\u9017\u53F7\u5206\u9694\uFF09",
714
+ /* ── kbGap ── */
715
+ "kbGap.addSynonym": "\u52A0\u540C\u4E49\u8BCD",
716
+ "kbGap.colCount": "\u6B21\u6570",
717
+ "kbGap.colLastSeen": "\u6700\u8FD1\u51FA\u73B0",
718
+ "kbGap.colQuery": "\u67E5\u8BE2",
719
+ "kbGap.colShare": "\u5360\u6BD4",
720
+ "kbGap.createDoc": "\u5EFA\u6587\u6863",
721
+ "kbGap.empty": "\u6CA1\u6709\u672A\u547D\u4E2D\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u826F\u597D",
722
+ "kbGap.title": "\u672A\u547D\u4E2D\u67E5\u8BE2\u8D8B\u52BF",
723
+ "kbGap.zeroClickHint": "\u7528\u6237\u53EF\u80FD\u5DF2\u4ECE\u5F15\u7528\u7247\u6BB5\u5F97\u5230\u7B54\u6848\uFF1B\u96F6\u70B9\u51FB\u9700\u4EBA\u5DE5\u5224\u8BFB\uFF0C\u4E0D\u76F4\u63A5\u89C6\u4E3A\u5931\u8D25",
724
+ "kbGap.zeroClickRate": "\u96F6\u70B9\u51FB\u7387",
725
+ "kbGap.zeroClickTitle": "\u96F6\u70B9\u51FB \u2260 \u5931\u8D25",
726
+ /* ── kbHittest ── */
727
+ "kbHittest.addAria": "\u65B0\u589E\u6D4B\u8BD5\u95EE\u9898",
728
+ "kbHittest.addPlaceholder": "\u65B0\u589E\u95EE\u9898\uFF0CEnter \u6DFB\u52A0",
729
+ "kbHittest.avgScore": "\u5747\u5206",
730
+ "kbHittest.deleteQuestion": "\u5220\u9664\u95EE\u9898",
731
+ "kbHittest.empty": "\u6682\u65E0\u6D4B\u8BD5\u95EE\u9898\u2014\u2014\u6DFB\u52A0\u7B2C\u4E00\u6761\uFF0C\u5F00\u59CB\u53EC\u56DE\u56DE\u5F52",
732
+ "kbHittest.expectHit": "\u671F\u671B\u547D\u4E2D\u5728\u524D\u6392\u547D\u4E2D\u4E2D",
733
+ "kbHittest.expectMiss": "\u671F\u671B\u672A\u547D\u4E2D",
734
+ "kbHittest.hitRate": "\u547D\u4E2D\u7387",
735
+ "kbHittest.noExpectation": "\u672A\u8BBE\u671F\u671B",
736
+ "kbHittest.notRun": "\u672A\u8FD0\u884C",
737
+ "kbHittest.pass": "\u901A\u8FC7",
738
+ "kbHittest.pin": "\u7F6E\u9876\u56FA\u5B9A",
739
+ "kbHittest.runAll": "\u8FD0\u884C\u5168\u90E8",
740
+ "kbHittest.sessionOnly": "\u53C2\u6570\u4EC5\u672C\u6B21\u4F1A\u8BDD\u751F\u6548",
741
+ "kbHittest.sessionOnlyHint": "\u6D4B\u8BD5\u8FD0\u884C\u7684\u68C0\u7D22\u53C2\u6570\uFF08topK/\u9608\u503C/\u91CD\u6392\uFF09\u4E0D\u843D\u5E93\uFF0C\u4EC5\u672C\u6B21\u4F1A\u8BDD\u751F\u6548",
742
+ "kbHittest.unpin": "\u53D6\u6D88\u7F6E\u9876",
743
+ /* ── kbMetadata ── */
744
+ "kbMetadata.addField": "\u65B0\u589E\u5B57\u6BB5",
745
+ "kbMetadata.builtIn": "\u5185\u7F6E",
746
+ "kbMetadata.colField": "\u5B57\u6BB5",
747
+ "kbMetadata.colType": "\u7C7B\u578B",
748
+ "kbMetadata.colValue": "\u503C",
749
+ "kbMetadata.empty": "\u6682\u65E0\u5143\u6570\u636E\u5B57\u6BB5",
750
+ "kbMetadata.nameLabel": "\u65B0\u5B57\u6BB5\u540D",
751
+ "kbMetadata.namePlaceholder": "\u5B57\u6BB5\u540D\uFF0C\u5982 department",
752
+ "kbMetadata.removeField": "\u79FB\u9664\u5B57\u6BB5 {p0}",
753
+ "kbMetadata.typeLabel": "\u65B0\u5B57\u6BB5\u7C7B\u578B",
754
+ "kbMetadata.valueOf": "{p0} \u7684\u503C",
755
+ /* ── kbPassage ── */
756
+ "kbPassage.blockRange": "\u5757 {p0}\u2013{p1}",
757
+ "kbPassage.close": "\u5173\u95ED\u539F\u6587",
758
+ "kbPassage.expandContext": "\u5C55\u5F00\u4E0A\u4E0B\u6587",
759
+ "kbPassage.locate": "\u539F\u6587\u5B9A\u4F4D",
760
+ "kbPassage.none": "\uFF08\u65E0\u539F\u6587\uFF09",
761
+ "kbPassage.pageRange": "\u7B2C {p0}\u2013{p1} \u9875",
762
+ "kbPassage.restricted": "\u5185\u5BB9\u53D7\u9650",
763
+ "kbPassage.unitByte": "\u5B57\u8282",
764
+ "kbPassage.unitChar": "\u5B57\u7B26",
765
+ /* ── kbPipeline ── */
766
+ "kbPipeline.empty": "\u6682\u65E0\u89E3\u6790\u4EFB\u52A1",
767
+ "kbPipeline.progress": "\u89E3\u6790\u8FDB\u5EA6",
768
+ "kbPipeline.rerun": "\u91CD\u8DD1",
769
+ "kbPipeline.unknownStage": "\u672A\u77E5\u9636\u6BB5",
770
+ /* ── kbQa ── */
771
+ "kbQa.colAnswer": "\u7B54\u6848",
772
+ "kbQa.colQuestion": "\u95EE\u9898",
773
+ "kbQa.csvHint": "CSV \u6A21\u677F\uFF1Aquestion,answer\uFF08\u6BCF\u884C\u4E00\u5BF9\uFF0CUTF-8\uFF09",
774
+ "kbQa.deleteRow": "\u5220\u9664\u7B2C {p0} \u884C",
775
+ "kbQa.empty": "\u6682\u65E0\u8BA4\u8BC1\u95EE\u7B54\u5BF9",
776
+ "kbQa.importCsv": "\u5BFC\u5165 CSV",
777
+ /* ── kbRerank ── */
778
+ "kbRerank.after": "\u91CD\u6392\u540E",
779
+ "kbRerank.before": "\u91CD\u6392\u524D",
780
+ "kbRerank.compareTitle": "\u91CD\u6392\u5BF9\u6BD4",
781
+ "kbRerank.down": "\u4E0B\u964D {p0} \u4F4D",
782
+ "kbRerank.dropped": "\u843D\u9009",
783
+ "kbRerank.empty": "\u4E24\u4FA7\u5747\u4E3A\u7A7A\u2014\u2014\u8FD0\u884C\u68C0\u7D22\u540E\u518D\u5BF9\u6BD4",
784
+ "kbRerank.enableCompare": "\u542F\u7528\u91CD\u6392\u5BF9\u6BD4",
785
+ "kbRerank.newIn": "\u65B0\u8FDB",
786
+ "kbRerank.unmarked": "\u672A\u6807",
787
+ "kbRerank.up": "\u4E0A\u5347 {p0} \u4F4D",
788
+ /* ── kbRetrieval ── */
789
+ "kbRetrieval.breakdown": "\u62C6\u89E3",
790
+ "kbRetrieval.copyParams": "\u590D\u5236\u53C2\u6570 JSON",
791
+ "kbRetrieval.decreaseTopK": "\u51CF\u5C11 topK",
792
+ "kbRetrieval.empty": "\u8F93\u5165\u95EE\u9898\u5E76\u8FD0\u884C\uFF0C\u547D\u4E2D\u7ED3\u679C\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
793
+ "kbRetrieval.failOpenNotice": "fail-open\uFF1A\u5F53\u524D\u67E5\u8BE2\u672A\u643A\u5E26\u8EAB\u4EFD\u8FC7\u6EE4\uFF0C\u5C06\u8FD4\u56DE\u5168\u90E8\u5019\u9009\uFF08\u4EC5\u9650\u8C03\u8BD5\uFF09",
794
+ "kbRetrieval.hybridWeight": "\u6DF7\u5408\u6743\u91CD\uFF08\u5DE6\u8BED\u4E49\u53F3\u5173\u952E\u8BCD\uFF09",
795
+ "kbRetrieval.identityLabel": "\u68C0\u7D22\u8EAB\u4EFD",
796
+ "kbRetrieval.identityShort": "\u8EAB\u4EFD",
797
+ "kbRetrieval.increaseTopK": "\u589E\u52A0 topK",
798
+ "kbRetrieval.keywordHint": "\u5173\u952E\u8BCD\uFF1A\u5927\u5E93\u5FEB\u901F\u7CBE\u786E",
799
+ "kbRetrieval.layerLabel": "\u8FC7\u6EE4\u5C42",
800
+ "kbRetrieval.layerNone": "none\uFF1Afail-open\uFF0C\u65E0\u8EAB\u4EFD\u8FC7\u6EE4\u8FD4\u56DE\u5168\u90E8\u2014\u2014\u5371\u9669\uFF0C\u4EC5\u6F14\u793A",
801
+ "kbRetrieval.layerPost": "post-filter\uFF1A\u5206\u6570\u5DF2\u53EF\u89C2\u5BDF\u2014\u2014\u7F3A\u9677\u5C42\uFF0C\u4EC5\u6F14\u793A",
802
+ "kbRetrieval.layerPre": "pre-filter\uFF1AACL \u5728\u68C0\u7D22\u6267\u884C\u4F53\u5185\u751F\u6548\u2014\u2014\u5B89\u5168\u57FA\u7EBF",
803
+ "kbRetrieval.leakNote": "\u5206\u6BCD\u672A\u8FC7\u6EE4\u524D\u8BA1\u6570\u5C5E\u6CC4\u9732\uFF0C\u4E0D\u663E\u793A",
804
+ "kbRetrieval.modeHybrid": "\u6DF7\u5408",
805
+ "kbRetrieval.modeKeyword": "\u5173\u952E\u8BCD",
806
+ "kbRetrieval.modeLabel": "\u68C0\u7D22\u6A21\u5F0F",
807
+ "kbRetrieval.modeVector": "\u5411\u91CF",
808
+ "kbRetrieval.postFilterNotice": "post-filter\uFF1A\u5206\u6570\u5DF2\u5728\u8FC7\u6EE4\u524D\u4EA7\u751F\uFF08\u7F3A\u9677\u5C42\u6F14\u793A\uFF09",
809
+ "kbRetrieval.queryLabel": "\u68C0\u7D22\u95EE\u9898",
810
+ "kbRetrieval.queryPlaceholder": "\u68C0\u7D22\u95EE\u9898\uFF0CEnter \u8FD0\u884C",
811
+ "kbRetrieval.rerankLabel": "\u91CD\u6392",
812
+ "kbRetrieval.rerankThresholdOff": "\u9608\u503C \u5173 \xB7 \u5B58\u6D3B {p0}",
813
+ "kbRetrieval.rerankThresholdOn": "\u9608\u503C {p0} \xB7 \u5B58\u6D3B {p1}",
814
+ "kbRetrieval.running": "\u8FD0\u884C\u4E2D\u2026",
815
+ "kbRetrieval.thresholdLabel": "\u5206\u6570\u9608\u503C",
816
+ "kbRetrieval.thresholdValue": "\u9608\u503C N/A",
817
+ "kbRetrieval.vectorHint": "\u8BED\u4E49\uFF1A\u8DE8\u8BED\u8A00/\u65E0\u7CBE\u786E\u8BCD",
818
+ "kbRetrieval.vectorPercent": "\u8BED\u4E49 {p0}",
819
+ "kbRetrieval.weightValue": "\u5411\u91CF {p0}% / \u5173\u952E\u8BCD {p1}%",
820
+ /* ── kbReview ── */
821
+ "kbReview.assignee": "\u5206\u914D\u4EBA",
822
+ "kbReview.doneNext": "\u5B8C\u6210\u5E76\u4E0B\u4E00\u6761",
823
+ "kbReview.empty": "\u6CA1\u6709\u5F85\u5BA1\u67E5\u6761\u76EE",
824
+ "kbReview.emptyAssignee": "\u8BE5\u5206\u914D\u4EBA\u4E0B\u6CA1\u6709\u6761\u76EE",
825
+ "kbReview.emptyQueue": "\u961F\u5217\u4E3A\u7A7A",
826
+ "kbReview.kbCloseHint": "\u5173\u95ED\u63D0\u793A",
827
+ "kbReview.kbSelectOption": "\u9009\u62E9\u9009\u9879",
828
+ "kbReview.kbSwitchItem": "\u5207\u6362\u6761\u76EE",
829
+ "kbReview.kbToggleHint": "\u663E\u793A/\u9690\u85CF\u672C\u63D0\u793A",
830
+ "kbReview.shortcuts": "? \u5FEB\u6377\u952E",
831
+ "kbReview.typeAnswer": "\u56DE\u7B54",
832
+ "kbReview.typeMessage": "\u6D88\u606F",
833
+ "kbReview.typeTrace": "\u8F68\u8FF9",
834
+ "kbReview.unassigned": "\u672A\u5206\u914D",
835
+ /* ── kbSandbox ── */
836
+ "kbSandbox.noResolver": "\u5BBF\u4E3B\u672A\u63D0\u4F9B ui:// \u89E3\u6790\u5668",
837
+ "kbSandbox.notReady": "kb-sandbox: \u5E94\u7528\u672A\u5C31\u7EEA",
838
+ "kbSandbox.resolveFailed": "ui:// \u89E3\u6790\u5931\u8D25\uFF1A{p0}",
839
+ "kbSandbox.timeout": "kb-sandbox: \u8C03\u7528\u8D85\u65F6\uFF08{p0}ms\uFF09",
840
+ "kbSandbox.title": "MCP App \u6C99\u7BB1",
841
+ /* ── kbSegment ── */
842
+ "kbSegment.avgLength": "\u5747\u957F {p0}",
843
+ "kbSegment.childNote": "\u5B50\u5206\u6BB5\u6309\u957F\u5EA6\u81EA\u52A8\u751F\u6210",
844
+ "kbSegment.cleanBlankLines": "\u5408\u5E76\u8FDE\u7EED\u7A7A\u884C",
845
+ "kbSegment.cleanEmails": "\u53BB\u9664\u90AE\u7BB1",
846
+ "kbSegment.cleanLinks": "\u53BB\u9664\u94FE\u63A5",
847
+ "kbSegment.empty": "\u6837\u672C\u4E3A\u7A7A\uFF0C\u6682\u65E0\u53EF\u9884\u89C8\u5206\u6BB5",
848
+ "kbSegment.maxLengthLabel": "\u6700\u5927\u957F\u5EA6",
849
+ "kbSegment.maxLengthPlaceholder": "\u5206\u6BB5\u6700\u5927\u957F\u5EA6",
850
+ "kbSegment.modeAuto": "\u81EA\u52A8",
851
+ "kbSegment.modeCustom": "\u81EA\u5B9A\u4E49",
852
+ "kbSegment.modeLabel": "\u5206\u6BB5\u6A21\u5F0F",
853
+ "kbSegment.modeNote": "\u5206\u6BB5\u6A21\u5F0F\u521B\u5EFA\u540E\u4E0D\u53EF\u66F4\u6539\uFF0C\u53C2\u6570\u53EF\u968F\u65F6\u8C03\u6574",
854
+ "kbSegment.modeParentChild": "\u7236\u5B50",
855
+ "kbSegment.overlapInvalid": "\u91CD\u53E0\u9700\u5C0F\u4E8E\u6700\u5927\u957F\u5EA6",
856
+ "kbSegment.overlapLabel": "\u91CD\u53E0",
857
+ "kbSegment.overlapPlaceholder": "\u5206\u6BB5\u91CD\u53E0",
858
+ "kbSegment.previewNote": "\u5171 {p0} \u6BB5\uFF0C\u4EC5\u9884\u89C8\u524D {p1} \u6BB5",
859
+ "kbSegment.segmentsCount": "{p0} \u6BB5",
860
+ "kbSegment.separatorLabel": "\u5206\u9694\u7B26",
861
+ "kbSegment.separatorPlaceholder": "\u5206\u6BB5\u5206\u9694\u7B26",
862
+ "kbSegment.toggleTip": "\u70B9\u51FB\u5C55\u5F00/\u6536\u8D77",
863
+ /* ── kbSources ── */
864
+ "kbSources.cited": "\u88AB\u5F15 \xD7{p0}",
865
+ "kbSources.empty": "\u4E0B\u4E00\u6761\u7B54\u6848\u5F15\u7528\u7684\u6BB5\u843D\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
866
+ /* ── kbSql ── */
867
+ "kbSql.adminOnly": "\u4EC5\u7BA1\u7406\u5458\u53EF\u89C1",
868
+ "kbSql.applyFix": "\u5E94\u7528\u4FEE\u590D\u5E76\u91CD\u8DD1",
869
+ "kbSql.confirmRerun": "\u786E\u8BA4\u5E76\u91CD\u8DD1",
870
+ "kbSql.editRerun": "\u7F16\u8F91\u91CD\u8DD1",
871
+ "kbSql.fixReady": "\u68C0\u6D4B\u5230\u4E0A\u7248\u62A5\u9519\uFF0C\u4FEE\u590D\u5EFA\u8BAE\u5DF2\u5C31\u7EEA",
872
+ "kbSql.generated": "\u751F\u6210\u7684 SQL",
873
+ /* ── kbTrace ── */
874
+ "kbTrace.empty": "\u6682\u65E0\u8C03\u7528\u94FE\u6570\u636E",
875
+ "kbTrace.hits": "\u547D\u4E2D {p0}",
876
+ /* ── kbVisibility ── */
877
+ "kbVisibility.adminOnlyNote": "\u4EE5\u4E0A\u5BF9\u7167\u4EC5\u9650 admin \u5BA1\u8BA1",
878
+ "kbVisibility.adminWarn": "admin \u5BA1\u8BA1\u89C6\u56FE\u2014\u2014\u547D\u4E2D\u5DEE\u5F02\u5BF9\u7167\u5BF9\u666E\u901A\u7528\u6237\u6784\u6210\u6CC4\u9732\uFF0C\u52FF\u5D4C\u5165\u7EC8\u7AEF\u7528\u6237\u754C\u9762",
879
+ "kbVisibility.failOpenNote": "fail-open\uFF1AKendra \u65E0\u8EAB\u4EFD\u4E0A\u4E0B\u6587\u8FD4\u56DE\u5168\u90E8\u6587\u6863\u2014\u2014\u68C0\u7D22\u5FC5\u987B\u643A\u5E26\u670D\u52A1\u7AEF\u8EAB\u4EFD",
880
+ "kbVisibility.focusColumn": "\u805A\u7126\u8EAB\u4EFD {p0} \u7684\u5217",
881
+ "kbVisibility.layerValue": "\u8FC7\u6EE4\u5C42\uFF1A{p0}",
882
+ "kbVisibility.leakageNote": "\u8BA1\u6570\u5DEE\u5F02\u5373 volume leakage\uFF1A\u5BF9\u666E\u901A\u7528\u6237\u53EA\u53EF\u7ED9\u300C\u5DF2\u6309\u6743\u9650\u8FC7\u6EE4\u300D\u6052\u5B9A\u6587\u6848\uFF0C\u7F13\u89E3\u53EA\u6709 padding \u53D6\u6574",
883
+ "kbVisibility.postFilterNote": "post-filter\uFF1AtopK \u88AB\u65E0\u6743\u7ED3\u679C\u5360\u7528\u4E14\u76F8\u4F3C\u5EA6\u5206\u6570\u53EF\u88AB\u89C2\u5BDF",
884
+ "kbVisibility.queryLabel": "\u67E5\u8BE2\uFF1A{p0}",
885
+ "kbVisibility.safeCount": "\u5B89\u5168\u8BA1\u6570 {p0}",
886
+ "kbVisibility.sourceUnavailable": "\u539F\u6587\u4E0D\u53EF\u7528",
887
+ "kbVisibility.unnamedHit": "\u672A\u547D\u540D\u547D\u4E2D",
888
+ /* ── kbWhoCan ── */
889
+ "kbWhoCan.check": "\u68C0\u67E5",
890
+ "kbWhoCan.checkIdentity": "\u68C0\u67E5\u8EAB\u4EFD",
891
+ "kbWhoCan.daysUntil": "{p0} \u5929\u540E",
892
+ "kbWhoCan.deniedBy": "\u88AB\u7B56\u7565\u62D2\u7EDD\uFF1A{p0}",
893
+ "kbWhoCan.denyVeto": "\u88AB Deny \u4E00\u7968\u5426\u51B3",
894
+ "kbWhoCan.direct": "\u76F4\u63A5",
895
+ "kbWhoCan.expiresSoon": "\u6700\u8FD1\u5230\u671F {p0}",
896
+ "kbWhoCan.hoursUntil": "{p0} \u5C0F\u65F6\u540E",
897
+ "kbWhoCan.inherit": "\u7EE7\u627F",
898
+ "kbWhoCan.link": "\u94FE\u63A5",
899
+ "kbWhoCan.noDeny": "\u65E0 Deny \u751F\u6548",
900
+ "kbWhoCan.noIdentity": "\u672A\u63D0\u4F9B\u53EF\u68C0\u67E5\u7684\u8EAB\u4EFD",
901
+ "kbWhoCan.none": "\u672A\u547D\u4E2D\u4EFB\u4F55\u6388\u6743\u2014\u2014\u9ED8\u8BA4\u62D2\u7EDD",
902
+ "kbWhoCan.recheckTip": "\u91CD\u65B0\u68C0\u67E5\u5F53\u524D\u8EAB\u4EFD\u7684\u6709\u6548\u6743\u9650",
903
+ "kbWhoCan.stackRule": "\u53E0\u52A0\u89C4\u5219\uFF1A\u591A\u6765\u6E90\u5E76\u96C6 \u2192 {p0}\uFF1B{p1}",
904
+ /* ── mentions ── */
905
+ "mentions.create": "\u521B\u5EFA\u201C{p0}\u201D",
906
+ "mentions.customBadge": "\u81EA\u5B9A\u4E49",
907
+ "mentions.loading": "\u52A0\u8F7D\u4E2D\u2026",
908
+ /* ── notification ── */
909
+ "notification.groupBusy": "\u64CD\u4F5C\u7ED3\u675F\u540E\u53EF\u4EE5\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5\u3002",
910
+ "notification.groupUnread": "{count} \u6761\u540C\u7EC4\u901A\u77E5\uFF0C{unread} \u6761\u672A\u8BFB",
911
+ "notification.groupCount": "{count} \u6761\u540C\u7EC4\u901A\u77E5",
912
+ "notification.groupExpanded": "\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5",
913
+ "notification.ariaLabel": "\u901A\u77E5",
914
+ "notification.viewDetails": "\u67E5\u770B\u8BE6\u60C5",
915
+ /* ── pagination ── */
916
+ "pagination.next": "\u4E0B\u4E00\u9875",
917
+ "pagination.pageSize": "{p0} \u6761/\u9875",
918
+ "pagination.prev": "\u4E0A\u4E00\u9875",
919
+ /* ── select ── */
920
+ "select.fieldLabel": "\u9009\u62E9\u9009\u9879",
921
+ "select.noMatch": "\u65E0\u5339\u914D\u9879",
922
+ "select.remove": "\u79FB\u9664 {p0}",
923
+ "select.searchPlaceholder": "\u641C\u7D22\u2026",
924
+ /* ── cascader / tree-select ── */
925
+ "cascader.placeholder": "\u8BF7\u9009\u62E9",
926
+ "cascader.noMatch": "\u65E0\u5339\u914D",
927
+ "cascader.search": "\u641C\u7D22",
928
+ "treeSelect.placeholder": "\u8BF7\u9009\u62E9",
929
+ "treeSelect.noMatch": "\u65E0\u5339\u914D",
930
+ "treeSelect.search": "\u641C\u7D22",
931
+ /* ── tagInput ── */
932
+ "tagInput.ariaLabel": "\u6807\u7B7E\u8F93\u5165\uFF0C\u5171 {p0} \u4E2A",
933
+ "tagInput.remove": "\u79FB\u9664 {p0}",
934
+ "tagInput.field": "\u6DFB\u52A0\u6807\u7B7E",
935
+ "tagInput.added": "\u5DF2\u6DFB\u52A0 {p0} \u4E2A\u6807\u7B7E\u3002",
936
+ "tagInput.removed": "\u5DF2\u79FB\u9664 {p0}\u3002",
937
+ "tagInput.duplicate": "\u5DF2\u6709\u300C{p0}\u300D\uFF0C\u53EF\u4EE5\u7F16\u8F91\u540E\u91CD\u8BD5\u3002",
938
+ "tagInput.limit": "\u6700\u591A {p0} \u4E2A\u6807\u7B7E\uFF0C\u8BF7\u5148\u79FB\u9664\u4E00\u4E2A\u3002",
939
+ /* ── upload ── */
940
+ "upload.remove": "\u79FB\u9664 {p0}",
941
+ "upload.tooBig": "\u6587\u4EF6\u8D85\u8FC7 {p0}",
942
+ "upload.tooMany": "\u6700\u591A {p0} \u4E2A\u6587\u4EF6",
943
+ "upload.typeNotAllowed": "\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301"
944
+ };
945
+ var current = { ...ICEN_LOCALE_ZH_CN };
946
+ function t(key, vars) {
947
+ const raw = current[key] ?? ICEN_LOCALE_ZH_CN[key];
948
+ if (vars == null || Array.isArray(raw)) return raw;
949
+ return raw.replace(/\{(\w+)\}/g, (m, name) => name in vars ? String(vars[name]) : m);
950
+ }
951
+
952
+ // src/behaviors/svg-parse.ts
953
+ function parseSvg(source) {
954
+ if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
955
+ const trimmed = source.trimStart();
956
+ if (!trimmed.startsWith("<svg")) return null;
957
+ const src = /^<svg(?![^>]*\bxmlns[=:])/i.test(trimmed) ? trimmed.replace(/^<svg/i, '<svg xmlns="http://www.w3.org/2000/svg"') : trimmed;
958
+ let parsed;
959
+ try {
960
+ parsed = new DOMParser().parseFromString(src, "image/svg+xml");
961
+ } catch {
962
+ return null;
963
+ }
964
+ const root = parsed.documentElement;
965
+ if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) return null;
966
+ const sanitize = (el) => {
967
+ for (const attr of Array.from(el.attributes)) {
968
+ const name = attr.name.toLowerCase();
969
+ if (name.startsWith("on")) el.removeAttribute(attr.name);
970
+ if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
971
+ el.removeAttribute(attr.name);
972
+ }
973
+ }
974
+ for (const child of Array.from(el.children)) {
975
+ const tag = child.tagName.toLowerCase();
976
+ if (tag === "script" || tag === "foreignobject") {
977
+ child.remove();
978
+ continue;
979
+ }
980
+ sanitize(child);
981
+ }
982
+ };
983
+ sanitize(root);
984
+ return document.importNode(root, true);
985
+ }
986
+
1
987
  // src/behaviors/ai-core.ts
2
988
  var USAGE_KEYS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"];
3
989
  function addUsage(sum2, u) {
@@ -8,54 +994,23 @@ function addUsage(sum2, u) {
8
994
  }
9
995
  return out;
10
996
  }
11
- var svg = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
12
- var ICON_TERMINAL = svg('<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>');
13
- var ICON_FILE = svg(
14
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>'
15
- );
16
- var ICON_FILE_PEN = svg(
17
- '<path d="M12.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8l5 5v3.5"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M19 6.5 16.5 4"/><path d="M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"/>'
18
- );
19
- var ICON_FILE_PLUS = svg(
20
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 15h6"/><path d="M12 12v6"/>'
21
- );
22
- var ICON_TRASH = svg(
23
- '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/>'
24
- );
25
- var ICON_SEARCH = svg('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>');
26
- var ICON_FOLDER_SEARCH = svg(
27
- '<path d="M11 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v4"/><circle cx="17" cy="17" r="3"/><path d="m21 21-1.9-1.9"/>'
28
- );
29
- var ICON_SCAN_SEARCH = svg(
30
- '<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.9-1.9"/>'
31
- );
32
- var ICON_GLOBE = svg(
33
- '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>'
34
- );
35
- var ICON_DOWNLOAD = svg(
36
- '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>'
37
- );
38
- var ICON_BLOCKS = svg(
39
- '<rect width="7" height="7" x="4" y="4" rx="1"/><rect width="7" height="7" x="13" y="4" rx="1"/><rect width="7" height="7" x="4" y="13" rx="1"/><rect width="7" height="7" x="13" y="13" rx="1"/>'
40
- );
41
- var ICON_ZAP = svg(
42
- '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>'
43
- );
44
- var ICON_LIST = svg(
45
- '<path d="M3 12h.01"/><path d="M3 18h.01"/><path d="M3 6h.01"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M8 6h13"/>'
46
- );
47
- var ICON_LIST_CHECKS = svg(
48
- '<path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/>'
49
- );
50
- var ICON_BOT = svg(
51
- '<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'
52
- );
53
- var ICON_CHART = svg(
54
- '<path d="M3 3v18h18"/><path d="M7 15l4-6 3 4 5-8"/>'
55
- );
56
- var ICON_FILE_TEXT = svg(
57
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>'
58
- );
997
+ var ICON_TERMINAL = iconHTML("terminal");
998
+ var ICON_FILE = iconHTML("file");
999
+ var ICON_FILE_PEN = iconHTML("edit");
1000
+ var ICON_FILE_PLUS = iconHTML("plus");
1001
+ var ICON_TRASH = iconHTML("trash");
1002
+ var ICON_SEARCH = iconHTML("search");
1003
+ var ICON_FOLDER_SEARCH = iconHTML("folder");
1004
+ var ICON_SCAN_SEARCH = iconHTML("search");
1005
+ var ICON_GLOBE = iconHTML("globe");
1006
+ var ICON_DOWNLOAD = iconHTML("download");
1007
+ var ICON_BLOCKS = iconHTML("server");
1008
+ var ICON_ZAP = iconHTML("sparkle");
1009
+ var ICON_LIST = iconHTML("list");
1010
+ var ICON_LIST_CHECKS = iconHTML("list");
1011
+ var ICON_BOT = iconHTML("agent");
1012
+ var ICON_CHART = iconHTML("chart-line");
1013
+ var ICON_FILE_TEXT = iconHTML("document");
59
1014
  function isObj(v) {
60
1015
  return typeof v === "object" && v !== null;
61
1016
  }
@@ -105,32 +1060,32 @@ var sum = (fn) => {
105
1060
  return (input) => fn(input) ?? "";
106
1061
  };
107
1062
  var BUILTIN_KINDS = {
108
- shell: { label: "\u547D\u4EE4", icon: ICON_TERMINAL, tint: "accent", summarize: sum(summarizeShell) },
109
- read: { label: "\u8BFB\u53D6", icon: ICON_FILE, tint: "info", summarize: sum(summarizeRead) },
110
- edit: { label: "\u7F16\u8F91", icon: ICON_FILE_PEN, tint: "warning", summarize: sum(summarizePath) },
111
- write: { label: "\u5199\u5165", icon: ICON_FILE_PLUS, tint: "warning", summarize: sum(summarizePath) },
112
- rm: { label: "\u5220\u9664", icon: ICON_TRASH, tint: "error", summarize: sum(summarizePath) },
113
- grep: { label: "\u641C\u7D22", icon: ICON_SEARCH, tint: "info", summarize: sum(summarizeGrep) },
114
- glob: { label: "\u5339\u914D", icon: ICON_FOLDER_SEARCH, tint: "info", summarize: sum((i) => pickStr(i, ["pattern", "glob", "path"])) },
115
- browser: { label: "\u6D4F\u89C8", icon: ICON_GLOBE, tint: "accent", summarize: sum((i) => pickStr(i, ["url", "uri", "target"])) },
116
- search: { label: "\u68C0\u7D22", icon: ICON_SCAN_SEARCH, tint: "accent", summarize: sum((i) => pickStr(i, ["query", "q", "pattern"])) },
117
- fetch: { label: "\u8BF7\u6C42", icon: ICON_DOWNLOAD, tint: "info", summarize: sum((i) => pickStr(i, ["url", "uri", "endpoint"])) },
1063
+ shell: { label: t("aiCore.kindShell"), icon: ICON_TERMINAL, tint: "accent", summarize: sum(summarizeShell) },
1064
+ read: { label: t("aiCore.kindRead"), icon: ICON_FILE, tint: "info", summarize: sum(summarizeRead) },
1065
+ edit: { label: t("aiCore.kindEdit"), icon: ICON_FILE_PEN, tint: "warning", summarize: sum(summarizePath) },
1066
+ write: { label: t("aiCore.kindWrite"), icon: ICON_FILE_PLUS, tint: "warning", summarize: sum(summarizePath) },
1067
+ rm: { label: t("aiCore.kindDelete"), icon: ICON_TRASH, tint: "error", summarize: sum(summarizePath) },
1068
+ grep: { label: t("aiCore.kindGrep"), icon: ICON_SEARCH, tint: "info", summarize: sum(summarizeGrep) },
1069
+ glob: { label: t("aiCore.kindGlob"), icon: ICON_FOLDER_SEARCH, tint: "info", summarize: sum((i) => pickStr(i, ["pattern", "glob", "path"])) },
1070
+ browser: { label: t("aiCore.kindBrowser"), icon: ICON_GLOBE, tint: "accent", summarize: sum((i) => pickStr(i, ["url", "uri", "target"])) },
1071
+ search: { label: t("aiCore.kindSearch"), icon: ICON_SCAN_SEARCH, tint: "accent", summarize: sum((i) => pickStr(i, ["query", "q", "pattern"])) },
1072
+ fetch: { label: t("aiCore.kindFetch"), icon: ICON_DOWNLOAD, tint: "info", summarize: sum((i) => pickStr(i, ["url", "uri", "endpoint"])) },
118
1073
  mcp: { label: "MCP", icon: ICON_BLOCKS, tint: "muted", summarize: sum((i) => pickStr(i, ["tool", "method", "name"])) },
119
- skill: { label: "\u6280\u80FD", icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
1074
+ skill: { label: t("aiCore.kindSkill"), icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
120
1075
  todo: {
121
- label: "\u5F85\u529E",
1076
+ label: t("aiCore.kindTodo"),
122
1077
  icon: ICON_LIST,
123
1078
  tint: "info",
124
1079
  summarize: sum((input) => {
125
1080
  const items = input && typeof input === "object" && Array.isArray(input.items) ? input.items : Array.isArray(input) ? input : [];
126
1081
  const done = items.filter((it) => it?.status === "done").length;
127
- return items.length > 0 ? `${done}/${items.length} \u5B8C\u6210` : "";
1082
+ return items.length > 0 ? t("aiCore.todoProgress", { p0: done, p1: items.length }) : "";
128
1083
  })
129
1084
  },
130
- plan: { label: "\u8BA1\u5212", icon: ICON_LIST_CHECKS, tint: "info" },
131
- subagent: { label: "\u5B50\u4EE3\u7406", icon: ICON_BOT, tint: "accent", summarize: sum((i) => pickStr(i, ["task", "description", "prompt", "message", "subagent_type"])) },
132
- note: { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" },
133
- chart: { label: "\u56FE\u8868", icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
1085
+ plan: { label: t("aiCore.kindPlan"), icon: ICON_LIST_CHECKS, tint: "info" },
1086
+ subagent: { label: t("aiCore.kindSubagent"), icon: ICON_BOT, tint: "accent", summarize: sum((i) => pickStr(i, ["task", "description", "prompt", "message", "subagent_type"])) },
1087
+ note: { label: t("aiCore.kindNote"), icon: ICON_FILE_TEXT, tint: "muted" },
1088
+ chart: { label: t("aiCore.kindChart"), icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
134
1089
  };
135
1090
  var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
136
1091
  function normalizeUsage(raw) {
@@ -185,51 +1140,45 @@ function contextEstimate(usage) {
185
1140
  return (u.input ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.output ?? 0) + (u.reasoning ?? 0);
186
1141
  }
187
1142
  function h(tag, className, text) {
1143
+ if (typeof document === "undefined") throw new Error("h() \u4EC5\u6D4F\u89C8\u5668\u73AF\u5883\u53EF\u7528\uFF08SSR \u65F6\u7531\u8C03\u7528\u65B9\u5B88\u536B\uFF0C\u89C1\u6A21\u5757\u5934\u6CE8\u91CA\uFF09");
188
1144
  const node = document.createElement(tag);
189
1145
  if (className) node.className = className;
190
1146
  if (text != null) node.textContent = text;
191
1147
  return node;
192
1148
  }
193
- function svgIcon(svg2) {
194
- if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
195
- let parsed;
196
- try {
197
- parsed = new DOMParser().parseFromString(svg2, "image/svg+xml");
198
- } catch {
199
- return null;
200
- }
201
- const root = parsed.documentElement;
202
- if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) {
203
- return null;
204
- }
205
- const sanitize = (el) => {
206
- for (const attr of Array.from(el.attributes)) {
207
- const name = attr.name.toLowerCase();
208
- if (name.startsWith("on")) el.removeAttribute(attr.name);
209
- if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
210
- el.removeAttribute(attr.name);
211
- }
212
- }
213
- for (const child of Array.from(el.children)) {
214
- const tag = child.tagName.toLowerCase();
215
- if (tag === "script" || tag === "foreignobject") {
216
- child.remove();
217
- continue;
218
- }
219
- sanitize(child);
220
- }
221
- };
222
- sanitize(root);
223
- return document.importNode(root, true);
1149
+ function svgIcon(svg) {
1150
+ return parseSvg(svg);
224
1151
  }
225
1152
 
226
1153
  // src/behaviors/popover.ts
1154
+ function revealPopoverItem(item, scrollport, opts = {}) {
1155
+ if (typeof document === "undefined") return;
1156
+ if (opts.focus) item.focus({ preventScroll: true });
1157
+ const rect = item.getBoundingClientRect(), bounds = scrollport.getBoundingClientRect();
1158
+ const style = getComputedStyle(scrollport);
1159
+ const inset = (value, extent) => {
1160
+ const amount = Number.parseFloat(value) || 0;
1161
+ return value.endsWith("%") ? amount * extent / 100 : amount;
1162
+ };
1163
+ if (opts.axis !== "inline") {
1164
+ const top = bounds.top + scrollport.clientTop + inset(style.scrollPaddingTop, scrollport.clientHeight);
1165
+ const bottom = bounds.top + scrollport.clientTop + scrollport.clientHeight - inset(style.scrollPaddingBottom, scrollport.clientHeight);
1166
+ if (rect.top < top) scrollport.scrollTop -= top - rect.top;
1167
+ else if (rect.bottom > bottom) scrollport.scrollTop += rect.bottom - bottom;
1168
+ }
1169
+ if (opts.axis === "inline" || opts.axis === "both") {
1170
+ const left = bounds.left + scrollport.clientLeft + inset(style.scrollPaddingLeft, scrollport.clientWidth);
1171
+ const right = bounds.left + scrollport.clientLeft + scrollport.clientWidth - inset(style.scrollPaddingRight, scrollport.clientWidth);
1172
+ if (rect.left < left) scrollport.scrollLeft -= left - rect.left;
1173
+ else if (rect.right > right) scrollport.scrollLeft += rect.right - right;
1174
+ }
1175
+ }
227
1176
  var PANEL_SIZING_ATTRS = {
228
- width: "data-panel-width",
229
- minWidth: "data-panel-min",
230
- maxWidth: "data-panel-max",
231
- minHeight: "data-panel-min-height",
232
- maxHeight: "data-panel-max-height"
1177
+ width: "data-icen-panel-width",
1178
+ minWidth: "data-icen-panel-min",
1179
+ maxWidth: "data-icen-panel-max",
1180
+ minHeight: "data-icen-panel-min-height",
1181
+ maxHeight: "data-icen-panel-max-height"
233
1182
  };
234
1183
  function readPanelSizing(el) {
235
1184
  const out = {};
@@ -289,10 +1238,10 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
289
1238
  const preferredSide = opts.side ?? "top";
290
1239
  const align = opts.align ?? "start";
291
1240
  if (preferredSide === "left" || preferredSide === "right") {
1241
+ const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
292
1242
  const availableLeft = Math.max(0, anchor.left - margin - offset);
293
1243
  const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
294
- const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
295
- const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
1244
+ const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= minWidth2 ? preferredSide : preferredSide === "left" ? "right" : "left";
296
1245
  const maxWidth2 = Math.min(
297
1246
  Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
298
1247
  side2 === "left" ? availableLeft : availableRight
@@ -321,7 +1270,7 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
321
1270
  const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
322
1271
  const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
323
1272
  const fallbackHeight = Math.min(260, requestedMaxHeight);
324
- const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
1273
+ const contentHeight = opts.contentHeightHint != null && opts.contentHeightHint > 0 ? opts.contentHeightHint : fallbackHeight;
325
1274
  const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
326
1275
  const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
327
1276
  const sideSpace = side === "top" ? availableTop : availableBottom;
@@ -338,6 +1287,9 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
338
1287
  return { side, left, top, width, maxHeight };
339
1288
  }
340
1289
  var openPanels = /* @__PURE__ */ new Map();
1290
+ var closingPanels = /* @__PURE__ */ new Map();
1291
+ var EXIT_FALLBACK_MS = 240;
1292
+ var menuCloser = null;
341
1293
  function rectFromElement(el) {
342
1294
  const r = el.getBoundingClientRect();
343
1295
  return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
@@ -355,21 +1307,54 @@ function relayout(state) {
355
1307
  closePopover(state.panel);
356
1308
  return;
357
1309
  }
358
- const layout = computePopoverLayout(
1310
+ let layout = computePopoverLayout(
359
1311
  anchor,
360
1312
  { width: window.innerWidth, height: window.innerHeight },
361
1313
  state.opts
362
1314
  );
363
1315
  const { panel } = state;
1316
+ const scrollPositions = [panel, ...panel.querySelectorAll("*")].filter((node) => node.scrollTop !== 0 || node.scrollLeft !== 0).map((node) => ({ node, top: node.scrollTop, left: node.scrollLeft }));
1317
+ panel.style.width = `${layout.width}px`;
1318
+ if (state.opts.contentHeightHint == null && (layout.side === "top" || layout.side === "bottom")) {
1319
+ panel.style.maxHeight = `${Math.min(state.opts.maxHeight ?? DEFAULT_MAX_HEIGHT, Math.max(80, window.innerHeight - (state.opts.margin ?? 12) * 2))}px`;
1320
+ const measured = panel.offsetHeight;
1321
+ if (measured > 0) layout = computePopoverLayout(anchor, { width: window.innerWidth, height: window.innerHeight }, { ...state.opts, contentHeightHint: measured });
1322
+ }
364
1323
  panel.style.left = `${layout.left}px`;
365
- panel.style.top = `${layout.top}px`;
366
1324
  panel.style.width = `${layout.width}px`;
367
1325
  panel.style.maxHeight = `${layout.maxHeight}px`;
368
- panel.dataset.side = layout.side;
1326
+ let top = layout.top;
1327
+ const actualHeight = panel.offsetHeight;
1328
+ if (actualHeight > 0 && (layout.side === "top" || layout.side === "bottom")) {
1329
+ const desired = layout.side === "top" ? anchor.top - (state.opts.offset ?? 8) - actualHeight : anchor.bottom + (state.opts.offset ?? 8);
1330
+ const margin = state.opts.margin ?? 12;
1331
+ top = clamp(desired, margin, window.innerHeight - margin - actualHeight);
1332
+ }
1333
+ panel.style.top = `${top}px`;
1334
+ for (const pos of scrollPositions) {
1335
+ pos.node.scrollTop = pos.top;
1336
+ pos.node.scrollLeft = pos.left;
1337
+ }
1338
+ panel.dataset.icenSide = layout.side;
369
1339
  panel.style.visibility = state.prevVisibility;
370
1340
  }
371
- function onWindowChange() {
372
- openPanels.forEach(relayout);
1341
+ function observeOpenPanel(state) {
1342
+ if (typeof ResizeObserver === "undefined") return;
1343
+ state.resizeObserver = new ResizeObserver(() => {
1344
+ if (state.layoutFrame != null) return;
1345
+ state.layoutFrame = window.requestAnimationFrame(() => {
1346
+ state.layoutFrame = void 0;
1347
+ if (openPanels.get(state.panel) === state) relayout(state);
1348
+ });
1349
+ });
1350
+ state.resizeObserver.observe(state.panel);
1351
+ if (state.anchor instanceof Element) state.resizeObserver.observe(state.anchor);
1352
+ }
1353
+ function onWindowChange(event) {
1354
+ openPanels.forEach((state) => {
1355
+ if (event?.type === "scroll" && event.target instanceof Node && state.panel.contains(event.target)) return;
1356
+ relayout(state);
1357
+ });
373
1358
  }
374
1359
  function onOutsidePointerDown(e) {
375
1360
  const target = e.target;
@@ -381,18 +1366,40 @@ function onOutsidePointerDown(e) {
381
1366
  });
382
1367
  }
383
1368
  function onKeyDown(e) {
384
- if (e.key !== "Escape") return;
1369
+ if (e.key !== "Escape" || e.defaultPrevented || e.isComposing || e.keyCode === 229 || menuCloser) return;
385
1370
  const states = Array.from(openPanels.values());
386
1371
  const top = states[states.length - 1];
387
1372
  if (top) {
388
1373
  e.preventDefault();
1374
+ e.stopPropagation();
389
1375
  closePopover(top.panel);
390
1376
  }
391
1377
  }
392
1378
  function openPopover(panel, options) {
393
1379
  if (typeof document === "undefined") return;
1380
+ const pending = closingPanels.get(panel);
1381
+ if (pending) {
1382
+ window.clearTimeout(pending.timer);
1383
+ panel.removeEventListener("transitionend", pending.onEnd);
1384
+ closingPanels.delete(panel);
1385
+ delete panel.dataset.icenState;
1386
+ openPanels.set(panel, pending.state);
1387
+ panel.hidden = false;
1388
+ panel.style.visibility = "hidden";
1389
+ relayout(pending.state);
1390
+ observeOpenPanel(pending.state);
1391
+ if (openPanels.size === 1) {
1392
+ window.addEventListener("resize", onWindowChange);
1393
+ window.addEventListener("scroll", onWindowChange, true);
1394
+ document.addEventListener("pointerdown", onOutsidePointerDown, true);
1395
+ window.addEventListener("keydown", onKeyDown, true);
1396
+ }
1397
+ const ifs = options.initialFocus;
1398
+ if (ifs && panel.contains(ifs)) ifs.focus({ preventScroll: true });
1399
+ return;
1400
+ }
394
1401
  if (openPanels.has(panel)) closePopover(panel);
395
- const { anchor, onClose, sizing, ...rest } = options;
1402
+ const { anchor, onClose, sizing, initialFocus, restoreFocus, ...rest } = options;
396
1403
  const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
397
1404
  const placeholder = document.createComment("popover-anchor");
398
1405
  panel.parentNode?.insertBefore(placeholder, panel);
@@ -405,25 +1412,25 @@ function openPopover(panel, options) {
405
1412
  onClose,
406
1413
  placeholder,
407
1414
  wasHidden,
408
- prevVisibility
1415
+ prevVisibility,
1416
+ returnFocus: restoreFocus === false ? null : anchor instanceof HTMLElement ? anchor : document.activeElement instanceof HTMLElement ? document.activeElement : null
409
1417
  };
410
1418
  openPanels.set(panel, state);
411
1419
  document.body.appendChild(panel);
412
1420
  panel.hidden = false;
413
1421
  panel.style.visibility = "hidden";
414
1422
  relayout(state);
1423
+ observeOpenPanel(state);
1424
+ if (initialFocus && panel.contains(initialFocus)) initialFocus.focus({ preventScroll: true });
415
1425
  if (openPanels.size === 1) {
416
1426
  window.addEventListener("resize", onWindowChange);
417
1427
  window.addEventListener("scroll", onWindowChange, true);
418
1428
  document.addEventListener("pointerdown", onOutsidePointerDown, true);
419
- window.addEventListener("keydown", onKeyDown);
1429
+ window.addEventListener("keydown", onKeyDown, true);
420
1430
  }
421
1431
  }
422
- function closePopover(panel) {
423
- if (typeof document === "undefined") return;
424
- const state = openPanels.get(panel);
425
- if (!state) return;
426
- openPanels.delete(panel);
1432
+ function finalizeClose(panel, state) {
1433
+ delete panel.dataset.icenState;
427
1434
  panel.style.visibility = state.prevVisibility;
428
1435
  if (typeof state.wasHidden === "string") panel.setAttribute("hidden", state.wasHidden);
429
1436
  else panel.hidden = state.wasHidden;
@@ -431,37 +1438,66 @@ function closePopover(panel) {
431
1438
  panel.style.top = "";
432
1439
  panel.style.width = "";
433
1440
  panel.style.maxHeight = "";
434
- delete panel.dataset.side;
1441
+ delete panel.dataset.icenSide;
435
1442
  if (state.placeholder.parentNode) {
436
1443
  state.placeholder.parentNode.insertBefore(panel, state.placeholder);
437
1444
  state.placeholder.remove();
438
1445
  }
1446
+ }
1447
+ function closePopover(panel, opts) {
1448
+ if (typeof document === "undefined") return;
1449
+ const state = openPanels.get(panel);
1450
+ if (!state) return;
1451
+ openPanels.delete(panel);
1452
+ state.resizeObserver?.disconnect();
1453
+ if (state.layoutFrame != null) {
1454
+ window.cancelAnimationFrame(state.layoutFrame);
1455
+ state.layoutFrame = void 0;
1456
+ }
1457
+ const ownsFocus = panel.contains(document.activeElement);
1458
+ if (ownsFocus && state.returnFocus?.isConnected) state.returnFocus.focus({ preventScroll: true });
439
1459
  if (openPanels.size === 0) {
440
1460
  window.removeEventListener("resize", onWindowChange);
441
1461
  window.removeEventListener("scroll", onWindowChange, true);
442
1462
  document.removeEventListener("pointerdown", onOutsidePointerDown, true);
443
- window.removeEventListener("keydown", onKeyDown);
1463
+ window.removeEventListener("keydown", onKeyDown, true);
444
1464
  }
445
1465
  state.onClose?.();
446
- }
447
-
448
- // src/behaviors/events.ts
449
- function emitIcen(source, type, detail) {
450
- return source.dispatchEvent(
451
- new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
452
- );
1466
+ const calm = opts?.immediate || typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1467
+ if (calm || !panel.isConnected) {
1468
+ finalizeClose(panel, state);
1469
+ return;
1470
+ }
1471
+ panel.dataset.icenState = "closing";
1472
+ const finalize = () => {
1473
+ const pending = closingPanels.get(panel);
1474
+ if (!pending || pending.state !== state) return;
1475
+ window.clearTimeout(pending.timer);
1476
+ panel.removeEventListener("transitionend", pending.onEnd);
1477
+ closingPanels.delete(panel);
1478
+ finalizeClose(panel, state);
1479
+ };
1480
+ const onEnd = (event) => {
1481
+ if (event.target === panel && event.propertyName === "opacity") finalize();
1482
+ };
1483
+ panel.addEventListener("transitionend", onEnd);
1484
+ closingPanels.set(panel, {
1485
+ state,
1486
+ timer: window.setTimeout(finalize, EXIT_FALLBACK_MS),
1487
+ onEnd
1488
+ });
453
1489
  }
454
1490
 
455
1491
  // src/behaviors/ai-panel.ts
456
1492
  function buildTodoHead(done, total) {
457
- const head = h("div", "ai-todo-head");
458
- head.appendChild(h("span", "ai-todo-progress", `${done}/${total}`));
459
- const bar = h("div", "ai-todo-bar");
1493
+ const head = h("div", "icen-ai-todo-head");
1494
+ head.appendChild(h("span", "icen-ai-todo-progress", `${done}/${total}`));
1495
+ const bar = h("div", "icen-ai-todo-bar");
460
1496
  bar.setAttribute("role", "progressbar");
461
1497
  bar.setAttribute("aria-valuemin", "0");
462
1498
  bar.setAttribute("aria-valuemax", String(total));
463
1499
  bar.setAttribute("aria-valuenow", String(done));
464
- bar.setAttribute("aria-label", "\u4EFB\u52A1\u8FDB\u5EA6");
1500
+ bar.setAttribute("aria-label", t("aiPanel.taskProgress"));
465
1501
  const fill = document.createElement("i");
466
1502
  fill.style.width = total > 0 ? `${Math.round(done / total * 100)}%` : "0%";
467
1503
  bar.appendChild(fill);
@@ -470,22 +1506,22 @@ function buildTodoHead(done, total) {
470
1506
  }
471
1507
  var ICON_TODO_CHECK = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m3.5 12.5 5 5L20.5 6" pathLength="1"/></svg>';
472
1508
  function buildTodoItem(item, index) {
473
- const row = h("div", `ai-todo-item is-${item.status}`);
474
- row.dataset.index = String(index);
475
- row.appendChild(h("span", "ai-item-status"));
476
- const check = h("span", "ai-todo-check is-anim");
1509
+ const row = h("div", `icen-ai-todo-item is-${item.status}`);
1510
+ row.dataset.icenIndex = String(index);
1511
+ row.appendChild(h("span", "icen-ai-item-status"));
1512
+ const check = h("span", "icen-ai-todo-check is-anim");
477
1513
  check.setAttribute("aria-hidden", "true");
478
1514
  const checkSvg = svgIcon(ICON_TODO_CHECK);
479
1515
  if (checkSvg) check.appendChild(checkSvg);
480
1516
  row.appendChild(check);
481
- row.appendChild(h("span", "ai-todo-text", item.content));
482
- row.appendChild(h("span", "ai-todo-active", item.activeForm ?? item.content));
1517
+ row.appendChild(h("span", "icen-ai-todo-text", item.content));
1518
+ row.appendChild(h("span", "icen-ai-todo-active", item.activeForm ?? item.content));
483
1519
  return row;
484
1520
  }
485
1521
  function renderAiTodo(el, items) {
486
1522
  if (typeof document === "undefined") return el;
487
1523
  el.textContent = "";
488
- const root = h("div", "ai-todo");
1524
+ const root = h("div", "icen-ai-todo");
489
1525
  const list = Array.isArray(items) ? items : [];
490
1526
  const done = list.filter((i) => i.status === "done").length;
491
1527
  root.appendChild(buildTodoHead(done, list.length));
@@ -510,38 +1546,38 @@ function renderAiUsage(el, usage, opts = {}) {
510
1546
  if (typeof document === "undefined") return el;
511
1547
  const u = normalizeUsage(usage);
512
1548
  const segs = [
513
- { key: "input", className: "ai-usage-seg--input", label: "\u8F93\u5165", value: u.input ?? 0 },
514
- { key: "output", className: "ai-usage-seg--output", label: "\u8F93\u51FA", value: u.output ?? 0 },
515
- { key: "cacheRead", className: "ai-usage-seg--cache-read", label: "\u7F13\u5B58\u8BFB", value: u.cacheRead ?? 0 },
516
- { key: "cacheWrite", className: "ai-usage-seg--cache-write", label: "\u7F13\u5B58\u5199", value: u.cacheWrite ?? 0 },
517
- { key: "reasoning", className: "ai-usage-seg--reasoning", label: "\u63A8\u7406", value: u.reasoning ?? 0 }
1549
+ { key: "input", className: "icen-ai-usage-seg--input", label: t("common.input"), value: u.input ?? 0 },
1550
+ { key: "output", className: "icen-ai-usage-seg--output", label: t("common.output"), value: u.output ?? 0 },
1551
+ { key: "cacheRead", className: "icen-ai-usage-seg--cache-read", label: t("aiPanel.cacheRead"), value: u.cacheRead ?? 0 },
1552
+ { key: "cacheWrite", className: "icen-ai-usage-seg--cache-write", label: t("aiPanel.cacheWrite"), value: u.cacheWrite ?? 0 },
1553
+ { key: "reasoning", className: "icen-ai-usage-seg--reasoning", label: t("aiPanel.reasoning"), value: u.reasoning ?? 0 }
518
1554
  ];
519
1555
  const activeSegs = segs.filter((s) => s.value > 0);
520
1556
  const sum2 = activeSegs.reduce((acc, s) => acc + s.value, 0);
521
1557
  const total = u.total ?? sum2;
522
1558
  el.textContent = "";
523
- const root = h("div", "ai-usage");
524
- const bar = h("div", "ai-usage-bar");
525
- const summary = activeSegs.length > 0 ? activeSegs.map((s) => `${s.label} ${formatTokens(s.value)}`).join("\uFF0C") : "\u6682\u65E0\u7528\u91CF";
1559
+ const root = h("div", "icen-ai-usage");
1560
+ const bar = h("div", "icen-ai-usage-bar");
1561
+ const summary = activeSegs.length > 0 ? activeSegs.map((s) => `${s.label} ${formatTokens(s.value)}`).join("\uFF0C") : t("aiPanel.noUsage");
526
1562
  bar.setAttribute("role", "img");
527
- bar.setAttribute("aria-label", `\u7528\u91CF\uFF1A${summary}`);
1563
+ bar.setAttribute("aria-label", t("aiPanel.usageAria", { p0: summary }));
528
1564
  for (const s of activeSegs) {
529
1565
  const seg = document.createElement("i");
530
- seg.className = `ai-usage-seg ${s.className}`;
1566
+ seg.className = `icen-ai-usage-seg ${s.className}`;
531
1567
  seg.style.width = sum2 > 0 ? `${s.value / sum2 * 100}%` : "0%";
532
1568
  bar.appendChild(seg);
533
1569
  }
534
1570
  root.appendChild(bar);
535
1571
  if (activeSegs.length > 0) {
536
- const legend = h("div", "ai-usage-legend");
1572
+ const legend = h("div", "icen-ai-usage-legend");
537
1573
  for (const s of activeSegs) {
538
- const item = h("span", "ai-usage-legend-item");
1574
+ const item = h("span", "icen-ai-usage-legend-item");
539
1575
  const dot = document.createElement("i");
540
- dot.className = `ai-usage-dot ${s.className}`;
1576
+ dot.className = `icen-ai-usage-dot ${s.className}`;
541
1577
  dot.setAttribute("aria-hidden", "true");
542
1578
  item.appendChild(dot);
543
- item.appendChild(h("span", "ai-usage-legend-label", s.label));
544
- item.appendChild(h("span", "ai-usage-legend-value", formatTokens(s.value)));
1579
+ item.appendChild(h("span", "icen-ai-usage-legend-label", s.label));
1580
+ item.appendChild(h("span", "icen-ai-usage-legend-value", formatTokens(s.value)));
545
1581
  legend.appendChild(item);
546
1582
  }
547
1583
  root.appendChild(legend);
@@ -553,7 +1589,7 @@ function renderAiUsage(el, usage, opts = {}) {
553
1589
  if (ttft != null) metricBits.push(`TTFT ${formatDuration(ttft)}`);
554
1590
  if (tps != null && tps > 0) metricBits.push(`${trimNum(tps)} tok/s`);
555
1591
  if (finish) metricBits.push(`finish: ${finish}`);
556
- if (metricBits.length > 0) root.appendChild(h("div", "ai-usage-metrics", metricBits.join(" \xB7 ")));
1592
+ if (metricBits.length > 0) root.appendChild(h("div", "icen-ai-usage-metrics", metricBits.join(" \xB7 ")));
557
1593
  const totalParts = [formatTokens(total)];
558
1594
  if (typeof opts.total === "number" && opts.total > 0) {
559
1595
  const pct = Math.min(100, Math.round(total / opts.total * 100));
@@ -562,7 +1598,7 @@ function renderAiUsage(el, usage, opts = {}) {
562
1598
  if (typeof opts.cost === "number" && Number.isFinite(opts.cost)) {
563
1599
  totalParts.push(` \xB7 $${opts.cost.toFixed(4)}`);
564
1600
  }
565
- root.appendChild(h("div", "ai-usage-total", totalParts.join("")));
1601
+ root.appendChild(h("div", "icen-ai-usage-total", totalParts.join("")));
566
1602
  el.appendChild(root);
567
1603
  return el;
568
1604
  }
@@ -573,31 +1609,33 @@ function renderAiUsageRing(el, usage, opts = {}) {
573
1609
  const hasLimit = typeof opts.total === "number" && opts.total > 0;
574
1610
  const pct = hasLimit ? Math.min(100, Math.max(0, Math.round(used / opts.total * 100))) : 0;
575
1611
  el.textContent = "";
576
- const btn = h("button", "ai-usage-ring");
1612
+ const btn = h("button", "icen-ai-usage-ring");
577
1613
  btn.type = "button";
578
1614
  btn.classList.add(pct >= 85 ? "is-hot" : pct >= 60 ? "is-warn" : "is-ok");
579
1615
  btn.setAttribute("aria-haspopup", "true");
580
1616
  btn.setAttribute(
581
1617
  "aria-label",
582
- hasLimit ? `\u4E0A\u4E0B\u6587\u7A97\u53E3\u5360\u7528 ${pct}%\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5` : `\u5DF2\u7528 ${formatTokens(used)}\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5`
1618
+ hasLimit ? t("aiPanel.ringPercent", { p0: pct }) : t("aiPanel.ringUsed", { p0: formatTokens(used) })
583
1619
  );
584
1620
  const NS = "http://www.w3.org/2000/svg";
585
- const svg2 = document.createElementNS(NS, "svg");
586
- svg2.setAttribute("viewBox", "0 0 36 36");
587
- svg2.setAttribute("aria-hidden", "true");
1621
+ const svg = document.createElementNS(NS, "svg");
1622
+ svg.setAttribute("viewBox", "0 0 36 36");
1623
+ svg.setAttribute("width", "36");
1624
+ svg.setAttribute("height", "36");
1625
+ svg.setAttribute("aria-hidden", "true");
588
1626
  const track = document.createElementNS(NS, "circle");
589
- track.setAttribute("class", "ai-usage-ring-track");
1627
+ track.setAttribute("class", "icen-ai-usage-ring-track");
590
1628
  const fill = document.createElementNS(NS, "circle");
591
- fill.setAttribute("class", "ai-usage-ring-fill");
1629
+ fill.setAttribute("class", "icen-ai-usage-ring-fill");
592
1630
  fill.setAttribute("stroke-dasharray", `${pct} 100`);
593
1631
  for (const c of [track, fill]) {
594
1632
  c.setAttribute("cx", "18");
595
1633
  c.setAttribute("cy", "18");
596
1634
  c.setAttribute("r", "15.9155");
597
- svg2.appendChild(c);
1635
+ svg.appendChild(c);
598
1636
  }
599
- btn.appendChild(svg2);
600
- btn.appendChild(h("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
1637
+ btn.appendChild(svg);
1638
+ btn.appendChild(h("span", "icen-ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
601
1639
  let panel = null;
602
1640
  const closePanel = () => {
603
1641
  if (!panel) return;
@@ -611,9 +1649,9 @@ function renderAiUsageRing(el, usage, opts = {}) {
611
1649
  closePanel();
612
1650
  return;
613
1651
  }
614
- const p = h("div", "popover ai-usage-popover");
1652
+ const p = h("div", "icen-popover icen-ai-usage-popover");
615
1653
  p.hidden = true;
616
- p.appendChild(h("div", "ai-usage-popover-title", opts.title ?? "\u4E0A\u4E0B\u6587\u7A97\u53E3"));
1654
+ p.appendChild(h("div", "icen-ai-usage-popover-title", opts.title ?? t("aiPanel.contextWindow")));
617
1655
  const body = h("div", "ai-usage-popover-body");
618
1656
  renderAiUsage(body, usage, opts);
619
1657
  p.appendChild(body);
@@ -650,26 +1688,26 @@ var ICON_SPARKLES = svgWrap(
650
1688
  );
651
1689
 
652
1690
  // src/behaviors/ai-chat.ts
653
- var ICON_COPY = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>';
654
- var ICON_RETRY = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>';
1691
+ var ICON_COPY = iconHTML("copy");
1692
+ var ICON_RETRY = iconHTML("refresh");
655
1693
  function emit(target, name, detail) {
656
1694
  emitIcen(target, name, detail);
657
1695
  }
658
1696
  var REASONING_AUTO_CLOSE_MS = 1e3;
659
1697
  function setReasoningOpen(reasoning, open) {
660
- const head = reasoning.querySelector(".ai-reasoning-head");
661
- const body = reasoning.querySelector(".ai-reasoning-body");
1698
+ const head = reasoning.querySelector(".icen-ai-reasoning-head");
1699
+ const body = reasoning.querySelector(".icen-ai-reasoning-body");
662
1700
  head?.setAttribute("aria-expanded", String(open));
663
1701
  reasoning.classList.toggle("is-open", open);
664
1702
  if (body) body.hidden = !open;
665
1703
  }
666
1704
  function clearContinueSlot(scope) {
667
- scope?.querySelectorAll(".ai-msg-continue").forEach((n) => n.remove());
1705
+ scope?.querySelectorAll(".icen-ai-msg-continue").forEach((n) => n.remove());
668
1706
  }
669
1707
  function createAiStream(el) {
670
- const reasoning = el.closest(".ai-reasoning");
671
- const msgBody = el.closest(".ai-msg-body");
672
- const msg = el.closest(".ai-msg");
1708
+ const reasoning = el.closest(".icen-ai-reasoning");
1709
+ const msgBody = el.closest(".icen-ai-msg-body");
1710
+ const msg = el.closest(".icen-ai-msg");
673
1711
  const startedAt = Date.now();
674
1712
  let state = "streaming";
675
1713
  el.classList.add("is-streaming");
@@ -677,18 +1715,18 @@ function createAiStream(el) {
677
1715
  if (reasoning) {
678
1716
  reasoning.classList.add("is-streaming");
679
1717
  setReasoningOpen(reasoning, true);
680
- const head = reasoning.querySelector(".ai-reasoning-head");
681
- if (head && head.dataset.reasoningStart == null) head.dataset.reasoningStart = String(startedAt);
1718
+ const head = reasoning.querySelector(".icen-ai-reasoning-head");
1719
+ if (head && head.dataset.icenReasoningStart == null) head.dataset.icenReasoningStart = String(startedAt);
682
1720
  }
683
1721
  clearContinueSlot(msg ?? msgBody ?? el);
684
1722
  const fillReasoningTime = () => {
685
1723
  if (!reasoning) return;
686
- const time = reasoning.querySelector(".ai-reasoning-time");
1724
+ const time = reasoning.querySelector(".icen-ai-reasoning-time");
687
1725
  if (!time) return;
688
- const head = reasoning.querySelector(".ai-reasoning-head");
689
- const raw = Number(head?.dataset.reasoningStart ?? "");
1726
+ const head = reasoning.querySelector(".icen-ai-reasoning-head");
1727
+ const raw = Number(head?.dataset.icenReasoningStart ?? "");
690
1728
  const t0 = Number.isFinite(raw) && raw > 0 ? raw : startedAt;
691
- time.textContent = `\u601D\u8003 ${formatDuration(Math.max(0, Date.now() - t0))}`;
1729
+ time.textContent = t("aiChat.thinking", { p0: formatDuration(Math.max(0, Date.now() - t0)) });
692
1730
  };
693
1731
  const scheduleReasoningAutoClose = () => {
694
1732
  if (!reasoning) return;
@@ -708,11 +1746,11 @@ function createAiStream(el) {
708
1746
  const appendContinueSlot = () => {
709
1747
  if (typeof document === "undefined") return;
710
1748
  const mount = msgBody ?? msg ?? (el.parentElement instanceof HTMLElement ? el.parentElement : null);
711
- if (!mount || mount.querySelector(":scope > .ai-msg-continue")) return;
1749
+ if (!mount || mount.querySelector(":scope > .icen-ai-msg-continue")) return;
712
1750
  const btn = document.createElement("button");
713
1751
  btn.type = "button";
714
- btn.className = "ai-msg-continue";
715
- btn.textContent = "\u7EE7\u7EED\u751F\u6210";
1752
+ btn.className = "icen-ai-msg-continue";
1753
+ btn.textContent = t("aiChat.continue");
716
1754
  btn.addEventListener("click", () => {
717
1755
  const target = msg ?? btn;
718
1756
  emit(target, "icen:ai-retry", { el: target });
@@ -758,10 +1796,10 @@ function createAiStream(el) {
758
1796
  };
759
1797
  }
760
1798
  var ROLE_AVATAR = {
761
- user: "\u6211",
1799
+ user: t("aiChat.roleUser"),
762
1800
  assistant: "AI",
763
- system: "\u7CFB",
764
- tool: "\u5DE5"
1801
+ system: t("aiChat.roleSystem"),
1802
+ tool: t("aiChat.roleTool")
765
1803
  };
766
1804
  function safeHref(uri) {
767
1805
  const s = uri.trim().toLowerCase();
@@ -770,7 +1808,7 @@ function safeHref(uri) {
770
1808
  }
771
1809
  function renderAiContentPart(part) {
772
1810
  if (part.type === "text") {
773
- const span = h("span", "ai-msg-text");
1811
+ const span = h("span", "icen-ai-msg-text");
774
1812
  span.textContent = part.text;
775
1813
  if (part.state === "streaming") span.classList.add("is-streaming");
776
1814
  return span;
@@ -779,7 +1817,7 @@ function renderAiContentPart(part) {
779
1817
  const src = aiContentUrl(part);
780
1818
  if (!src) return void 0;
781
1819
  const img = document.createElement("img");
782
- img.className = "ai-msg-media ai-msg-media--image";
1820
+ img.className = "icen-ai-msg-media icen-ai-msg-media--image";
783
1821
  img.alt = part.alt ?? "";
784
1822
  img.setAttribute("loading", "lazy");
785
1823
  img.setAttribute("src", src);
@@ -789,7 +1827,7 @@ function renderAiContentPart(part) {
789
1827
  const src = aiContentUrl(part);
790
1828
  if (!src) return void 0;
791
1829
  const audio = document.createElement("audio");
792
- audio.className = "ai-msg-media ai-msg-media--audio";
1830
+ audio.className = "icen-ai-msg-media icen-ai-msg-media--audio";
793
1831
  audio.controls = true;
794
1832
  audio.setAttribute("src", src);
795
1833
  return audio;
@@ -798,28 +1836,28 @@ function renderAiContentPart(part) {
798
1836
  const src = aiContentUrl(part);
799
1837
  if (!src) return void 0;
800
1838
  const video = document.createElement("video");
801
- video.className = "ai-msg-media ai-msg-media--video";
1839
+ video.className = "icen-ai-msg-media icen-ai-msg-media--video";
802
1840
  video.controls = true;
803
1841
  video.setAttribute("playsinline", "");
804
1842
  video.setAttribute("src", src);
805
1843
  return video;
806
1844
  }
807
1845
  if (part.type === "file") {
808
- const chip = h("span", "ai-msg-file");
809
- chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
1846
+ const chip = h("span", "icen-ai-msg-file");
1847
+ chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? t("aiChat.file")));
810
1848
  const href2 = part.url ? safeHref(part.url) : void 0;
811
1849
  if (href2 && /^(https?:|data:)/i.test(href2)) {
812
1850
  const a2 = document.createElement("a");
813
- a2.className = "ai-msg-file-link";
1851
+ a2.className = "icen-ai-msg-file-link";
814
1852
  a2.href = href2;
815
1853
  a2.download = part.filename ?? "";
816
- a2.textContent = "\u4E0B\u8F7D";
1854
+ a2.textContent = t("aiChat.download");
817
1855
  chip.appendChild(a2);
818
1856
  }
819
1857
  return chip;
820
1858
  }
821
1859
  const a = document.createElement("a");
822
- a.className = "ai-msg-link";
1860
+ a.className = "icen-ai-msg-link";
823
1861
  const href = safeHref(part.uri);
824
1862
  if (href) a.href = href;
825
1863
  a.textContent = part.name ?? part.uri;
@@ -836,9 +1874,9 @@ function renderAiMessage(scrollEl, model) {
836
1874
  stream: () => createAiStream(scrollEl)
837
1875
  };
838
1876
  }
839
- const row = h("div", `ai-msg ai-msg--${model.role}`);
840
- const avatar = h("span", "ai-msg-avatar", ROLE_AVATAR[model.role] ?? "");
841
- const body = h("div", "ai-msg-body");
1877
+ const row = h("div", `icen-ai-msg icen-ai-msg--${model.role}`);
1878
+ const avatar = h("span", "icen-ai-msg-avatar", ROLE_AVATAR[model.role] ?? "");
1879
+ const body = h("div", "icen-ai-msg-body");
842
1880
  row.appendChild(avatar);
843
1881
  row.appendChild(body);
844
1882
  const parts = typeof model.content === "string" ? [{ type: "text", text: model.content }] : model.content;
@@ -846,21 +1884,21 @@ function renderAiMessage(scrollEl, model) {
846
1884
  const node = renderAiContentPart(part);
847
1885
  if (node) body.appendChild(node);
848
1886
  }
849
- if (model.model) body.appendChild(h("span", "ai-msg-model", model.model));
850
- const metaEl = h("div", "ai-msg-meta");
1887
+ if (model.model) body.appendChild(h("span", "icen-ai-msg-model", model.model));
1888
+ const metaEl = h("div", "icen-ai-msg-meta");
851
1889
  if (model.meta != null) metaEl.textContent = model.meta;
852
1890
  row.appendChild(metaEl);
853
- const actions = h("div", "ai-msg-actions");
1891
+ const actions = h("div", "icen-ai-msg-actions");
854
1892
  const copyBtn = h("button", "ai-msg-action");
855
1893
  copyBtn.type = "button";
856
- copyBtn.setAttribute("data-ai-msg-action", "copy");
857
- copyBtn.setAttribute("aria-label", "\u590D\u5236");
1894
+ copyBtn.setAttribute("data-icen-ai-msg-action", "copy");
1895
+ copyBtn.setAttribute("aria-label", t("common.copy"));
858
1896
  const copyIcon = svgIcon(ICON_COPY);
859
1897
  if (copyIcon) copyBtn.appendChild(copyIcon);
860
1898
  const retryBtn = h("button", "ai-msg-action");
861
1899
  retryBtn.type = "button";
862
- retryBtn.setAttribute("data-ai-msg-action", "retry");
863
- retryBtn.setAttribute("aria-label", "\u91CD\u8BD5");
1900
+ retryBtn.setAttribute("data-icen-ai-msg-action", "retry");
1901
+ retryBtn.setAttribute("aria-label", t("common.retry"));
864
1902
  const retryIcon = svgIcon(ICON_RETRY);
865
1903
  if (retryIcon) retryBtn.appendChild(retryIcon);
866
1904
  actions.append(copyBtn, retryBtn);
@@ -872,10 +1910,11 @@ function renderAiMessage(scrollEl, model) {
872
1910
  metaEl.textContent = meta;
873
1911
  },
874
1912
  setError(message) {
875
- row.classList.add("ai-msg--error");
876
- let errEl = row.querySelector(".ai-msg-error-text");
1913
+ row.classList.add("icen-ai-msg--error");
1914
+ let errEl = row.querySelector(".icen-ai-msg-error-text");
877
1915
  if (!errEl) {
878
- errEl = h("div", "ai-msg-error-text");
1916
+ errEl = h("div", "icen-ai-msg-error-text");
1917
+ errEl.setAttribute("role", "status");
879
1918
  body.appendChild(errEl);
880
1919
  }
881
1920
  errEl.textContent = message;
@@ -892,34 +1931,102 @@ function renderAiMessage(scrollEl, model) {
892
1931
  return handle;
893
1932
  }
894
1933
 
1934
+ // src/behaviors/input.ts
1935
+ var searchFields = /* @__PURE__ */ new WeakMap();
1936
+ function enhanceSearchField(input) {
1937
+ if (typeof document === "undefined" || !input.parentElement) return { refresh() {
1938
+ }, destroy() {
1939
+ } };
1940
+ const previous = searchFields.get(input);
1941
+ if (previous) return previous;
1942
+ const host = input.parentElement;
1943
+ const wasFocused = document.activeElement === input;
1944
+ const hadHostClass = host.classList.contains("icen-search-field");
1945
+ const hadInputClass = input.classList.contains("icen-search-input");
1946
+ const hadStandardClass = input.classList.contains("icen-input");
1947
+ const ownedWrap = !host.classList.contains("icen-input-wrap");
1948
+ const next = input.nextSibling;
1949
+ const wrap = ownedWrap ? document.createElement("div") : host;
1950
+ if (ownedWrap) {
1951
+ wrap.className = "icen-input-wrap icen-input-wrap--sm icen-input-wrap--search icen-control";
1952
+ host.insertBefore(wrap, input);
1953
+ wrap.append(input);
1954
+ }
1955
+ const leading = wrap.querySelector(".icen-input-leading") ? null : document.createElement("span");
1956
+ if (leading) {
1957
+ leading.className = "icen-input-leading";
1958
+ leading.append(createIcon("search"));
1959
+ wrap.prepend(leading);
1960
+ }
1961
+ host.classList.add("icen-search-field");
1962
+ input.classList.add("icen-search-input", "icen-input");
1963
+ const clear = document.createElement("button");
1964
+ clear.type = "button";
1965
+ clear.className = "icen-input-clear icen-search-clear";
1966
+ clear.append(createIcon("close"));
1967
+ wrap.append(clear);
1968
+ const ac = new AbortController();
1969
+ let disposed = false;
1970
+ const refresh = () => {
1971
+ if (disposed) return;
1972
+ clear.hidden = !input.value || input.disabled || input.readOnly;
1973
+ clear.setAttribute("aria-label", t("input.clearSearch"));
1974
+ };
1975
+ clear.addEventListener("pointerdown", (event) => event.preventDefault(), { signal: ac.signal });
1976
+ clear.addEventListener("click", () => {
1977
+ if (input.disabled || input.readOnly) return;
1978
+ input.value = "";
1979
+ input.dispatchEvent(new Event("input", { bubbles: true }));
1980
+ input.focus({ preventScroll: true });
1981
+ refresh();
1982
+ }, { signal: ac.signal });
1983
+ for (const event of ["input", "change", "focus"]) input.addEventListener(event, refresh, { signal: ac.signal });
1984
+ document.addEventListener("icen:locale-change", refresh, { signal: ac.signal });
1985
+ const observer = new MutationObserver(refresh);
1986
+ observer.observe(input, { attributes: true, attributeFilter: ["disabled", "readonly"] });
1987
+ refresh();
1988
+ if (wasFocused) input.focus({ preventScroll: true });
1989
+ const handle = { refresh, destroy() {
1990
+ if (disposed) return;
1991
+ disposed = true;
1992
+ observer.disconnect();
1993
+ ac.abort();
1994
+ clear.remove();
1995
+ leading?.remove();
1996
+ searchFields.delete(input);
1997
+ const focused = document.activeElement === input;
1998
+ if (ownedWrap) {
1999
+ host.insertBefore(input, next?.parentNode === host ? next : null);
2000
+ wrap.remove();
2001
+ }
2002
+ if (focused) input.focus({ preventScroll: true });
2003
+ if (!hadHostClass) host.classList.remove("icen-search-field");
2004
+ if (!hadInputClass) input.classList.remove("icen-search-input");
2005
+ if (!hadStandardClass) input.classList.remove("icen-input");
2006
+ } };
2007
+ searchFields.set(input, handle);
2008
+ return handle;
2009
+ }
2010
+
895
2011
  // src/behaviors/ai-composer.ts
896
2012
  var MAX_ROWS_DEFAULT = 8;
897
2013
  var HISTORY_LIMIT = 50;
898
2014
  var CACHE_WARN_RATIO = 0.4;
899
- var SEND_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 2-7 20-4-9-9-4z"/><path d="M22 2 11 13"/></svg>';
900
- var STOP_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>';
901
- var ATTACH_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>';
902
- var svgWrap2 = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
903
- var ICON_CHEVRON = svgWrap2('<path d="m6 9 6 6 6-6"/>');
904
- var ICON_CHECK = svgWrap2('<path d="M20 6 9 17l-5-5"/>');
905
- var ICON_INFO = svgWrap2('<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>');
906
- var ICON_FILE3 = svgWrap2(
907
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>'
908
- );
909
- var ICON_FOLDER = svgWrap2(
910
- '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>'
911
- );
912
- var ICON_DOC = svgWrap2(
913
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M16 13H8"/><path d="M16 17H8"/>'
914
- );
915
- var ICON_BOT2 = svgWrap2(
916
- '<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'
917
- );
2015
+ var SEND_ICON = iconHTML("arrow-up");
2016
+ var STOP_ICON = iconHTML("stop");
2017
+ var ATTACH_ICON = iconHTML("paperclip");
2018
+ var ICON_INFO = iconHTML("circle-info");
2019
+ var ICON_FILE3 = iconHTML("file");
2020
+ var ICON_FOLDER = iconHTML("folder");
2021
+ var ICON_DOC = iconHTML("document");
2022
+ var ICON_BOT2 = iconHTML("agent");
918
2023
  var REF_KIND_BUILTIN = {
919
- file: { label: "\u6587\u4EF6", icon: ICON_FILE3, order: 0 },
920
- folder: { label: "\u6587\u4EF6\u5939", icon: ICON_FOLDER, order: 1 },
921
- doc: { label: "\u6587\u6863", icon: ICON_DOC, order: 2 },
922
- agent: { label: "\u4EE3\u7406", icon: ICON_BOT2, order: 3 }
2024
+ skill: { label: t("aiComposer.skills"), icon: iconHTML("sparkle"), order: -2 },
2025
+ tool: { label: t("aiComposer.tools"), icon: iconHTML("box"), order: -1 },
2026
+ file: { label: t("aiComposer.attachFile"), icon: ICON_FILE3, order: 0 },
2027
+ folder: { label: t("aiComposer.attachFolder"), icon: ICON_FOLDER, order: 1 },
2028
+ doc: { label: t("aiComposer.attachDoc"), icon: ICON_DOC, order: 2 },
2029
+ agent: { label: t("aiComposer.attachAgent"), icon: ICON_BOT2, order: 3 }
923
2030
  };
924
2031
  var REF_KIND_ORDER_LAST = Number.MAX_SAFE_INTEGER;
925
2032
  var REF_KIND_REGISTRY = /* @__PURE__ */ new Map();
@@ -946,6 +2053,7 @@ function knownRefKinds(sources) {
946
2053
  }
947
2054
  var RUNNING = /* @__PURE__ */ new WeakMap();
948
2055
  var QUEUES = /* @__PURE__ */ new WeakMap();
2056
+ var FILES = /* @__PURE__ */ new WeakMap();
949
2057
  var MODELS = /* @__PURE__ */ new WeakMap();
950
2058
  var MODEL_CTX = /* @__PURE__ */ new WeakMap();
951
2059
  var COMMANDS = /* @__PURE__ */ new WeakMap();
@@ -964,14 +2072,14 @@ function emit2(target, name, detail) {
964
2072
  emitIcen(target, name, detail);
965
2073
  }
966
2074
  function resolveComposer(el) {
967
- return el.closest("[data-ai-composer]");
2075
+ return el.closest("[data-icen-ai-composer]");
968
2076
  }
969
2077
  function getTextarea(composer) {
970
- return composer.querySelector(".ai-composer-input");
2078
+ return composer.querySelector(".icen-ai-composer-input");
971
2079
  }
972
2080
  function autosizeTa(ta) {
973
- const composer = ta.closest("[data-ai-composer]");
974
- const maxRows = Number(composer?.getAttribute("data-max-rows")) || MAX_ROWS_DEFAULT;
2081
+ const composer = ta.closest("[data-icen-ai-composer]");
2082
+ const maxRows = Number(composer?.getAttribute("data-icen-max-rows")) || MAX_ROWS_DEFAULT;
975
2083
  const cs = window.getComputedStyle(ta);
976
2084
  const lineHeight = parseFloat(cs.lineHeight) || 20;
977
2085
  const padY = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
@@ -982,51 +2090,58 @@ function autosizeTa(ta) {
982
2090
  ta.style.height = `${Math.max(minH, Math.min(ta.scrollHeight, maxH))}px`;
983
2091
  }
984
2092
  function ensureIcons(composer) {
985
- const sendBtn = composer.querySelector("[data-ai-send]");
2093
+ const sendBtn = composer.querySelector("[data-icen-ai-send]");
986
2094
  if (sendBtn && !sendBtn.hasChildNodes()) {
987
2095
  const sendWrap = document.createElement("span");
988
- sendWrap.className = "ai-composer-send-icon";
989
- sendWrap.dataset.aiSendIcon = "send";
2096
+ sendWrap.className = "icen-ai-composer-send-icon";
2097
+ sendWrap.dataset.icenAiSendIcon = "send";
990
2098
  const stopWrap = document.createElement("span");
991
- stopWrap.className = "ai-composer-send-icon";
992
- stopWrap.dataset.aiSendIcon = "stop";
2099
+ stopWrap.className = "icen-ai-composer-send-icon";
2100
+ stopWrap.dataset.icenAiSendIcon = "stop";
993
2101
  const sendSvg = svgIcon(SEND_ICON);
994
2102
  const stopSvg = svgIcon(STOP_ICON);
995
2103
  if (sendSvg) sendWrap.appendChild(sendSvg);
996
2104
  if (stopSvg) stopWrap.appendChild(stopSvg);
997
2105
  sendBtn.append(sendWrap, stopWrap);
998
2106
  }
999
- const attachBtn = composer.querySelector("[data-ai-attach]");
2107
+ const attachBtn = composer.querySelector("[data-icen-ai-attach]");
1000
2108
  if (attachBtn && !attachBtn.hasChildNodes()) {
1001
2109
  const icon = svgIcon(ATTACH_ICON);
1002
2110
  if (icon) attachBtn.appendChild(icon);
1003
2111
  }
1004
- const sendIcon = sendBtn?.querySelector('[data-ai-send-icon="send"]') ?? null;
1005
- const stopIcon = sendBtn?.querySelector('[data-ai-send-icon="stop"]') ?? null;
2112
+ const sendIcon = sendBtn?.querySelector('[data-icen-ai-send-icon="send"]') ?? null;
2113
+ const stopIcon = sendBtn?.querySelector('[data-icen-ai-send-icon="stop"]') ?? null;
1006
2114
  return { sendBtn, sendIcon, stopIcon };
1007
2115
  }
1008
- function makeChip(text, removable, ariaLabel) {
2116
+ function makeChip(text, removable, ariaLabel, icon) {
1009
2117
  const chip = document.createElement("span");
1010
- chip.className = "ai-composer-chip";
2118
+ chip.className = "icen-ai-composer-chip";
2119
+ if (icon) {
2120
+ const mark = document.createElement("span");
2121
+ mark.className = "icen-ai-composer-chip-icon";
2122
+ const svg = createIcon(icon);
2123
+ if (svg) mark.append(svg);
2124
+ chip.append(mark);
2125
+ }
1011
2126
  const label = document.createElement("span");
1012
- label.className = "ai-composer-chip-text";
2127
+ label.className = "icen-ai-composer-chip-text";
1013
2128
  label.textContent = text;
1014
2129
  chip.appendChild(label);
1015
2130
  if (removable) {
1016
2131
  const remove = document.createElement("button");
1017
2132
  remove.type = "button";
1018
- remove.className = "ai-composer-chip-remove";
2133
+ remove.className = "icen-ai-composer-chip-remove";
1019
2134
  remove.setAttribute("aria-label", ariaLabel);
1020
- remove.textContent = "\xD7";
2135
+ remove.replaceChildren(createIcon("close"));
1021
2136
  chip.appendChild(remove);
1022
2137
  }
1023
2138
  return chip;
1024
2139
  }
1025
2140
  function renderQueueChips(composer) {
1026
- const queueEl = composer.querySelector(".ai-composer-queue");
2141
+ const queueEl = composer.querySelector(".icen-ai-composer-queue");
1027
2142
  if (!queueEl) return;
1028
2143
  const queue = QUEUES.get(composer) ?? [];
1029
- queueEl.replaceChildren(...queue.map((text, i) => makeChip(text, true, `\u79FB\u9664\u6392\u961F\u6D88\u606F ${i + 1}`)));
2144
+ queueEl.replaceChildren(...queue.map((draft, i) => makeChip(draft.text || [...draft.files?.map((f) => f.name) ?? [], ...draft.refs?.map((r) => r.label) ?? []].join(" \xB7 "), true, t("aiComposer.removeQueued", { p0: i + 1 }), "layers")));
1030
2145
  queueEl.hidden = queue.length === 0;
1031
2146
  }
1032
2147
  function setComposerRunning(el, running) {
@@ -1039,24 +2154,24 @@ function setComposerRunning(el, running) {
1039
2154
  } else {
1040
2155
  composer.removeAttribute("aria-busy");
1041
2156
  }
1042
- const sendBtn = composer.querySelector("[data-ai-send]");
2157
+ const sendBtn = composer.querySelector("[data-icen-ai-send]");
1043
2158
  if (!sendBtn) return;
1044
2159
  sendBtn.classList.toggle("is-stop", running);
1045
- sendBtn.setAttribute("aria-label", running ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001");
1046
- const sendIcon = sendBtn.querySelector('[data-ai-send-icon="send"]');
1047
- const stopIcon = sendBtn.querySelector('[data-ai-send-icon="stop"]');
2160
+ sendBtn.setAttribute("aria-label", running ? t("aiComposer.stop") : t("aiComposer.send"));
2161
+ const sendIcon = sendBtn.querySelector('[data-icen-ai-send-icon="send"]');
2162
+ const stopIcon = sendBtn.querySelector('[data-icen-ai-send-icon="stop"]');
1048
2163
  if (sendIcon) sendIcon.hidden = running;
1049
2164
  if (stopIcon) stopIcon.hidden = !running;
1050
2165
  }
1051
2166
  function providerMark(provider, badgeClass) {
1052
2167
  const slot = document.createElement("span");
1053
2168
  slot.className = badgeClass;
1054
- const svg2 = provider.icon ? svgIcon(provider.icon) : null;
1055
- if (svg2) {
1056
- slot.appendChild(svg2);
2169
+ const svg = provider.icon ? svgIcon(provider.icon) : null;
2170
+ if (svg) {
2171
+ slot.appendChild(svg);
1057
2172
  } else {
1058
2173
  const badge = document.createElement("span");
1059
- badge.className = "ai-composer-model-badge";
2174
+ badge.className = "icen-ai-composer-model-badge";
1060
2175
  badge.textContent = (provider.label.trim()[0] ?? "?").toUpperCase();
1061
2176
  slot.appendChild(badge);
1062
2177
  }
@@ -1065,8 +2180,8 @@ function providerMark(provider, badgeClass) {
1065
2180
  function buildModelButton(composer) {
1066
2181
  const btn = document.createElement("button");
1067
2182
  btn.type = "button";
1068
- btn.className = "ai-composer-model";
1069
- btn.setAttribute("data-ai-model-open", "");
2183
+ btn.className = "icen-ai-composer-model";
2184
+ btn.setAttribute("data-icen-ai-model-open", "");
1070
2185
  btn.setAttribute("aria-haspopup", "listbox");
1071
2186
  btn.setAttribute("aria-expanded", "false");
1072
2187
  btn.addEventListener("click", () => {
@@ -1077,66 +2192,59 @@ function buildModelButton(composer) {
1077
2192
  }
1078
2193
  function updateModelButton(composer) {
1079
2194
  const cfg = MODELS.get(composer);
1080
- const btn = composer.querySelector("[data-ai-model-open]");
2195
+ const btn = composer.querySelector("[data-icen-ai-model-open]");
1081
2196
  if (!cfg || !btn) return;
1082
2197
  const provider = cfg.providers.find((p) => p.id === cfg.current.provider) ?? cfg.providers[0];
1083
2198
  if (!provider) return;
1084
2199
  const model = provider.models.find((m) => m.id === cfg.current.model) ?? provider.models[0];
1085
2200
  if (!model) return;
1086
- const iconSlot = btn.querySelector(".ai-composer-model-icon") ?? (() => {
2201
+ const iconSlot = btn.querySelector(".icen-ai-composer-model-icon") ?? (() => {
1087
2202
  const slot = document.createElement("span");
1088
- slot.className = "ai-composer-model-icon";
2203
+ slot.className = "icen-ai-composer-model-icon";
1089
2204
  btn.prepend(slot);
1090
2205
  return slot;
1091
2206
  })();
1092
- iconSlot.replaceChildren(providerMark(provider, "ai-composer-model-provider-icon"));
1093
- let name = btn.querySelector(".ai-composer-model-name");
2207
+ iconSlot.replaceChildren(providerMark(provider, "icen-ai-composer-model-provider-icon"));
2208
+ let name = btn.querySelector(".icen-ai-composer-model-name");
1094
2209
  if (!name) {
1095
2210
  name = document.createElement("span");
1096
- name.className = "ai-composer-model-name";
1097
- btn.insertBefore(name, btn.querySelector(".ai-composer-model-chevron"));
2211
+ name.className = "icen-ai-composer-model-name";
2212
+ btn.insertBefore(name, btn.querySelector(".icen-ai-composer-model-chevron"));
1098
2213
  }
1099
2214
  name.textContent = model.label;
1100
- btn.setAttribute("aria-label", `\u6A21\u578B ${model.label}\uFF08${provider.label}\uFF09`);
1101
- if (!btn.querySelector(".ai-composer-model-chevron")) {
1102
- const chev = svgIcon(ICON_CHEVRON);
1103
- if (chev) {
1104
- const wrap = document.createElement("span");
1105
- wrap.className = "ai-composer-model-chevron";
1106
- wrap.appendChild(chev);
1107
- btn.appendChild(wrap);
1108
- }
1109
- }
2215
+ btn.setAttribute("aria-label", t("aiComposer.modelAria", { p0: model.label, p1: provider.label }));
2216
+ btn.querySelector(".icen-ai-composer-model-chevron")?.remove();
1110
2217
  }
1111
2218
  function syncToolbar(composer) {
1112
- const box = composer.querySelector(".ai-composer-box");
2219
+ const box = composer.querySelector(".icen-ai-composer-box");
1113
2220
  if (!box || !getTextarea(composer)) return;
1114
2221
  const modelsCfg = MODELS.get(composer);
1115
2222
  const usageCfg = USAGE.get(composer);
1116
- if (!modelsCfg && !usageCfg) return;
1117
- let bar = composer.querySelector(".ai-composer-toolbar");
1118
- let modelBtn = composer.querySelector("[data-ai-model-open]");
2223
+ const hasLibrary = !!(COMMANDS.get(composer)?.length || REF_SOURCES.get(composer)?.length);
2224
+ if (!modelsCfg && !usageCfg && !hasLibrary && !composer.querySelector(".icen-ai-composer-toolbar")) return;
2225
+ let bar = composer.querySelector(".icen-ai-composer-toolbar");
2226
+ let modelBtn = composer.querySelector("[data-icen-ai-model-open]");
1119
2227
  if (modelsCfg && !modelBtn) {
1120
2228
  modelBtn = buildModelButton(composer);
1121
- modelBtn.dataset.aiModelOpen = "auto";
2229
+ modelBtn.dataset.icenAiModelOpen = "auto";
1122
2230
  updateModelButton(composer);
1123
2231
  }
1124
- if (!modelsCfg && modelBtn?.dataset.aiModelOpen === "auto") {
2232
+ if (!modelsCfg && modelBtn?.dataset.icenAiModelOpen === "auto") {
1125
2233
  modelBtn.remove();
1126
2234
  modelBtn = null;
1127
2235
  }
1128
- let usageMount = composer.querySelector(".ai-composer-usage");
2236
+ let usageMount = composer.querySelector(".icen-ai-composer-usage");
1129
2237
  if (usageCfg && !usageMount) {
1130
2238
  usageMount = document.createElement("span");
1131
- usageMount.className = "ai-composer-usage";
1132
- usageMount.dataset.aiUsage = "auto";
2239
+ usageMount.className = "icen-ai-composer-usage";
2240
+ usageMount.dataset.icenAiUsage = "auto";
1133
2241
  }
1134
- if (!usageCfg && usageMount?.dataset.aiUsage === "auto") {
2242
+ if (!usageCfg && usageMount?.dataset.icenAiUsage === "auto") {
1135
2243
  usageMount.remove();
1136
2244
  usageMount = null;
1137
2245
  }
1138
- const attach = composer.querySelector("[data-ai-attach]");
1139
- const send = composer.querySelector("[data-ai-send]");
2246
+ const attach = composer.querySelector("[data-icen-ai-attach]");
2247
+ const send = composer.querySelector("[data-icen-ai-send]");
1140
2248
  if (bar) {
1141
2249
  if (modelBtn && modelBtn.parentElement !== bar) bar.insertBefore(modelBtn, bar.firstChild);
1142
2250
  if (usageMount && usageMount.parentElement !== bar) {
@@ -1144,24 +2252,36 @@ function syncToolbar(composer) {
1144
2252
  }
1145
2253
  } else {
1146
2254
  bar = document.createElement("div");
1147
- bar.className = "ai-composer-toolbar";
2255
+ bar.className = "icen-ai-composer-toolbar";
1148
2256
  bar.appendChild(modelBtn ?? document.createComment("model"));
1149
2257
  const spacer = document.createElement("span");
1150
- spacer.className = "ai-composer-spacer";
2258
+ spacer.className = "icen-ai-composer-spacer";
1151
2259
  bar.appendChild(spacer);
1152
2260
  if (usageMount) bar.appendChild(usageMount);
1153
2261
  if (attach) bar.appendChild(attach);
1154
2262
  if (send) bar.appendChild(send);
1155
2263
  box.appendChild(bar);
1156
2264
  }
1157
- const legacy = composer.querySelector(".ai-composer-actions");
1158
- if (legacy && legacy.childElementCount === 0 && !(legacy.textContent ?? "").trim()) {
1159
- legacy.remove();
1160
- }
2265
+ let library = composer.querySelector("[data-icen-ai-library]");
2266
+ if (hasLibrary && !library) {
2267
+ library = document.createElement("button");
2268
+ library.type = "button";
2269
+ library.className = "icen-ai-composer-btn icen-ai-composer-library";
2270
+ library.dataset.icenAiLibrary = "auto";
2271
+ library.setAttribute("aria-label", t("aiComposer.library"));
2272
+ library.setAttribute("aria-haspopup", "dialog");
2273
+ library.setAttribute("aria-expanded", "false");
2274
+ library.append(createIcon("plus"));
2275
+ library.addEventListener("click", () => {
2276
+ if (POPUP.get(composer)?.mode === "library") closePopup(composer);
2277
+ else openComposerLibrary(composer);
2278
+ });
2279
+ bar.prepend(library);
2280
+ } else if (!hasLibrary && library?.dataset.icenAiLibrary === "auto") library.remove();
1161
2281
  }
1162
2282
  function refreshUsageRing(composer) {
1163
2283
  const cfg = USAGE.get(composer);
1164
- const mount = composer.querySelector(".ai-composer-usage");
2284
+ const mount = composer.querySelector(".icen-ai-composer-usage");
1165
2285
  if (!cfg || !mount) return;
1166
2286
  const explicit = typeof cfg.opts.total === "number" && cfg.opts.total > 0;
1167
2287
  renderAiUsageRing(mount, cfg.usage, {
@@ -1169,18 +2289,21 @@ function refreshUsageRing(composer) {
1169
2289
  total: explicit ? cfg.opts.total : MODEL_CTX.get(composer)
1170
2290
  });
1171
2291
  }
1172
- var visibleOptions = (s) => s.options.filter((o) => !o.hidden);
2292
+ var visibleOptions = (s) => s.options.filter((o) => !o.hidden && o.getAttribute("aria-disabled") !== "true");
2293
+ var popupId = 0;
1173
2294
  function setPopupFocus(s, idx) {
1174
2295
  const vis = visibleOptions(s);
1175
2296
  s.options.forEach((o) => o.classList.remove("is-focused"));
1176
2297
  if (vis.length === 0) {
1177
2298
  s.focused = -1;
2299
+ s.control?.removeAttribute("aria-activedescendant");
1178
2300
  return;
1179
2301
  }
1180
2302
  s.focused = Math.max(0, Math.min(idx, vis.length - 1));
1181
2303
  const cur = vis[s.focused];
1182
2304
  cur.classList.add("is-focused");
1183
- cur.scrollIntoView({ block: "nearest" });
2305
+ s.control?.setAttribute("aria-activedescendant", cur.id);
2306
+ revealPopoverItem(cur, s.panel.querySelector(".icen-ai-composer-popup-list") ?? s.panel);
1184
2307
  }
1185
2308
  function movePopupFocus(s, delta) {
1186
2309
  const vis = visibleOptions(s);
@@ -1189,15 +2312,25 @@ function movePopupFocus(s, delta) {
1189
2312
  }
1190
2313
  function settlePopup(s) {
1191
2314
  for (const g of s.groups) {
1192
- g.hidden = !Array.from(g.querySelectorAll(".ai-composer-popup-option")).some((o) => !o.hidden);
2315
+ g.hidden = !Array.from(g.querySelectorAll(".icen-ai-composer-popup-option")).some((o) => !o.hidden);
1193
2316
  }
1194
- s.emptyEl.hidden = visibleOptions(s).length > 0;
2317
+ s.emptyEl.hidden = s.options.some((o) => !o.hidden);
1195
2318
  setPopupFocus(s, 0);
1196
2319
  }
1197
2320
  function filterPopupOptions(s, q) {
1198
2321
  const needle = q.trim().toLowerCase();
1199
2322
  for (const opt of s.options) {
1200
- opt.hidden = needle !== "" && !(opt.dataset.hay ?? "").includes(needle);
2323
+ opt.hidden = !!s.category && s.category !== "all" && opt.dataset.category !== s.category || needle !== "" && !(opt.dataset.hay ?? "").includes(needle);
2324
+ for (const label of opt.querySelectorAll("[data-icen-search-text]")) {
2325
+ const text = label.dataset.icenSearchText ?? "";
2326
+ const at = needle ? text.toLowerCase().indexOf(needle) : -1;
2327
+ if (at < 0) label.textContent = text;
2328
+ else {
2329
+ const mark = document.createElement("mark");
2330
+ mark.textContent = text.slice(at, at + needle.length);
2331
+ label.replaceChildren(document.createTextNode(text.slice(0, at)), mark, document.createTextNode(text.slice(at + needle.length)));
2332
+ }
2333
+ }
1201
2334
  }
1202
2335
  settlePopup(s);
1203
2336
  }
@@ -1214,6 +2347,9 @@ function popupKeydown(composer, s, e) {
1214
2347
  return;
1215
2348
  case "Enter":
1216
2349
  case "Tab": {
2350
+ if (e.key === "Tab" && (s.mode === "library" || s.mode === "model")) {
2351
+ return;
2352
+ }
1217
2353
  e.preventDefault();
1218
2354
  const vis = visibleOptions(s);
1219
2355
  const opt = vis[s.focused] ?? vis[0];
@@ -1221,10 +2357,18 @@ function popupKeydown(composer, s, e) {
1221
2357
  s.pick(opt);
1222
2358
  return;
1223
2359
  }
2360
+ if (s.mode === "library" || s.mode === "model") return;
1224
2361
  closePopup(composer, false);
1225
2362
  if (e.key === "Enter") SUBMITS.get(composer)?.();
1226
2363
  return;
1227
2364
  }
2365
+ case "Home":
2366
+ case "End":
2367
+ if (e.altKey || e.ctrlKey || e.metaKey) {
2368
+ e.preventDefault();
2369
+ setPopupFocus(s, e.key === "Home" ? 0 : visibleOptions(s).length - 1);
2370
+ }
2371
+ return;
1228
2372
  case "Escape":
1229
2373
  e.preventDefault();
1230
2374
  closePopup(composer);
@@ -1234,14 +2378,19 @@ function popupKeydown(composer, s, e) {
1234
2378
  function closePopup(composer, restoreFocus = true) {
1235
2379
  const s = POPUP.get(composer);
1236
2380
  if (!s) return;
2381
+ s.restoreFocus = restoreFocus;
1237
2382
  POPUP.delete(composer);
1238
2383
  if (s.searchInput && s.keydownHandler) {
1239
2384
  s.searchInput.removeEventListener("keydown", s.keydownHandler);
1240
2385
  }
1241
- if (s.mode === "model") {
1242
- composer.querySelector("[data-ai-model-open]")?.setAttribute("aria-expanded", "false");
2386
+ s.control?.removeAttribute("aria-activedescendant");
2387
+ s.control?.removeAttribute("aria-controls");
2388
+ if (s.mode === "library") {
2389
+ composer.querySelector("[data-icen-ai-library]")?.setAttribute("aria-expanded", "false");
2390
+ } else if (s.mode === "model") {
2391
+ composer.querySelector("[data-icen-ai-model-open]")?.setAttribute("aria-expanded", "false");
1243
2392
  } else {
1244
- getTextarea(composer)?.setAttribute("aria-expanded", "false");
2393
+ getTextarea(composer)?.removeAttribute("aria-expanded");
1245
2394
  }
1246
2395
  closePopover(s.panel);
1247
2396
  if (restoreFocus && s.returnFocus && s.returnFocus.isConnected) s.returnFocus.focus();
@@ -1253,7 +2402,9 @@ function resolvePopupSizing(composer, defaults) {
1253
2402
  function mountPopup(composer, init) {
1254
2403
  closePopup(composer, false);
1255
2404
  const panel = document.createElement("div");
1256
- panel.className = "ai-composer-popup";
2405
+ panel.className = "icen-ai-composer-popup";
2406
+ panel.dataset.icenMaterial = "floating";
2407
+ panel.dataset.icenMode = init.mode;
1257
2408
  panel.hidden = true;
1258
2409
  const session = {
1259
2410
  mode: init.mode,
@@ -1269,49 +2420,245 @@ function mountPopup(composer, init) {
1269
2420
  pick: init.pick
1270
2421
  };
1271
2422
  init.build(session);
2423
+ if (session.searchInput) session.searchField = enhanceSearchField(session.searchInput);
2424
+ const list = panel.querySelector('[role="listbox"]');
2425
+ if (list) {
2426
+ list.id = `icen-composer-list-${++popupId}`;
2427
+ session.options.forEach((o, i) => {
2428
+ o.id = `${list.id}-${i}`;
2429
+ o.tabIndex = -1;
2430
+ });
2431
+ session.control = session.searchInput ?? getTextarea(composer);
2432
+ session.control?.setAttribute("aria-controls", list.id);
2433
+ session.control?.setAttribute("aria-haspopup", "listbox");
2434
+ }
1272
2435
  openPopover(panel, {
1273
2436
  anchor: init.anchor,
1274
2437
  side: "top",
1275
2438
  align: "start",
1276
2439
  offset: 8,
1277
2440
  sizing: resolvePopupSizing(composer, init.sizing),
2441
+ restoreFocus: false,
1278
2442
  onClose: () => {
1279
2443
  if (POPUP.get(composer) === session) POPUP.delete(composer);
2444
+ session.control?.removeAttribute("aria-activedescendant");
2445
+ session.control?.removeAttribute("aria-controls");
2446
+ composer.querySelector("[data-icen-ai-library]")?.setAttribute("aria-expanded", "false");
2447
+ composer.querySelector("[data-icen-ai-model-open]")?.setAttribute("aria-expanded", "false");
2448
+ getTextarea(composer)?.removeAttribute("aria-expanded");
2449
+ if (session.restoreFocus !== false && panel.contains(document.activeElement) && session.returnFocus?.isConnected) {
2450
+ session.returnFocus.focus({ preventScroll: true });
2451
+ }
2452
+ session.searchField?.destroy();
1280
2453
  panel.remove();
1281
2454
  }
1282
2455
  });
1283
2456
  POPUP.set(composer, session);
1284
2457
  panel.addEventListener("click", (e) => {
1285
- const t = e.target;
1286
- if (!(t instanceof Element)) return;
1287
- const opt = t.closest(".ai-composer-popup-option");
1288
- if (opt && panel.contains(opt) && !opt.hidden) session.pick(opt);
2458
+ const t2 = e.target;
2459
+ if (!(t2 instanceof Element)) return;
2460
+ const opt = t2.closest(".icen-ai-composer-popup-option");
2461
+ if (opt && panel.contains(opt) && !opt.hidden && opt.getAttribute("aria-disabled") !== "true") session.pick(opt);
1289
2462
  });
1290
2463
  panel.addEventListener("mouseover", (e) => {
1291
- const t = e.target;
1292
- if (!(t instanceof Element)) return;
1293
- const opt = t.closest(".ai-composer-popup-option");
1294
- if (!opt || opt.hidden) return;
2464
+ const t2 = e.target;
2465
+ if (!(t2 instanceof Element)) return;
2466
+ const opt = t2.closest(".icen-ai-composer-popup-option");
2467
+ if (!opt || opt.hidden || opt.getAttribute("aria-disabled") === "true") return;
1295
2468
  const idx = visibleOptions(session).indexOf(opt);
1296
2469
  if (idx >= 0) setPopupFocus(session, idx);
1297
2470
  });
2471
+ panel.addEventListener("focusout", (e) => {
2472
+ const next = e.relatedTarget;
2473
+ if (next instanceof Node && !panel.contains(next) && next !== session.returnFocus) closePopup(composer, false);
2474
+ });
2475
+ panel.addEventListener("keydown", (e) => {
2476
+ if (e.key === "Escape" && !e.isComposing) {
2477
+ e.preventDefault();
2478
+ closePopup(composer);
2479
+ } else if (e.target instanceof Element && e.target.closest(".icen-ai-composer-popup-option") && ["ArrowUp", "ArrowDown"].includes(e.key)) {
2480
+ popupKeydown(composer, session, e);
2481
+ session.searchInput?.focus({ preventScroll: true });
2482
+ }
2483
+ });
1298
2484
  return session;
1299
2485
  }
1300
2486
  function appendPopupEmpty(s, text) {
1301
- s.emptyEl.className = "ai-composer-popup-empty";
2487
+ s.emptyEl.className = "icen-ai-composer-popup-empty";
1302
2488
  s.emptyEl.textContent = text;
1303
2489
  s.emptyEl.hidden = true;
1304
2490
  s.panel.appendChild(s.emptyEl);
1305
2491
  }
1306
2492
  function popupSizingFor(box) {
1307
2493
  const w = box.getBoundingClientRect().width;
1308
- return { minWidth: Math.min(Math.max(w, 200), 320), maxWidth: 360, maxHeight: 320 };
2494
+ return { width: Math.min(Math.max(w, 280), 440), maxWidth: 440, maxHeight: 400 };
2495
+ }
2496
+ function searchLeaf(text, className) {
2497
+ const el = document.createElement("span");
2498
+ el.className = className;
2499
+ el.textContent = text;
2500
+ el.dataset.icenSearchText = text;
2501
+ return el;
2502
+ }
2503
+ function resourceIcon(source) {
2504
+ const mark = document.createElement("span");
2505
+ mark.className = "icen-ai-composer-popup-option-icon";
2506
+ const svg = (source.icon ? createIcon(source.icon) : null) ?? svgIcon(getRefKindDef(source.kind).icon);
2507
+ if (svg) mark.append(svg);
2508
+ return mark;
2509
+ }
2510
+ function resourceOption(source) {
2511
+ const opt = document.createElement("button");
2512
+ opt.type = "button";
2513
+ opt.className = "icen-ai-composer-popup-option";
2514
+ opt.setAttribute("role", "option");
2515
+ opt.dataset.refId = source.id;
2516
+ opt.dataset.refKind = source.kind;
2517
+ opt.dataset.category = source.kind;
2518
+ opt.dataset.hay = [source.label, source.description, source.sub, getRefKindDef(source.kind).label, ...source.keywords ?? []].join(" ").toLowerCase();
2519
+ opt.setAttribute("aria-selected", "false");
2520
+ if (source.disabled) opt.setAttribute("aria-disabled", "true");
2521
+ const body = document.createElement("span");
2522
+ body.className = "icen-ai-composer-popup-option-copy";
2523
+ body.append(searchLeaf(source.label, "icen-ai-composer-popup-option-label"));
2524
+ if (source.description || source.sub) body.append(searchLeaf(source.description ?? source.sub ?? "", "icen-ai-composer-popup-option-sub"));
2525
+ opt.append(resourceIcon(source), body);
2526
+ return opt;
2527
+ }
2528
+ function commandOption(c) {
2529
+ const opt = document.createElement("button");
2530
+ opt.type = "button";
2531
+ opt.className = "icen-ai-composer-popup-option icen-ai-composer-popup-cmd";
2532
+ opt.setAttribute("role", "option");
2533
+ opt.setAttribute("aria-selected", "false");
2534
+ if (c.disabled) opt.setAttribute("aria-disabled", "true");
2535
+ opt.dataset.name = c.name;
2536
+ opt.dataset.category = "command";
2537
+ opt.dataset.hay = [c.name, c.label, c.description, c.group, ...c.keywords ?? []].join(" ").toLowerCase();
2538
+ const mark = document.createElement("span");
2539
+ mark.className = "icen-ai-composer-popup-option-icon";
2540
+ mark.append(createIcon(c.icon ?? "terminal") ?? createIcon("terminal"));
2541
+ const body = document.createElement("span");
2542
+ body.className = "icen-ai-composer-popup-option-copy";
2543
+ const line = document.createElement("span");
2544
+ line.className = "icen-ai-composer-popup-cmd-line";
2545
+ line.append(searchLeaf(c.label ?? `/${c.name}`, "icen-ai-composer-popup-option-label"));
2546
+ if (c.label) line.append(searchLeaf(`/${c.name}`, "icen-ai-composer-popup-cmd-name"));
2547
+ if (c.argsHint) line.append(searchLeaf(c.argsHint, "icen-ai-composer-popup-cmd-args"));
2548
+ body.append(line);
2549
+ if (c.description) body.append(searchLeaf(c.description, "icen-ai-composer-popup-option-sub"));
2550
+ opt.append(mark, body);
2551
+ return opt;
2552
+ }
2553
+ function popupFoot(s, text) {
2554
+ const foot = document.createElement("div");
2555
+ foot.className = "icen-ai-composer-popup-foot";
2556
+ foot.textContent = text;
2557
+ s.panel.append(foot);
2558
+ }
2559
+ function openComposerLibrary(el, category = "all") {
2560
+ if (!isBrowser()) return;
2561
+ const composer = resolveComposer(el);
2562
+ if (!composer) return;
2563
+ const ta = getTextarea(composer);
2564
+ if (!ta || ta.disabled || ta.readOnly) return;
2565
+ const anchor = composer.querySelector("[data-icen-ai-library]") ?? ta;
2566
+ const sources = REF_SOURCES.get(composer) ?? [], commands = COMMANDS.get(composer) ?? [];
2567
+ if (!sources.length && !commands.length) return;
2568
+ const session = mountPopup(composer, {
2569
+ mode: "library",
2570
+ anchor: composer,
2571
+ sizing: popupSizingFor(composer),
2572
+ returnFocus: anchor,
2573
+ pick: (opt) => {
2574
+ if (opt.dataset.name) {
2575
+ const draft = ta.value.replace(/^\/\S*\s?/, "");
2576
+ ta.value = `/${opt.dataset.name} ${draft}`;
2577
+ autosizeTa(ta);
2578
+ closePopup(composer, false);
2579
+ ta.focus();
2580
+ ta.setSelectionRange(ta.value.length, ta.value.length);
2581
+ return;
2582
+ }
2583
+ const source = sources.find((r) => r.id === opt.dataset.refId && r.kind === opt.dataset.refKind);
2584
+ if (!source) return;
2585
+ const active = (ACTIVE_REFS.get(composer) ?? []).some((r) => r.id === source.id && r.kind === source.kind);
2586
+ if (active) removeActiveRef(composer, source);
2587
+ else addActiveRef(composer, source);
2588
+ opt.setAttribute("aria-selected", String(!active));
2589
+ },
2590
+ build: (s) => {
2591
+ s.panel.setAttribute("role", "dialog");
2592
+ s.panel.setAttribute("aria-label", t("aiComposer.library"));
2593
+ const search = document.createElement("div");
2594
+ search.className = "icen-ai-composer-popup-search";
2595
+ const input = document.createElement("input");
2596
+ input.type = "search";
2597
+ input.className = "icen-ai-composer-popup-search-input";
2598
+ input.placeholder = t("aiComposer.searchLibrary");
2599
+ input.setAttribute("aria-label", t("aiComposer.searchLibrary"));
2600
+ search.append(input);
2601
+ s.searchInput = input;
2602
+ s.panel.append(search);
2603
+ const categories = [{ id: "all", label: t("aiComposer.all") }, ...knownRefKinds(sources).filter((k) => sources.some((r) => r.kind === k)).map((k) => ({ id: k, label: getRefKindDef(k).label })), ...commands.length ? [{ id: "command", label: t("aiComposer.commands") }] : []];
2604
+ s.category = categories.some((c) => c.id === category) ? category : "all";
2605
+ const filters = document.createElement("div");
2606
+ filters.className = "icen-ai-composer-popup-filters";
2607
+ filters.setAttribute("role", "group");
2608
+ filters.setAttribute("aria-label", t("aiComposer.refs"));
2609
+ for (const c of categories) {
2610
+ const b = document.createElement("button");
2611
+ b.type = "button";
2612
+ b.textContent = c.label;
2613
+ b.setAttribute("aria-pressed", String(c.id === s.category));
2614
+ b.addEventListener("click", () => {
2615
+ s.category = c.id;
2616
+ for (const node of filters.querySelectorAll("button")) node.setAttribute("aria-pressed", String(node === b));
2617
+ filterPopupOptions(s, input.value);
2618
+ });
2619
+ filters.append(b);
2620
+ }
2621
+ s.panel.append(filters);
2622
+ const list = document.createElement("div");
2623
+ list.className = "icen-ai-composer-popup-list";
2624
+ list.setAttribute("role", "listbox");
2625
+ list.setAttribute("aria-label", t("aiComposer.library"));
2626
+ list.setAttribute("aria-multiselectable", "true");
2627
+ const addGroup = (title, options) => {
2628
+ const g = document.createElement("div");
2629
+ g.className = "icen-ai-composer-popup-group";
2630
+ g.setAttribute("role", "group");
2631
+ g.setAttribute("aria-label", title);
2632
+ g.append(searchLeaf(title, "icen-ai-composer-popup-group-label"), ...options);
2633
+ list.append(g);
2634
+ s.groups.push(g);
2635
+ s.options.push(...options);
2636
+ };
2637
+ for (const kind of knownRefKinds(sources)) {
2638
+ const options = sources.filter((r) => r.kind === kind).map((r) => {
2639
+ const opt = resourceOption(r);
2640
+ opt.setAttribute("aria-selected", String((ACTIVE_REFS.get(composer) ?? []).some((a) => a.id === r.id && a.kind === r.kind)));
2641
+ return opt;
2642
+ });
2643
+ if (options.length) addGroup(getRefKindDef(kind).label, options);
2644
+ }
2645
+ for (const group of new Set(commands.map((c) => c.group ?? t("aiComposer.commands")))) addGroup(group, commands.filter((c) => (c.group ?? t("aiComposer.commands")) === group).map(commandOption));
2646
+ s.panel.append(list);
2647
+ appendPopupEmpty(s, t("aiComposer.noLibraryMatch"));
2648
+ popupFoot(s, t("aiComposer.libraryHint"));
2649
+ input.addEventListener("input", () => filterPopupOptions(s, input.value));
2650
+ input.addEventListener("keydown", (e) => popupKeydown(composer, s, e));
2651
+ }
2652
+ });
2653
+ anchor.setAttribute("aria-expanded", "true");
2654
+ filterPopupOptions(session, "");
2655
+ session.searchInput?.focus({ preventScroll: true });
1309
2656
  }
1310
2657
  function openModelPopup(composer) {
1311
2658
  const cfg = MODELS.get(composer);
1312
2659
  if (!cfg) return;
1313
- const btn = composer.querySelector("[data-ai-model-open]");
1314
- const anchor = btn ?? composer.querySelector(".ai-composer-box") ?? composer;
2660
+ const btn = composer.querySelector("[data-icen-ai-model-open]");
2661
+ const anchor = btn ?? composer.querySelector(".icen-ai-composer-box") ?? composer;
1315
2662
  const ta = getTextarea(composer);
1316
2663
  const session = mountPopup(composer, {
1317
2664
  mode: "model",
@@ -1321,7 +2668,7 @@ function openModelPopup(composer) {
1321
2668
  pick: (opt) => {
1322
2669
  const provider = opt.dataset.provider ?? "";
1323
2670
  const model = opt.dataset.model ?? "";
1324
- const label = opt.dataset.label ?? model;
2671
+ const label = opt.dataset.icenLabel ?? model;
1325
2672
  const context = typeof opt.dataset.context === "string" ? Number(opt.dataset.context) : void 0;
1326
2673
  MODELS.set(composer, { providers: cfg.providers, current: { provider, model } });
1327
2674
  if (typeof context === "number" && Number.isFinite(context) && context > 0) {
@@ -1336,25 +2683,25 @@ function openModelPopup(composer) {
1336
2683
  },
1337
2684
  build: (s) => {
1338
2685
  const search = document.createElement("div");
1339
- search.className = "ai-composer-popup-search";
2686
+ search.className = "icen-ai-composer-popup-search";
1340
2687
  const input = document.createElement("input");
1341
2688
  input.type = "search";
1342
- input.className = "ai-composer-popup-search-input";
1343
- input.placeholder = "\u641C\u7D22\u6A21\u578B";
1344
- input.setAttribute("aria-label", "\u641C\u7D22\u6A21\u578B");
2689
+ input.className = "icen-ai-composer-popup-search-input";
2690
+ input.placeholder = t("aiComposer.searchModels");
2691
+ input.setAttribute("aria-label", t("aiComposer.searchModels"));
1345
2692
  search.appendChild(input);
1346
2693
  s.panel.appendChild(search);
1347
2694
  s.searchInput = input;
1348
2695
  const list = document.createElement("div");
1349
- list.className = "ai-composer-popup-list";
2696
+ list.className = "icen-ai-composer-popup-list";
1350
2697
  list.setAttribute("role", "listbox");
1351
- list.setAttribute("aria-label", "\u9009\u62E9\u6A21\u578B");
2698
+ list.setAttribute("aria-label", t("aiComposer.selectModel"));
1352
2699
  for (const p of cfg.providers) {
1353
2700
  const group = document.createElement("div");
1354
- group.className = "ai-composer-popup-group";
2701
+ group.className = "icen-ai-composer-popup-group";
1355
2702
  const glabel = document.createElement("div");
1356
- glabel.className = "ai-composer-popup-group-label";
1357
- glabel.appendChild(providerMark(p, "ai-composer-popup-group-icon"));
2703
+ glabel.className = "icen-ai-composer-popup-group-label";
2704
+ glabel.appendChild(providerMark(p, "icen-ai-composer-popup-group-icon"));
1358
2705
  const gtext = document.createElement("span");
1359
2706
  gtext.textContent = p.label;
1360
2707
  glabel.appendChild(gtext);
@@ -1362,30 +2709,26 @@ function openModelPopup(composer) {
1362
2709
  for (const m of p.models) {
1363
2710
  const opt = document.createElement("button");
1364
2711
  opt.type = "button";
1365
- opt.className = "ai-composer-popup-option";
2712
+ opt.className = "icen-ai-composer-popup-option";
1366
2713
  opt.setAttribute("role", "option");
1367
2714
  opt.dataset.provider = p.id;
1368
2715
  opt.dataset.providerLabel = p.label;
1369
2716
  opt.dataset.model = m.id;
1370
- opt.dataset.label = m.label;
2717
+ opt.dataset.icenLabel = m.label;
1371
2718
  opt.dataset.hay = `${m.label} ${p.label}`.toLowerCase();
1372
2719
  if (typeof m.context === "number") opt.dataset.context = String(m.context);
1373
2720
  const selected = cfg.current.provider === p.id && cfg.current.model === m.id;
1374
2721
  opt.setAttribute("aria-selected", String(selected));
1375
2722
  const icon = document.createElement("span");
1376
- icon.className = "ai-composer-popup-option-icon";
2723
+ icon.className = "icen-ai-composer-popup-option-icon";
1377
2724
  icon.appendChild(providerMark(p, "ai-composer-popup-option-provider-icon"));
1378
2725
  const label = document.createElement("span");
1379
- label.className = "ai-composer-popup-option-label";
2726
+ label.className = "icen-ai-composer-popup-option-label";
1380
2727
  label.textContent = m.label;
1381
2728
  const meta = document.createElement("span");
1382
- meta.className = "ai-composer-popup-option-meta";
2729
+ meta.className = "icen-ai-composer-popup-option-meta";
1383
2730
  meta.textContent = typeof m.context === "number" ? formatTokens(m.context) : "";
1384
- const check = document.createElement("span");
1385
- check.className = "ai-composer-popup-option-check";
1386
- const checkSvg = svgIcon(ICON_CHECK);
1387
- if (checkSvg) check.appendChild(checkSvg);
1388
- opt.append(icon, label, meta, check);
2731
+ opt.append(icon, label, meta);
1389
2732
  group.appendChild(opt);
1390
2733
  s.options.push(opt);
1391
2734
  }
@@ -1393,9 +2736,9 @@ function openModelPopup(composer) {
1393
2736
  s.groups.push(group);
1394
2737
  }
1395
2738
  s.panel.appendChild(list);
1396
- appendPopupEmpty(s, "\u65E0\u5339\u914D\u6A21\u578B");
2739
+ appendPopupEmpty(s, t("aiComposer.noModelMatch"));
1397
2740
  const foot = document.createElement("div");
1398
- foot.className = "ai-composer-popup-foot";
2741
+ foot.className = "icen-ai-composer-popup-foot";
1399
2742
  const usageCfg = USAGE.get(composer);
1400
2743
  if (usageCfg) {
1401
2744
  const u = usageCfg.usage;
@@ -1407,7 +2750,7 @@ function openModelPopup(composer) {
1407
2750
  const info = svgIcon(ICON_INFO);
1408
2751
  if (info) foot.appendChild(info);
1409
2752
  const footText = document.createElement("span");
1410
- footText.textContent = "\u5207\u6362\u6A21\u578B\u4F1A\u4F7F prompt \u7F13\u5B58\u5931\u6548";
2753
+ footText.textContent = t("aiComposer.cacheInvalidHint");
1411
2754
  foot.appendChild(footText);
1412
2755
  s.panel.appendChild(foot);
1413
2756
  s.keydownHandler = (e) => popupKeydown(composer, s, e);
@@ -1420,10 +2763,21 @@ function openModelPopup(composer) {
1420
2763
  btn?.setAttribute("aria-expanded", "true");
1421
2764
  session.searchInput?.focus();
1422
2765
  }
2766
+ function runComposerCommand(composer, name) {
2767
+ const command = COMMANDS.get(composer)?.find((c) => c.name === name && !c.disabled);
2768
+ const ta = getTextarea(composer);
2769
+ if (!command || !ta || ta.disabled || ta.readOnly) return false;
2770
+ const args = /^\/\S*\s+([\s\S]*)$/.exec(ta.value)?.[1] ?? "";
2771
+ ta.value = "";
2772
+ autosizeTa(ta);
2773
+ emit2(composer, "icen:ai-command", { name: command.name, args });
2774
+ closePopup(composer);
2775
+ return true;
2776
+ }
1423
2777
  function openCommandPopup(composer, token) {
1424
2778
  const commands = COMMANDS.get(composer);
1425
2779
  if (!commands || commands.length === 0) return;
1426
- const box = composer.querySelector(".ai-composer-box") ?? composer;
2780
+ const box = composer.querySelector(".icen-ai-composer-box") ?? composer;
1427
2781
  const ta = getTextarea(composer);
1428
2782
  const session = mountPopup(composer, {
1429
2783
  mode: "command",
@@ -1431,59 +2785,27 @@ function openCommandPopup(composer, token) {
1431
2785
  sizing: popupSizingFor(box),
1432
2786
  returnFocus: ta,
1433
2787
  pick: (opt) => {
1434
- const name = opt.dataset.name ?? "";
1435
- const m = /^\/\S*\s+([\s\S]*)$/.exec(ta?.value ?? "");
1436
- const args = m ? m[1] : "";
1437
- if (ta) {
1438
- ta.value = "";
1439
- autosizeTa(ta);
1440
- }
1441
- emit2(composer, "icen:ai-command", { name, args });
1442
- closePopup(composer);
2788
+ runComposerCommand(composer, opt.dataset.name ?? "");
1443
2789
  },
1444
2790
  build: (s) => {
1445
2791
  const list = document.createElement("div");
1446
- list.className = "ai-composer-popup-list";
2792
+ list.className = "icen-ai-composer-popup-list";
1447
2793
  list.setAttribute("role", "listbox");
1448
- list.setAttribute("aria-label", "\u547D\u4EE4");
2794
+ list.setAttribute("aria-label", t("aiComposer.commands"));
1449
2795
  const group = document.createElement("div");
1450
- group.className = "ai-composer-popup-group";
2796
+ group.className = "icen-ai-composer-popup-group";
1451
2797
  for (const c of commands) {
1452
- const opt = document.createElement("button");
1453
- opt.type = "button";
1454
- opt.className = "ai-composer-popup-option ai-composer-popup-cmd";
1455
- opt.setAttribute("role", "option");
1456
- opt.dataset.name = c.name;
1457
- opt.dataset.hay = `${c.name} ${c.description ?? ""}`.toLowerCase();
1458
- const line = document.createElement("span");
1459
- line.className = "ai-composer-popup-cmd-line";
1460
- const nameEl = document.createElement("span");
1461
- nameEl.className = "ai-composer-popup-cmd-name";
1462
- nameEl.textContent = `/${c.name}`;
1463
- line.appendChild(nameEl);
1464
- if (c.argsHint) {
1465
- const argsEl = document.createElement("span");
1466
- argsEl.className = "ai-composer-popup-cmd-args";
1467
- argsEl.textContent = c.argsHint;
1468
- line.appendChild(argsEl);
1469
- }
1470
- opt.appendChild(line);
1471
- if (c.description) {
1472
- opt.appendChild(Object.assign(document.createElement("span"), {
1473
- className: "ai-composer-popup-cmd-desc",
1474
- textContent: c.description
1475
- }));
1476
- }
2798
+ const opt = commandOption(c);
1477
2799
  group.appendChild(opt);
1478
2800
  s.options.push(opt);
1479
2801
  }
1480
2802
  list.appendChild(group);
1481
2803
  s.groups.push(group);
1482
2804
  s.panel.appendChild(list);
1483
- appendPopupEmpty(s, "\u65E0\u5339\u914D\u547D\u4EE4");
2805
+ appendPopupEmpty(s, t("aiComposer.noCommandMatch"));
2806
+ popupFoot(s, t("aiComposer.commandHint"));
1484
2807
  }
1485
2808
  });
1486
- ta?.setAttribute("aria-expanded", "true");
1487
2809
  filterPopupOptions(session, token);
1488
2810
  }
1489
2811
  function commandToken(value) {
@@ -1502,7 +2824,7 @@ function updateCommandPopup(composer, s) {
1502
2824
  function openRefPopup(composer, range, filter) {
1503
2825
  const sources = REF_SOURCES.get(composer);
1504
2826
  if (!sources || sources.length === 0) return;
1505
- const box = composer.querySelector(".ai-composer-box") ?? composer;
2827
+ const box = composer.querySelector(".icen-ai-composer-box") ?? composer;
1506
2828
  const ta = getTextarea(composer);
1507
2829
  const session = mountPopup(composer, {
1508
2830
  mode: "ref",
@@ -1511,7 +2833,7 @@ function openRefPopup(composer, range, filter) {
1511
2833
  returnFocus: ta,
1512
2834
  refRange: range,
1513
2835
  pick: (opt) => {
1514
- const source = (REF_SOURCES.get(composer) ?? []).find((r2) => r2.id === opt.dataset.refId);
2836
+ const source = (REF_SOURCES.get(composer) ?? []).find((r2) => r2.id === opt.dataset.refId && r2.kind === opt.dataset.refKind);
1515
2837
  const cur = POPUP.get(composer);
1516
2838
  const r = cur?.refRange ?? (ta ? findRefToken(ta) : null);
1517
2839
  if (source && ta && r) {
@@ -1527,41 +2849,23 @@ function openRefPopup(composer, range, filter) {
1527
2849
  },
1528
2850
  build: (s) => {
1529
2851
  const list = document.createElement("div");
1530
- list.className = "ai-composer-popup-list";
2852
+ list.className = "icen-ai-composer-popup-list";
1531
2853
  list.setAttribute("role", "listbox");
1532
- list.setAttribute("aria-label", "\u5F15\u7528");
2854
+ list.setAttribute("aria-label", t("aiComposer.refs"));
1533
2855
  for (const kind of knownRefKinds(sources)) {
1534
2856
  const def = getRefKindDef(kind);
1535
2857
  const items = sources.filter((r) => r.kind === kind);
1536
2858
  if (items.length === 0) continue;
1537
2859
  const group = document.createElement("div");
1538
- group.className = "ai-composer-popup-group";
1539
- group.dataset.kind = kind;
2860
+ group.className = "icen-ai-composer-popup-group";
2861
+ group.dataset.icenKind = kind;
1540
2862
  group.appendChild(Object.assign(document.createElement("div"), {
1541
- className: "ai-composer-popup-group-label",
2863
+ className: "icen-ai-composer-popup-group-label",
1542
2864
  textContent: def.label
1543
2865
  }));
1544
2866
  for (const r of items) {
1545
- const opt = document.createElement("button");
1546
- opt.type = "button";
1547
- opt.className = "ai-composer-popup-option";
1548
- opt.setAttribute("role", "option");
1549
- opt.dataset.refId = r.id;
1550
- opt.dataset.hay = `${r.label} ${r.sub ?? ""}`.toLowerCase();
1551
- const icon = document.createElement("span");
1552
- icon.className = "ai-composer-popup-option-icon";
1553
- const svg2 = svgIcon(def.icon);
1554
- if (svg2) icon.appendChild(svg2);
1555
- const label = document.createElement("span");
1556
- label.className = "ai-composer-popup-option-label";
1557
- label.textContent = r.label;
1558
- if (r.sub) {
1559
- const sub = document.createElement("span");
1560
- sub.className = "ai-composer-popup-option-sub";
1561
- sub.textContent = r.sub;
1562
- label.appendChild(sub);
1563
- }
1564
- opt.append(icon, label);
2867
+ const opt = resourceOption(r);
2868
+ opt.setAttribute("aria-selected", String((ACTIVE_REFS.get(composer) ?? []).some((a) => a.id === r.id && a.kind === r.kind)));
1565
2869
  group.appendChild(opt);
1566
2870
  s.options.push(opt);
1567
2871
  }
@@ -1569,22 +2873,22 @@ function openRefPopup(composer, range, filter) {
1569
2873
  s.groups.push(group);
1570
2874
  }
1571
2875
  s.panel.appendChild(list);
1572
- appendPopupEmpty(s, "\u65E0\u5339\u914D\u5F15\u7528");
2876
+ appendPopupEmpty(s, t("aiComposer.noRefMatch"));
2877
+ popupFoot(s, t("aiComposer.refHint"));
1573
2878
  }
1574
2879
  });
1575
- ta?.setAttribute("aria-expanded", "true");
1576
2880
  filterPopupOptions(session, filter);
1577
2881
  }
1578
2882
  function updateRefPopup(composer, s) {
1579
2883
  const ta = getTextarea(composer);
1580
- const t = ta ? findRefToken(ta) : null;
1581
- if (!t) {
2884
+ const t2 = ta ? findRefToken(ta) : null;
2885
+ if (!t2) {
1582
2886
  closePopup(composer, false);
1583
2887
  maybeOpenTriggerPopups(composer);
1584
2888
  return;
1585
2889
  }
1586
- s.refRange = { start: t.start, end: t.end };
1587
- filterPopupOptions(s, t.filter);
2890
+ s.refRange = { start: t2.start, end: t2.end };
2891
+ filterPopupOptions(s, t2.filter);
1588
2892
  }
1589
2893
  function findRefToken(ta) {
1590
2894
  const value = ta.value;
@@ -1606,30 +2910,34 @@ function maybeOpenTriggerPopups(composer) {
1606
2910
  }
1607
2911
  const sources = REF_SOURCES.get(composer);
1608
2912
  if (sources && sources.length > 0) {
1609
- const t = findRefToken(ta);
1610
- if (t) openRefPopup(composer, { start: t.start, end: t.end }, t.filter);
2913
+ const t2 = findRefToken(ta);
2914
+ if (t2) openRefPopup(composer, { start: t2.start, end: t2.end }, t2.filter);
1611
2915
  }
1612
2916
  }
1613
2917
  function ensureRefsRow(composer) {
1614
- const existing = composer.querySelector(".ai-composer-refs");
2918
+ const existing = composer.querySelector(".icen-ai-composer-refs");
1615
2919
  if (existing) return existing;
1616
2920
  const row = document.createElement("div");
1617
- row.className = "ai-composer-refs";
2921
+ row.className = "icen-ai-composer-refs";
1618
2922
  row.dataset.aiRefs = "auto";
1619
2923
  row.hidden = true;
1620
- const attach = composer.querySelector(".ai-composer-attach");
1621
- const box = composer.querySelector(".ai-composer-box");
2924
+ const attach = composer.querySelector(".icen-ai-composer-attach");
2925
+ const box = composer.querySelector(".icen-ai-composer-box");
1622
2926
  composer.insertBefore(row, attach ?? box ?? null);
1623
2927
  return row;
1624
2928
  }
1625
2929
  function renderRefs(composer) {
1626
- const row = composer.querySelector(".ai-composer-refs");
2930
+ const row = composer.querySelector(".icen-ai-composer-refs");
1627
2931
  const actives = ACTIVE_REFS.get(composer) ?? [];
1628
2932
  if (!row) return;
1629
2933
  row.replaceChildren(
1630
2934
  ...actives.map((src) => {
1631
- const chip = makeChip(src.label, true, `\u79FB\u9664\u5F15\u7528 ${src.label}`);
2935
+ const chip = makeChip(src.label, true, t("aiComposer.removeRef", { p0: src.label }));
2936
+ chip.prepend(resourceIcon(src));
2937
+ chip.dataset.icenKind = src.kind;
1632
2938
  chip.dataset.refId = src.id;
2939
+ chip.dataset.refKind = src.kind;
2940
+ chip.title = [src.label, src.description ?? src.sub].filter(Boolean).join(" \xB7 ");
1633
2941
  return chip;
1634
2942
  })
1635
2943
  );
@@ -1637,7 +2945,7 @@ function renderRefs(composer) {
1637
2945
  }
1638
2946
  function addActiveRef(composer, source) {
1639
2947
  const actives = ACTIVE_REFS.get(composer) ?? [];
1640
- if (actives.some((r) => r.id === source.id)) return;
2948
+ if (actives.some((r) => r.id === source.id && r.kind === source.kind)) return;
1641
2949
  actives.push(source);
1642
2950
  ACTIVE_REFS.set(composer, actives);
1643
2951
  renderRefs(composer);
@@ -1645,24 +2953,62 @@ function addActiveRef(composer, source) {
1645
2953
  }
1646
2954
  function removeActiveRef(composer, source) {
1647
2955
  const actives = ACTIVE_REFS.get(composer) ?? [];
1648
- const idx = actives.findIndex((r) => r.id === source.id);
2956
+ const idx = actives.findIndex((r) => r.id === source.id && r.kind === source.kind);
1649
2957
  if (idx === -1) return;
1650
2958
  actives.splice(idx, 1);
1651
2959
  ACTIVE_REFS.set(composer, actives);
2960
+ const activeButton = document.activeElement;
2961
+ const restore = activeButton instanceof Element && !!activeButton.closest(".icen-ai-composer-refs");
1652
2962
  renderRefs(composer);
2963
+ if (restore) {
2964
+ const buttons = composer.querySelectorAll(".icen-ai-composer-refs .icen-ai-composer-chip-remove");
2965
+ (buttons[Math.min(idx, buttons.length - 1)] ?? getTextarea(composer))?.focus({ preventScroll: true });
2966
+ }
1653
2967
  const ta = getTextarea(composer);
1654
- if (ta) {
2968
+ if (ta && !actives.some((r) => r.label === source.label)) {
1655
2969
  const needle = `@${source.label}`;
1656
- const at = ta.value.indexOf(needle);
2970
+ let at = ta.value.indexOf(needle);
2971
+ while (at !== -1 && (at > 0 && !/\s/.test(ta.value[at - 1]) || ta.value[at + needle.length] !== void 0 && !/\s/.test(ta.value[at + needle.length]))) at = ta.value.indexOf(needle, at + needle.length);
1657
2972
  if (at !== -1) {
2973
+ const start = ta.selectionStart, end = ta.selectionEnd, direction = ta.selectionDirection;
1658
2974
  let endIdx = at + needle.length;
1659
2975
  if (ta.value[endIdx] === " ") endIdx += 1;
1660
2976
  ta.value = ta.value.slice(0, at) + ta.value.slice(endIdx);
2977
+ const shifted = (pos) => pos <= at ? pos : pos < endIdx ? at : pos - (endIdx - at);
2978
+ ta.setSelectionRange(shifted(start), shifted(end), direction);
1661
2979
  autosizeTa(ta);
1662
2980
  }
1663
2981
  }
1664
2982
  emit2(composer, "icen:ai-ref", { action: "remove", ref: source });
1665
2983
  }
2984
+ function renderFiles(composer) {
2985
+ let row = composer.querySelector(".icen-ai-composer-attach");
2986
+ const files = FILES.get(composer) ?? [];
2987
+ if (!row && files.length) {
2988
+ row = document.createElement("div");
2989
+ row.className = "icen-ai-composer-attach";
2990
+ composer.insertBefore(row, composer.querySelector(".icen-ai-composer-box"));
2991
+ }
2992
+ if (!row) return;
2993
+ row.replaceChildren(...files.map((file, index) => {
2994
+ const chip = makeChip(file.name, true, t("aiComposer.removeAttachment", { p0: file.name }), "file");
2995
+ chip.dataset.icenFile = String(index);
2996
+ const size = document.createElement("span");
2997
+ size.className = "icen-ai-composer-chip-meta";
2998
+ size.textContent = file.size < 1024 ? `${file.size} B` : `${Math.ceil(file.size / 1024)} KB`;
2999
+ chip.insertBefore(size, chip.lastElementChild);
3000
+ chip.title = file.name;
3001
+ return chip;
3002
+ }));
3003
+ row.hidden = !files.length;
3004
+ }
3005
+ function getComposerDraft(el) {
3006
+ if (!isBrowser()) return { text: "" };
3007
+ const c = resolveComposer(el);
3008
+ if (!c) return { text: "" };
3009
+ const refs = ACTIVE_REFS.get(c) ?? [], files = FILES.get(c) ?? [];
3010
+ return { text: getTextarea(c)?.value ?? "", ...refs.length ? { refs: refs.map((r) => ({ ...r })) } : {}, ...files.length ? { files: [...files] } : {} };
3011
+ }
1666
3012
  function pushHistory(composer, text) {
1667
3013
  const h2 = HISTORIES.get(composer) ?? { items: [], index: -1 };
1668
3014
  if (h2.items[h2.items.length - 1] !== text) {
@@ -1681,31 +3027,35 @@ function setupComposer(composer) {
1681
3027
  el.__icenAiComposerInit = false;
1682
3028
  return void 0;
1683
3029
  }
1684
- const queueEl = composer.querySelector(".ai-composer-queue");
1685
- const attachEl = composer.querySelector(".ai-composer-attach");
3030
+ const queueEl = composer.querySelector(".icen-ai-composer-queue");
1686
3031
  const { sendBtn, sendIcon, stopIcon } = ensureIcons(composer);
1687
3032
  if (!ta.hasAttribute("placeholder")) {
1688
- ta.setAttribute("placeholder", "\u8F93\u5165\u6D88\u606F\uFF0C/ \u547D\u4EE4 \xB7 @ \u5F15\u7528 \xB7 Enter \u53D1\u9001");
3033
+ ta.setAttribute("placeholder", t("aiComposer.placeholder"));
1689
3034
  }
1690
3035
  const renderQueue = () => renderQueueChips(composer);
1691
- const enqueue = (text) => {
3036
+ const enqueue = (draft) => {
1692
3037
  const queue = QUEUES.get(composer) ?? [];
1693
- queue.push(text);
3038
+ queue.push({ ...draft, ...draft.refs ? { refs: draft.refs.map((r) => ({ ...r })) } : {}, ...draft.files ? { files: [...draft.files] } : {} });
1694
3039
  QUEUES.set(composer, queue);
1695
3040
  renderQueue();
1696
3041
  };
1697
3042
  const isRunning = () => RUNNING.get(composer) ?? false;
1698
3043
  const submit = () => {
1699
- const text = ta.value;
1700
- if (!text.trim()) return;
3044
+ if (ta.disabled || ta.readOnly || composing) return;
3045
+ const draft = getComposerDraft(composer);
3046
+ if (!draft.text.trim() && !draft.files?.length && !draft.refs?.length) return;
1701
3047
  ta.value = "";
1702
3048
  autosizeTa(ta);
3049
+ ACTIVE_REFS.set(composer, []);
3050
+ FILES.set(composer, []);
3051
+ renderRefs(composer);
3052
+ renderFiles(composer);
1703
3053
  if (isRunning()) {
1704
- enqueue(text);
1705
- emit2(composer, "icen:ai-queue", { text });
3054
+ enqueue(draft);
3055
+ emit2(composer, "icen:ai-queue", draft);
1706
3056
  } else {
1707
- pushHistory(composer, text);
1708
- emit2(composer, "icen:ai-send", { text });
3057
+ pushHistory(composer, draft.text);
3058
+ emit2(composer, "icen:ai-send", draft);
1709
3059
  }
1710
3060
  };
1711
3061
  SUBMITS.set(composer, submit);
@@ -1713,10 +3063,14 @@ function setupComposer(composer) {
1713
3063
  const queue = QUEUES.get(composer) ?? [];
1714
3064
  if (queue.length === 0) return;
1715
3065
  const index = queue.length - 1;
1716
- const text = queue.pop() ?? "";
3066
+ const draft = queue.pop();
1717
3067
  QUEUES.set(composer, queue);
1718
3068
  renderQueue();
1719
- ta.value = text;
3069
+ ta.value = draft.text;
3070
+ for (const ref of draft.refs ?? []) addActiveRef(composer, ref);
3071
+ const files = FILES.get(composer) ?? [];
3072
+ FILES.set(composer, Array.from(/* @__PURE__ */ new Set([...files, ...draft.files ?? []])));
3073
+ renderFiles(composer);
1720
3074
  autosizeTa(ta);
1721
3075
  emit2(composer, "icen:ai-dequeue", { index });
1722
3076
  };
@@ -1738,7 +3092,7 @@ function setupComposer(composer) {
1738
3092
  autosizeTa(ta);
1739
3093
  };
1740
3094
  const onKeydown = (e) => {
1741
- if (composing || e.isComposing) return;
3095
+ if (composing || e.isComposing || ta.disabled || ta.readOnly) return;
1742
3096
  const session = POPUP.get(composer);
1743
3097
  if (session) {
1744
3098
  popupKeydown(composer, session, e);
@@ -1746,6 +3100,7 @@ function setupComposer(composer) {
1746
3100
  }
1747
3101
  if (e.key === "Enter" && !e.shiftKey) {
1748
3102
  e.preventDefault();
3103
+ if (ta.value.startsWith("/") && runComposerCommand(composer, commandToken(ta.value))) return;
1749
3104
  submit();
1750
3105
  return;
1751
3106
  }
@@ -1753,7 +3108,7 @@ function setupComposer(composer) {
1753
3108
  const h2 = HISTORIES.get(composer);
1754
3109
  const browsing = (h2?.index ?? -1) >= 0;
1755
3110
  if (ta.value === "") {
1756
- if (isRunning() && (QUEUES.get(composer) ?? []).length > 0) {
3111
+ if ((QUEUES.get(composer) ?? []).length > 0) {
1757
3112
  e.preventDefault();
1758
3113
  takeBackLastQueued();
1759
3114
  return;
@@ -1778,6 +3133,7 @@ function setupComposer(composer) {
1778
3133
  };
1779
3134
  const onInput = () => {
1780
3135
  autosizeTa(ta);
3136
+ if (composing) return;
1781
3137
  const h2 = HISTORIES.get(composer);
1782
3138
  if (h2 && h2.index >= 0 && ta.value !== h2.items[h2.index]) h2.index = -1;
1783
3139
  const session = POPUP.get(composer);
@@ -1791,9 +3147,11 @@ function setupComposer(composer) {
1791
3147
  let composing = false;
1792
3148
  const onCompositionStart = () => {
1793
3149
  composing = true;
3150
+ closePopup(composer, false);
1794
3151
  };
1795
3152
  const onCompositionEnd = () => {
1796
3153
  composing = false;
3154
+ onInput();
1797
3155
  };
1798
3156
  const onSendClick = () => {
1799
3157
  if (isRunning()) {
@@ -1803,22 +3161,25 @@ function setupComposer(composer) {
1803
3161
  }
1804
3162
  };
1805
3163
  const onQueueClick = (e) => {
1806
- const btn = e.target instanceof Element ? e.target.closest(".ai-composer-chip-remove") : null;
3164
+ const btn = e.target instanceof Element ? e.target.closest(".icen-ai-composer-chip-remove") : null;
1807
3165
  if (!btn) return;
1808
- const index = Array.from(queueEl?.querySelectorAll(".ai-composer-chip-remove") ?? []).indexOf(btn);
3166
+ const index = Array.from(queueEl?.querySelectorAll(".icen-ai-composer-chip-remove") ?? []).indexOf(btn);
1809
3167
  const queue = QUEUES.get(composer) ?? [];
1810
3168
  if (index >= 0 && index < queue.length) {
1811
3169
  queue.splice(index, 1);
1812
3170
  QUEUES.set(composer, queue);
1813
3171
  renderQueue();
3172
+ const buttons = queueEl?.querySelectorAll(".icen-ai-composer-chip-remove");
3173
+ (buttons?.[Math.min(index, buttons.length - 1)] ?? ta).focus({ preventScroll: true });
1814
3174
  emit2(composer, "icen:ai-dequeue", { index });
1815
3175
  }
1816
3176
  };
1817
3177
  const onRefsClick = (e) => {
1818
- const btn = e.target instanceof Element ? e.target.closest(".ai-composer-chip-remove") : null;
1819
- if (!btn || !btn.closest(".ai-composer-refs")) return;
1820
- const id = btn.closest(".ai-composer-chip")?.dataset.refId;
1821
- const source = (ACTIVE_REFS.get(composer) ?? []).find((r) => r.id === id);
3178
+ const btn = e.target instanceof Element ? e.target.closest(".icen-ai-composer-chip-remove") : null;
3179
+ if (!btn || !btn.closest(".icen-ai-composer-refs")) return;
3180
+ const id = btn.closest(".icen-ai-composer-chip")?.dataset.refId;
3181
+ const kind = btn.closest(".icen-ai-composer-chip")?.dataset.refKind;
3182
+ const source = (ACTIVE_REFS.get(composer) ?? []).find((r) => r.id === id && r.kind === kind);
1822
3183
  if (source) removeActiveRef(composer, source);
1823
3184
  };
1824
3185
  const fileInput = document.createElement("input");
@@ -1827,20 +3188,32 @@ function setupComposer(composer) {
1827
3188
  fileInput.multiple = true;
1828
3189
  composer.appendChild(fileInput);
1829
3190
  const onAttachClick = () => {
1830
- fileInput.click();
3191
+ if (!ta.disabled && !ta.readOnly) fileInput.click();
1831
3192
  };
1832
3193
  const addFiles = (files) => {
1833
- if (files.length === 0) return;
1834
- if (attachEl) {
1835
- for (const f of files) {
1836
- const chip = makeChip(f.name, true, `\u79FB\u9664\u9644\u4EF6 ${f.name}`);
1837
- const remove = chip.querySelector(".ai-composer-chip-remove");
1838
- remove?.addEventListener("click", () => chip.remove(), { once: true });
1839
- attachEl.appendChild(chip);
1840
- }
1841
- attachEl.hidden = false;
3194
+ if (!files.length || ta.disabled || ta.readOnly) return;
3195
+ const current2 = FILES.get(composer) ?? [];
3196
+ const accepted = [];
3197
+ for (const file of files) {
3198
+ if (![...current2, ...accepted].some((c) => c.name === file.name && c.size === file.size && c.lastModified === file.lastModified && c.type === file.type)) accepted.push(file);
1842
3199
  }
1843
- emit2(composer, "icen:ai-attach", { files });
3200
+ if (!accepted.length) return;
3201
+ FILES.set(composer, [...current2, ...accepted]);
3202
+ renderFiles(composer);
3203
+ emit2(composer, "icen:ai-attach", { files: accepted });
3204
+ };
3205
+ const onRemoveFile = (e) => {
3206
+ const button = e.target instanceof Element ? e.target.closest(".icen-ai-composer-attach .icen-ai-composer-chip-remove") : null;
3207
+ if (!button) return;
3208
+ const index = Number(button.closest("[data-icen-file]")?.dataset.icenFile);
3209
+ const files = FILES.get(composer) ?? [];
3210
+ if (!Number.isInteger(index) || index < 0 || index >= files.length) return;
3211
+ const [file] = files.splice(index, 1);
3212
+ FILES.set(composer, files);
3213
+ renderFiles(composer);
3214
+ const buttons = composer.querySelectorAll(".icen-ai-composer-attach .icen-ai-composer-chip-remove");
3215
+ (buttons[Math.min(index, buttons.length - 1)] ?? ta).focus({ preventScroll: true });
3216
+ emit2(composer, "icen:ai-attachment-remove", { file, files: [...files] });
1844
3217
  };
1845
3218
  const onFileChange = () => {
1846
3219
  const files = Array.from(fileInput.files ?? []);
@@ -1853,7 +3226,7 @@ function setupComposer(composer) {
1853
3226
  e.preventDefault();
1854
3227
  addFiles(files);
1855
3228
  };
1856
- const boxEl = composer.querySelector(".ai-composer-box");
3229
+ const boxEl = composer.querySelector(".icen-ai-composer-box");
1857
3230
  const onDragOver = (e) => {
1858
3231
  if (!e.dataTransfer?.types.includes("Files")) return;
1859
3232
  e.preventDefault();
@@ -1880,7 +3253,8 @@ function setupComposer(composer) {
1880
3253
  sendBtn?.addEventListener("click", onSendClick);
1881
3254
  queueEl?.addEventListener("click", onQueueClick);
1882
3255
  composer.addEventListener("click", onRefsClick);
1883
- composer.querySelector("[data-ai-attach]")?.addEventListener("click", onAttachClick);
3256
+ composer.addEventListener("click", onRemoveFile);
3257
+ composer.querySelector("[data-icen-ai-attach]")?.addEventListener("click", onAttachClick);
1884
3258
  fileInput.addEventListener("change", onFileChange);
1885
3259
  const initialRunning = RUNNING.get(composer) ?? composer.classList.contains("is-running");
1886
3260
  if (initialRunning) setComposerRunning(composer, true);
@@ -1891,6 +3265,7 @@ function setupComposer(composer) {
1891
3265
  autosizeTa(ta);
1892
3266
  renderQueue();
1893
3267
  renderRefs(composer);
3268
+ renderFiles(composer);
1894
3269
  return () => {
1895
3270
  closePopup(composer, false);
1896
3271
  ta.removeEventListener("keydown", onKeydown);
@@ -1904,7 +3279,8 @@ function setupComposer(composer) {
1904
3279
  sendBtn?.removeEventListener("click", onSendClick);
1905
3280
  queueEl?.removeEventListener("click", onQueueClick);
1906
3281
  composer.removeEventListener("click", onRefsClick);
1907
- composer.querySelector("[data-ai-attach]")?.removeEventListener("click", onAttachClick);
3282
+ composer.removeEventListener("click", onRemoveFile);
3283
+ composer.querySelector("[data-icen-ai-attach]")?.removeEventListener("click", onAttachClick);
1908
3284
  fileInput.removeEventListener("change", onFileChange);
1909
3285
  fileInput.remove();
1910
3286
  el.__icenAiComposerInit = false;
@@ -1915,10 +3291,10 @@ function initAiComposer(root) {
1915
3291
  if (isBrowser()) {
1916
3292
  const scope = root ?? document;
1917
3293
  const candidates = [];
1918
- if (scope instanceof Element && scope.matches("[data-ai-composer]")) {
3294
+ if (scope instanceof Element && scope.matches("[data-icen-ai-composer]")) {
1919
3295
  candidates.push(scope);
1920
3296
  }
1921
- candidates.push(...Array.from(scope.querySelectorAll("[data-ai-composer]")));
3297
+ candidates.push(...Array.from(scope.querySelectorAll("[data-icen-ai-composer]")));
1922
3298
  for (const composer of candidates) {
1923
3299
  const cleanup = setupComposer(composer);
1924
3300
  if (cleanup) cleanups.push(cleanup);
@@ -1929,7 +3305,7 @@ function initAiComposer(root) {
1929
3305
  cleanups.length = 0;
1930
3306
  };
1931
3307
  }
1932
- function setComposerModels(el, providers, current) {
3308
+ function setComposerModels(el, providers, current2) {
1933
3309
  if (!isBrowser()) return;
1934
3310
  const composer = resolveComposer(el);
1935
3311
  if (!composer) return;
@@ -1939,13 +3315,13 @@ function setComposerModels(el, providers, current) {
1939
3315
  if (list.length === 0) {
1940
3316
  MODELS.delete(composer);
1941
3317
  MODEL_CTX.delete(composer);
1942
- const btn = composer.querySelector("[data-ai-model-open]");
1943
- if (btn?.dataset.aiModelOpen === "auto") btn.remove();
3318
+ const btn = composer.querySelector("[data-icen-ai-model-open]");
3319
+ if (btn?.dataset.icenAiModelOpen === "auto") btn.remove();
1944
3320
  syncToolbar(composer);
1945
3321
  refreshUsageRing(composer);
1946
3322
  return;
1947
3323
  }
1948
- const cur = current ?? { provider: list[0].id, model: list[0].models[0].id };
3324
+ const cur = current2 ?? { provider: list[0].id, model: list[0].models[0].id };
1949
3325
  MODELS.set(composer, { providers: list, current: cur });
1950
3326
  const provider = list.find((p) => p.id === cur.provider) ?? list[0];
1951
3327
  const model = provider.models.find((m) => m.id === cur.model) ?? provider.models[0];
@@ -1966,10 +3342,13 @@ function setComposerCommands(el, commands) {
1966
3342
  if (list.length === 0) {
1967
3343
  COMMANDS.delete(composer);
1968
3344
  const session = POPUP.get(composer);
1969
- if (session?.mode === "command") closePopup(composer);
3345
+ if (session?.mode === "command" || session?.mode === "library") closePopup(composer);
3346
+ syncToolbar(composer);
1970
3347
  return;
1971
3348
  }
1972
3349
  COMMANDS.set(composer, list);
3350
+ syncToolbar(composer);
3351
+ if (POPUP.get(composer)) closePopup(composer);
1973
3352
  }
1974
3353
  function setComposerRefs(el, sources) {
1975
3354
  if (!isBrowser()) return;
@@ -1977,6 +3356,8 @@ function setComposerRefs(el, sources) {
1977
3356
  if (!composer) return;
1978
3357
  const list = (Array.isArray(sources) ? sources : []).filter((s) => s && typeof s.label === "string" && s.label);
1979
3358
  REF_SOURCES.set(composer, list);
3359
+ syncToolbar(composer);
3360
+ if (POPUP.get(composer)) closePopup(composer);
1980
3361
  if (list.length === 0) {
1981
3362
  const session = POPUP.get(composer);
1982
3363
  if (session?.mode === "ref") closePopup(composer);
@@ -1990,8 +3371,8 @@ function setComposerUsage(el, usage, opts = {}) {
1990
3371
  if (!composer) return;
1991
3372
  if (!usage) {
1992
3373
  USAGE.delete(composer);
1993
- const mount = composer.querySelector(".ai-composer-usage");
1994
- if (mount?.dataset.aiUsage === "auto") mount.remove();
3374
+ const mount = composer.querySelector(".icen-ai-composer-usage");
3375
+ if (mount?.dataset.icenAiUsage === "auto") mount.remove();
1995
3376
  syncToolbar(composer);
1996
3377
  return;
1997
3378
  }
@@ -2003,7 +3384,7 @@ function setComposerTodo(el, items) {
2003
3384
  if (!isBrowser()) return;
2004
3385
  const composer = resolveComposer(el);
2005
3386
  if (!composer) return;
2006
- let chip = composer.querySelector(".ai-composer-todo");
3387
+ let chip = composer.querySelector(".icen-ai-composer-todo");
2007
3388
  if (!items || items.length === 0) {
2008
3389
  TODO_STATES.delete(composer);
2009
3390
  chip?.remove();
@@ -2013,17 +3394,17 @@ function setComposerTodo(el, items) {
2013
3394
  if (!chip) {
2014
3395
  const btn = document.createElement("button");
2015
3396
  btn.type = "button";
2016
- btn.className = "ai-composer-todo";
3397
+ btn.className = "icen-ai-composer-todo";
2017
3398
  btn.setAttribute("aria-haspopup", "true");
2018
3399
  btn.addEventListener("click", () => {
2019
3400
  const cur = TODO_STATES.get(composer);
2020
3401
  if (!cur) return;
2021
3402
  const p = document.createElement("div");
2022
- p.className = "popover ai-composer-todo-pop";
3403
+ p.className = "icen-popover icen-ai-composer-todo-pop";
2023
3404
  p.hidden = true;
2024
3405
  const title = document.createElement("div");
2025
- title.className = "ai-usage-popover-title";
2026
- title.textContent = "\u5F53\u524D\u5F85\u529E";
3406
+ title.className = "icen-ai-usage-popover-title";
3407
+ title.textContent = t("aiComposer.currentTodos");
2027
3408
  const body = document.createElement("div");
2028
3409
  body.className = "ai-usage-popover-body";
2029
3410
  renderAiTodo(body, cur);
@@ -2041,39 +3422,42 @@ function setComposerTodo(el, items) {
2041
3422
  );
2042
3423
  if (icon) chip.appendChild(icon);
2043
3424
  chip.appendChild(document.createElement("span")).className = "ai-composer-todo-label";
2044
- const toolbar = composer.querySelector(".ai-composer-toolbar");
2045
- const usage = composer.querySelector(".ai-composer-usage");
3425
+ const toolbar = composer.querySelector(".icen-ai-composer-toolbar");
3426
+ const usage = composer.querySelector(".icen-ai-composer-usage");
2046
3427
  if (toolbar) toolbar.insertBefore(chip, usage ?? null);
2047
3428
  else {
2048
- const box = composer.querySelector(".ai-composer-box");
3429
+ const box = composer.querySelector(".icen-ai-composer-box");
2049
3430
  box?.before(chip);
2050
3431
  }
2051
3432
  }
2052
- const done = items.filter((t) => t.status === "done").length;
2053
- const failed = items.some((t) => t.status === "error" || t.status === "cancelled");
3433
+ const done = items.filter((t2) => t2.status === "done").length;
3434
+ const failed = items.some((t2) => t2.status === "error" || t2.status === "cancelled");
2054
3435
  chip.classList.toggle("is-done", done === items.length && !failed);
2055
3436
  chip.classList.toggle("is-error", failed);
2056
- chip.setAttribute("aria-label", `\u5F53\u524D\u5F85\u529E ${done}/${items.length}${failed ? "\uFF08\u6709\u5931\u8D25\u9879\uFF09" : ""}`);
3437
+ chip.setAttribute("aria-label", t("aiComposer.todoChip", { p0: done, p1: items.length, p2: failed ? t("aiComposer.todoHasError") : "" }));
2057
3438
  const label = chip.querySelector(".ai-composer-todo-label");
2058
3439
  if (label) label.textContent = `${done}/${items.length}`;
2059
3440
  }
2060
3441
  var BOUND = /* @__PURE__ */ new WeakSet();
2061
3442
  function bindComposer(el, opts = {}) {
2062
3443
  const noop = () => void 0;
2063
- if (!isBrowser()) return { unbind: noop };
3444
+ if (!isBrowser()) return { unbind: noop, retry: () => false };
2064
3445
  const composer = resolveComposer(el);
2065
- if (!composer) return { unbind: noop };
2066
- if (BOUND.has(composer)) return { unbind: noop };
3446
+ if (!composer) return { unbind: noop, retry: () => false };
3447
+ if (BOUND.has(composer)) return { unbind: noop, retry: () => false };
2067
3448
  BOUND.add(composer);
2068
3449
  if (opts.popoverSizing) POPUP_SIZING.set(composer, opts.popoverSizing);
2069
3450
  const disposers = [];
2070
3451
  const usageCfg = opts.usage;
2071
3452
  const manageRunning = opts.running ?? true;
3453
+ let bound = true;
3454
+ let retry = () => false;
3455
+ let updateUsage = (_detail) => {
3456
+ };
2072
3457
  if (usageCfg) {
2073
3458
  let cost = 0;
2074
3459
  let billing = {};
2075
- const onDone = (e) => {
2076
- const detail = e.detail;
3460
+ updateUsage = (detail) => {
2077
3461
  if (!detail?.usage) return;
2078
3462
  if (typeof detail.cost === "number") cost += detail.cost;
2079
3463
  let usage;
@@ -2098,8 +3482,12 @@ function bindComposer(el, opts = {}) {
2098
3482
  cost: cost > 0 ? Math.round(cost * 1e6) / 1e6 : void 0
2099
3483
  });
2100
3484
  };
2101
- document.addEventListener("icen:ai-done", onDone);
2102
- disposers.push(() => document.removeEventListener("icen:ai-done", onDone));
3485
+ if (!opts.client || !opts.messages) {
3486
+ const onDone = (e) => updateUsage(e.detail);
3487
+ const target = opts.eventTarget ?? document;
3488
+ target.addEventListener("icen:ai-done", onDone);
3489
+ disposers.push(() => target.removeEventListener("icen:ai-done", onDone));
3490
+ }
2103
3491
  }
2104
3492
  if (opts.client && !opts.messages) {
2105
3493
  console.warn("[ai-composer] client \u6A21\u5F0F\u9700\u8981 opts.messages\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u7EAF\u72B6\u6001\u7ED1\u5B9A");
@@ -2108,81 +3496,164 @@ function bindComposer(el, opts = {}) {
2108
3496
  const client = opts.client;
2109
3497
  const messages = opts.messages;
2110
3498
  const history = [];
2111
- let session = null;
3499
+ let active = null;
3500
+ let retryable = null;
2112
3501
  const metaOf = (r) => {
2113
3502
  const tok = r.usage.total ?? 0;
2114
- const parts = [tok > 0 ? `${formatTokens(tok)} tok` : "\u5B8C\u6210"];
3503
+ const parts = [tok > 0 ? `${formatTokens(tok)} tok` : t("aiComposer.done")];
2115
3504
  if (typeof r.cost === "number") parts.push(`$${r.cost.toFixed(4)}`);
2116
- return `\u521A\u521A \xB7 ${parts.join(" \xB7 ")}`;
3505
+ return t("aiComposer.justNow", { p0: parts.join(" \xB7 ") });
2117
3506
  };
2118
- const startTurn = () => {
2119
- const assistant = renderAiMessage(messages, {
3507
+ const startTurn = (previous, draft) => {
3508
+ const assistant = previous?.assistant ?? renderAiMessage(messages, {
2120
3509
  role: "assistant",
2121
3510
  content: "",
2122
3511
  streaming: true,
2123
3512
  model: client.config.model
2124
3513
  });
3514
+ if (previous) {
3515
+ const restoreRetryFocus = assistant.body.contains(document.activeElement);
3516
+ previous.textNode?.remove();
3517
+ assistant.body.querySelectorAll(".icen-ai-msg-continue,.icen-ai-msg-error-text").forEach((node) => node.remove());
3518
+ assistant.el.classList.remove("icen-ai-msg--error");
3519
+ assistant.setMeta("");
3520
+ if (restoreRetryFocus) assistant.el.querySelector('[data-icen-ai-msg-action="retry"]')?.focus({ preventScroll: true });
3521
+ }
2125
3522
  const streamH = assistant.stream();
3523
+ assistant.el.classList.add("is-streaming");
3524
+ const turn = { assistant, stream: streamH, textNode: assistant.body.lastElementChild, request: [...history], session: null, text: "", draft: previous?.draft ?? draft, preparation: new AbortController() };
3525
+ active = turn;
3526
+ retryable = null;
2126
3527
  setComposerRunning(composer, true);
2127
- const s = client.stream({ messages: [...history] });
2128
- session = s;
2129
3528
  let failed = false;
2130
3529
  void (async () => {
2131
- for await (const chunk of s) {
2132
- if (chunk.type === "text") streamH.append(chunk.delta);
2133
- else if (chunk.type === "error") {
2134
- failed = true;
2135
- streamH.fail();
2136
- assistant.setError(chunk.message);
3530
+ try {
3531
+ if (turn.draft && opts.prepareMessage) {
3532
+ const content = await opts.prepareMessage(turn.draft, { signal: turn.preparation.signal });
3533
+ if (!bound || active !== turn) return;
3534
+ turn.request[turn.request.length - 1] = { role: "user", content };
3535
+ history.splice(0, history.length, ...turn.request);
3536
+ } else if (turn.draft?.files?.length) throw new Error(t("aiComposer.filesNeedAdapter"));
3537
+ const s = client.stream({ messages: turn.request });
3538
+ turn.session = s;
3539
+ void s.done.catch(() => {
3540
+ });
3541
+ for await (const chunk of s) {
3542
+ if (!bound || active !== turn) return;
3543
+ if (chunk.type === "text") {
3544
+ turn.text += chunk.delta;
3545
+ streamH.append(chunk.delta);
3546
+ } else if (chunk.type === "error") {
3547
+ failed = true;
3548
+ streamH.fail();
3549
+ assistant.setError(chunk.message);
3550
+ }
3551
+ }
3552
+ const result = await s.done;
3553
+ if (!bound || active !== turn) return;
3554
+ if (!failed) {
3555
+ streamH.done();
3556
+ assistant.setMeta(metaOf(result));
3557
+ updateUsage(result);
3558
+ }
3559
+ if (result.text) history.push({ role: "assistant", content: result.text });
3560
+ } catch (error) {
3561
+ if (!bound || active !== turn) return;
3562
+ failed = true;
3563
+ streamH.fail();
3564
+ assistant.setError(error instanceof Error ? error.message : String(error));
3565
+ } finally {
3566
+ if (!bound || active !== turn) return;
3567
+ active = null;
3568
+ assistant.el.classList.remove("is-streaming");
3569
+ setComposerRunning(composer, false);
3570
+ if (failed) {
3571
+ retryable = turn;
3572
+ return;
3573
+ }
3574
+ const queue = QUEUES.get(composer) ?? [];
3575
+ if (queue.length > 0) {
3576
+ const next = queue.shift();
3577
+ QUEUES.set(composer, queue);
3578
+ renderQueueChips(composer);
3579
+ emit2(composer, "icen:ai-dequeue", { index: 0 });
3580
+ if (bound) send(next);
2137
3581
  }
2138
- }
2139
- const result = await s.done;
2140
- session = null;
2141
- if (!failed) streamH.done();
2142
- assistant.setMeta(metaOf(result));
2143
- if (result.text) history.push({ role: "assistant", content: result.text });
2144
- setComposerRunning(composer, false);
2145
- const queue = QUEUES.get(composer) ?? [];
2146
- if (queue.length > 0) {
2147
- const next = queue.shift() ?? "";
2148
- QUEUES.set(composer, queue);
2149
- renderQueueChips(composer);
2150
- emit2(composer, "icen:ai-dequeue", { index: 0 });
2151
- send(next);
2152
3582
  }
2153
3583
  })();
2154
3584
  };
2155
- const send = (text) => {
2156
- renderAiMessage(messages, { role: "user", content: text });
3585
+ const send = (draft) => {
3586
+ if (!bound || active) return;
3587
+ const missingRefs = (draft.refs ?? []).filter((r) => !draft.text.includes(`@${r.label}`));
3588
+ const text = [draft.text, ...missingRefs.map((r) => `@${r.label}`)].filter(Boolean).join("\n");
3589
+ const message = renderAiMessage(messages, { role: "user", content: draft.text });
3590
+ if (draft.refs?.length || draft.files?.length) {
3591
+ const context = document.createElement("div");
3592
+ context.className = "icen-ai-msg-context";
3593
+ for (const ref of draft.refs ?? []) {
3594
+ const chip = makeChip(ref.label, false, "");
3595
+ chip.prepend(resourceIcon(ref));
3596
+ context.append(chip);
3597
+ }
3598
+ for (const file of draft.files ?? []) context.append(makeChip(file.name, false, "", "file"));
3599
+ message.body.append(context);
3600
+ }
2157
3601
  history.push({ role: "user", content: text });
2158
- startTurn();
3602
+ startTurn(void 0, draft);
2159
3603
  };
2160
3604
  const onSend = (e) => {
2161
- const text = e.detail?.text;
2162
- if (typeof text === "string" && text) send(text);
3605
+ const draft = e.detail;
3606
+ if (typeof draft?.text === "string" && (draft.text || draft.files?.length || draft.refs?.length)) send(draft);
2163
3607
  };
2164
3608
  const onStop = () => {
2165
- session?.cancel();
3609
+ const turn = active;
3610
+ if (!turn) return;
3611
+ active = null;
3612
+ turn.preparation.abort();
3613
+ retryable = turn;
3614
+ turn.stream.cancel();
3615
+ turn.assistant.el.classList.remove("is-streaming");
3616
+ turn.assistant.setMeta(t("aiComposer.stopped"));
3617
+ if (turn.text) history.push({ role: "assistant", content: turn.text });
3618
+ setComposerRunning(composer, false);
3619
+ turn.session?.cancel();
3620
+ };
3621
+ retry = () => {
3622
+ if (!bound || active || !retryable) return false;
3623
+ const turn = retryable;
3624
+ history.splice(0, history.length, ...turn.request);
3625
+ startTurn(turn);
3626
+ return true;
3627
+ };
3628
+ const onRetry = (event) => {
3629
+ if (event.detail?.el === retryable?.assistant.el) retry();
2166
3630
  };
2167
3631
  composer.addEventListener("icen:ai-send", onSend);
2168
3632
  composer.addEventListener("icen:ai-stop", onStop);
3633
+ messages.addEventListener("icen:ai-retry", onRetry);
2169
3634
  disposers.push(() => {
2170
3635
  composer.removeEventListener("icen:ai-send", onSend);
2171
3636
  composer.removeEventListener("icen:ai-stop", onStop);
2172
- session?.cancel();
3637
+ messages.removeEventListener("icen:ai-retry", onRetry);
3638
+ onStop();
3639
+ retryable = null;
2173
3640
  });
2174
3641
  } else if (manageRunning) {
2175
3642
  const onSend = () => setComposerRunning(composer, true);
2176
3643
  const onDone = () => setComposerRunning(composer, false);
2177
3644
  composer.addEventListener("icen:ai-send", onSend);
2178
- document.addEventListener("icen:ai-done", onDone);
3645
+ const target = opts.eventTarget ?? document;
3646
+ target.addEventListener("icen:ai-done", onDone);
2179
3647
  disposers.push(() => {
2180
3648
  composer.removeEventListener("icen:ai-send", onSend);
2181
- document.removeEventListener("icen:ai-done", onDone);
3649
+ target.removeEventListener("icen:ai-done", onDone);
2182
3650
  });
2183
3651
  }
2184
3652
  return {
3653
+ retry: () => retry(),
2185
3654
  unbind() {
3655
+ if (!bound) return;
3656
+ bound = false;
2186
3657
  for (const dispose of disposers) dispose();
2187
3658
  disposers.length = 0;
2188
3659
  BOUND.delete(composer);
@@ -2191,7 +3662,9 @@ function bindComposer(el, opts = {}) {
2191
3662
  }
2192
3663
  export {
2193
3664
  bindComposer,
3665
+ getComposerDraft,
2194
3666
  initAiComposer,
3667
+ openComposerLibrary,
2195
3668
  registerRefKind,
2196
3669
  setComposerCommands,
2197
3670
  setComposerModels,