@elabs-ai/components-ui 4.0.0 → 4.2.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 (339) hide show
  1. package/README.md +10 -8
  2. package/dist/chunk-FCH4ZN6G.js +57 -0
  3. package/dist/chunk-FCH4ZN6G.js.map +1 -0
  4. package/dist/form.d.ts +28 -0
  5. package/dist/form.js +123 -0
  6. package/dist/form.js.map +1 -0
  7. package/dist/index.d.ts +2597 -188
  8. package/dist/index.js +12855 -3578
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/cn.js +10 -1
  11. package/dist/lib/cn.js.map +1 -1
  12. package/package.json +21 -7
  13. package/src/__contract__/attribution-panel.contract.test.tsx +49 -0
  14. package/src/__contract__/badge.contract.test.tsx +49 -0
  15. package/src/__contract__/bounded-number.contract.test.tsx +49 -0
  16. package/src/__contract__/button.contract.test.tsx +49 -0
  17. package/src/__contract__/color-picker.contract.test.tsx +49 -0
  18. package/src/__contract__/combobox.contract.test.tsx +49 -0
  19. package/src/__contract__/command-trigger.contract.test.tsx +49 -0
  20. package/src/__contract__/copyable-value.contract.test.tsx +49 -0
  21. package/src/__contract__/date-picker.contract.test.tsx +49 -0
  22. package/src/__contract__/date-range-picker.contract.test.tsx +49 -0
  23. package/src/__contract__/empty-state.contract.test.tsx +49 -0
  24. package/src/__contract__/error-state.contract.test.tsx +49 -0
  25. package/src/__contract__/icon-button.contract.test.tsx +49 -0
  26. package/src/__contract__/input.contract.test.tsx +49 -0
  27. package/src/__contract__/key-value-editor.contract.test.tsx +49 -0
  28. package/src/__contract__/list-editor.contract.test.tsx +49 -0
  29. package/src/__contract__/loading-state.contract.test.tsx +49 -0
  30. package/src/__contract__/metric-card.contract.test.tsx +49 -0
  31. package/src/__contract__/model-picker.contract.test.tsx +49 -0
  32. package/src/__contract__/nav-notifications.contract.test.tsx +49 -0
  33. package/src/__contract__/number-input.contract.test.tsx +49 -0
  34. package/src/__contract__/rating.contract.test.tsx +49 -0
  35. package/src/__contract__/schema-form.contract.test.tsx +49 -0
  36. package/src/__contract__/section-header.contract.test.tsx +49 -0
  37. package/src/__contract__/segmented-field.contract.test.tsx +49 -0
  38. package/src/__contract__/slider-number.contract.test.tsx +49 -0
  39. package/src/__contract__/status-badge.contract.test.tsx +49 -0
  40. package/src/__contract__/tag-input.contract.test.tsx +49 -0
  41. package/src/__contract__/text.contract.test.tsx +49 -0
  42. package/src/__contract__/textarea.contract.test.tsx +49 -0
  43. package/src/__contract__/timeline.contract.test.tsx +49 -0
  44. package/src/__contract__/toggle.contract.test.tsx +49 -0
  45. package/src/__contract__/transfer.contract.test.tsx +49 -0
  46. package/src/__contract__/tree-select.contract.test.tsx +49 -0
  47. package/src/__contract__/tree.contract.test.tsx +49 -0
  48. package/src/__contract__/virtual-select.contract.test.tsx +49 -0
  49. package/src/__contract__/workspace-picker.contract.test.tsx +49 -0
  50. package/src/_peer-deps.test.ts +59 -0
  51. package/src/blocks-comparison-table.stories.tsx +2 -2
  52. package/src/components/accordion/accordion.tsx +13 -4
  53. package/src/components/advanced-group/advanced-group.tsx +1 -1
  54. package/src/components/alert/alert.tsx +15 -4
  55. package/src/components/alert-dialog/alert-dialog.test.tsx +32 -0
  56. package/src/components/alert-dialog/alert-dialog.tsx +28 -6
  57. package/src/components/app-shell/app-shell.stories.tsx +44 -2
  58. package/src/components/app-shell/app-shell.test.tsx +99 -0
  59. package/src/components/app-shell/app-shell.tsx +56 -4
  60. package/src/components/app-sidebar/app-sidebar.stories.tsx +9 -1
  61. package/src/components/attribution-panel/attribution-panel.tsx +3 -1
  62. package/src/components/attribution-panel/attributions.generated.ts +83 -79
  63. package/src/components/avatar/avatar.tsx +4 -1
  64. package/src/components/badge/badge.stories.tsx +8 -0
  65. package/src/components/badge/badge.test.tsx +8 -0
  66. package/src/components/badge/badge.tsx +15 -5
  67. package/src/components/bento-grid/bento-grid.test.tsx +24 -3
  68. package/src/components/bento-grid/bento-grid.tsx +11 -1
  69. package/src/components/bounded-number/bounded-number.test.tsx +1 -1
  70. package/src/components/bounded-number/bounded-number.tsx +78 -75
  71. package/src/components/breadcrumb/breadcrumb.stories.tsx +38 -0
  72. package/src/components/breadcrumb/breadcrumb.test.tsx +24 -1
  73. package/src/components/breadcrumb/breadcrumb.tsx +10 -4
  74. package/src/components/button/button.stories.tsx +116 -8
  75. package/src/components/button/button.tsx +18 -12
  76. package/src/components/button-group/button-group.tsx +1 -1
  77. package/src/components/calendar/calendar.tsx +3 -2
  78. package/src/components/card/card.stories.tsx +11 -6
  79. package/src/components/card/card.test.tsx +24 -2
  80. package/src/components/card/card.tsx +48 -7
  81. package/src/components/carousel/carousel.test.tsx +169 -0
  82. package/src/components/carousel/carousel.tsx +93 -32
  83. package/src/components/change-review/change-review.stories.tsx +145 -1
  84. package/src/components/change-review/change-review.test.tsx +143 -0
  85. package/src/components/change-review/change-review.tsx +214 -24
  86. package/src/components/checkbox/checkbox.tsx +2 -1
  87. package/src/components/collapsible-panel/use-collapsible-panel.ts +0 -3
  88. package/src/components/color-picker/color-picker.tsx +22 -15
  89. package/src/components/combobox/combobox.stories.tsx +10 -0
  90. package/src/components/combobox/combobox.test.tsx +54 -0
  91. package/src/components/combobox/combobox.tsx +25 -11
  92. package/src/components/command/command.stories.tsx +16 -6
  93. package/src/components/command/command.test.tsx +88 -0
  94. package/src/components/command/command.tsx +38 -13
  95. package/src/components/command/index.ts +1 -0
  96. package/src/components/command-trigger/command-trigger.stories.tsx +39 -0
  97. package/src/components/command-trigger/command-trigger.test.tsx +18 -0
  98. package/src/components/command-trigger/command-trigger.tsx +70 -0
  99. package/src/components/command-trigger/index.ts +1 -0
  100. package/src/components/context-menu/context-menu.tsx +16 -7
  101. package/src/components/context-rail/context-rail.stories.tsx +285 -0
  102. package/src/components/context-rail/context-rail.test.tsx +263 -0
  103. package/src/components/context-rail/context-rail.tsx +678 -0
  104. package/src/components/context-rail/index.ts +1 -0
  105. package/src/components/copyable-value/copyable-value.stories.tsx +10 -0
  106. package/src/components/copyable-value/copyable-value.test.tsx +28 -1
  107. package/src/components/copyable-value/copyable-value.tsx +13 -2
  108. package/src/components/date-picker/date-picker.stories.tsx +9 -0
  109. package/src/components/date-picker/date-picker.test.tsx +52 -0
  110. package/src/components/date-picker/date-picker.tsx +13 -15
  111. package/src/components/date-range-picker/date-range-picker.stories.tsx +9 -0
  112. package/src/components/date-range-picker/date-range-picker.test.tsx +76 -1
  113. package/src/components/date-range-picker/date-range-picker.tsx +39 -28
  114. package/src/components/descriptions/descriptions.tsx +1 -1
  115. package/src/components/dialog/dialog.test.tsx +8 -3
  116. package/src/components/dialog/dialog.tsx +14 -6
  117. package/src/components/drawer/drawer.tsx +23 -6
  118. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +1 -1
  119. package/src/components/dropdown-menu/dropdown-menu.tsx +4 -4
  120. package/src/components/empty-state/empty-state.test.tsx +26 -0
  121. package/src/components/empty-state/empty-state.tsx +6 -1
  122. package/src/components/expand-dialog/expand-dialog.tsx +1 -2
  123. package/src/components/field/field-context.ts +59 -0
  124. package/src/components/field/field.stories.tsx +142 -0
  125. package/src/components/field/field.test.tsx +415 -0
  126. package/src/components/field/field.tsx +342 -0
  127. package/src/components/field/index.ts +9 -0
  128. package/src/components/field-row/field-row.stories.tsx +85 -1
  129. package/src/components/field-row/field-row.test.tsx +145 -0
  130. package/src/components/field-row/field-row.tsx +50 -6
  131. package/src/components/file-upload/file-upload.stories.tsx +16 -0
  132. package/src/components/file-upload/file-upload.test.tsx +90 -12
  133. package/src/components/file-upload/file-upload.tsx +164 -31
  134. package/src/components/form/form.tsx +8 -3
  135. package/src/components/hover-card/hover-card.tsx +1 -0
  136. package/src/components/icon-button/icon-button.test.tsx +1 -1
  137. package/src/components/icon-button/icon-button.tsx +3 -3
  138. package/src/components/input/input.stories.tsx +121 -0
  139. package/src/components/input/input.test.tsx +13 -0
  140. package/src/components/input/input.tsx +8 -4
  141. package/src/components/input-group/input-group.stories.tsx +74 -0
  142. package/src/components/input-group/input-group.tsx +18 -9
  143. package/src/components/input-otp/input-otp.tsx +8 -2
  144. package/src/components/keyboard-shortcuts/index.ts +6 -0
  145. package/src/components/keyboard-shortcuts/keyboard-shortcuts.stories.tsx +177 -0
  146. package/src/components/keyboard-shortcuts/keyboard-shortcuts.test.tsx +73 -0
  147. package/src/components/keyboard-shortcuts/keyboard-shortcuts.tsx +203 -0
  148. package/src/components/label/label.tsx +1 -1
  149. package/src/components/link-preview/link-preview.tsx +1 -1
  150. package/src/components/list-editor/list-editor.test.tsx +24 -0
  151. package/src/components/list-editor/list-editor.tsx +126 -52
  152. package/src/components/locale-provider/index.ts +6 -1
  153. package/src/components/locale-provider/locale-keys.test.ts +134 -0
  154. package/src/components/locale-provider/locale-provider.stories.tsx +89 -1
  155. package/src/components/locale-provider/locale-provider.test.tsx +257 -1
  156. package/src/components/locale-provider/locale-provider.tsx +63 -8
  157. package/src/components/locale-provider/messages.ts +822 -2
  158. package/src/components/match-highlight/match-highlight.stories.tsx +1 -1
  159. package/src/components/mention-input/mention-input-mirror.stories.tsx +9 -1
  160. package/src/components/mention-input/mention-input.stories.tsx +3 -3
  161. package/src/components/mention-input/mention-input.test.tsx +51 -1
  162. package/src/components/mention-input/mention-input.tsx +66 -46
  163. package/src/components/mention-input/mention-value.ts +8 -2
  164. package/src/components/menubar/menubar.tsx +16 -6
  165. package/src/components/metric-card/metric-card.stories.tsx +5 -3
  166. package/src/components/metric-card/metric-card.tsx +21 -2
  167. package/src/components/model-picker/model-picker.stories.tsx +63 -3
  168. package/src/components/model-picker/model-picker.test.tsx +63 -0
  169. package/src/components/model-picker/model-picker.tsx +62 -36
  170. package/src/components/nav-main/nav-main.tsx +8 -4
  171. package/src/components/nav-notifications/nav-notifications.stories.tsx +110 -0
  172. package/src/components/nav-notifications/nav-notifications.test.tsx +11 -0
  173. package/src/components/nav-notifications/nav-notifications.tsx +67 -9
  174. package/src/components/nav-user/nav-user.tsx +5 -5
  175. package/src/components/navigation-menu/navigation-menu.stories.tsx +22 -0
  176. package/src/components/navigation-menu/navigation-menu.test.tsx +174 -0
  177. package/src/components/navigation-menu/navigation-menu.tsx +204 -15
  178. package/src/components/number-input/number-input.stories.tsx +7 -3
  179. package/src/components/number-input/number-input.test.tsx +47 -0
  180. package/src/components/number-input/number-input.tsx +65 -24
  181. package/src/components/page-shell/page-shell.stories.tsx +163 -0
  182. package/src/components/page-shell/page-shell.test.tsx +229 -0
  183. package/src/components/page-shell/page-shell.tsx +108 -25
  184. package/src/components/pagination/pagination.test.tsx +109 -0
  185. package/src/components/pagination/pagination.tsx +27 -19
  186. package/src/components/popover/popover.tsx +1 -0
  187. package/src/components/progress/progress.tsx +2 -0
  188. package/src/components/radio-group/radio-group.tsx +1 -1
  189. package/src/components/rating/rating.tsx +3 -1
  190. package/src/components/resizable/resizable.stories.tsx +114 -2
  191. package/src/components/resizable/resizable.test.tsx +76 -0
  192. package/src/components/resizable/resizable.tsx +32 -6
  193. package/src/components/revision-timeline/revision-timeline.stories.tsx +8 -1
  194. package/src/components/revision-timeline/revision-timeline.test.tsx +26 -0
  195. package/src/components/revision-timeline/revision-timeline.tsx +53 -5
  196. package/src/components/schema-form/from-json-schema.test.ts +390 -0
  197. package/src/components/schema-form/from-json-schema.ts +375 -0
  198. package/src/components/schema-form/index.ts +81 -0
  199. package/src/components/schema-form/schema-form-spec.ts +698 -0
  200. package/src/components/schema-form/schema-form-store.ts +506 -0
  201. package/src/components/schema-form/schema-form.stories.tsx +566 -0
  202. package/src/components/schema-form/schema-form.test.tsx +1021 -0
  203. package/src/components/schema-form/schema-form.tsx +1381 -0
  204. package/src/components/section-header/section-header.stories.tsx +37 -0
  205. package/src/components/section-header/section-header.tsx +14 -3
  206. package/src/components/select/select.tsx +13 -7
  207. package/src/components/separator/separator.tsx +1 -0
  208. package/src/components/sheet/sheet.test.tsx +39 -0
  209. package/src/components/sheet/sheet.tsx +40 -9
  210. package/src/components/side-dock/index.ts +1 -0
  211. package/src/components/side-dock/side-dock.stories.tsx +137 -0
  212. package/src/components/side-dock/side-dock.test.tsx +299 -0
  213. package/src/components/side-dock/side-dock.tsx +515 -0
  214. package/src/components/sidebar/index.ts +1 -0
  215. package/src/components/sidebar/sidebar-frame.test.tsx +162 -0
  216. package/src/components/sidebar/sidebar.stories.tsx +421 -1
  217. package/src/components/sidebar/sidebar.test.tsx +71 -2
  218. package/src/components/sidebar/sidebar.tsx +264 -38
  219. package/src/components/skeleton/skeleton.tsx +1 -0
  220. package/src/components/skip-link/index.ts +1 -0
  221. package/src/components/skip-link/skip-link.stories.tsx +106 -0
  222. package/src/components/skip-link/skip-link.test.tsx +20 -0
  223. package/src/components/skip-link/skip-link.tsx +34 -0
  224. package/src/components/slider/slider.tsx +1 -1
  225. package/src/components/slider-number/slider-number.stories.tsx +4 -1
  226. package/src/components/slider-number/slider-number.test.tsx +1 -1
  227. package/src/components/sonner/sonner.tsx +2 -0
  228. package/src/components/spinner/spinner.test.tsx +18 -0
  229. package/src/components/spinner/spinner.tsx +14 -4
  230. package/src/components/split-panel/index.ts +6 -1
  231. package/src/components/split-panel/split-panel.stories.tsx +18 -2
  232. package/src/components/split-panel/split-panel.test.tsx +20 -0
  233. package/src/components/split-panel/split-panel.tsx +28 -8
  234. package/src/components/state-panel/state-panel.stories.tsx +52 -0
  235. package/src/components/state-panel/state-panel.test.tsx +125 -0
  236. package/src/components/state-panel/state-panel.tsx +110 -24
  237. package/src/components/status-badge/status-badge.stories.tsx +8 -0
  238. package/src/components/switch/switch.tsx +3 -1
  239. package/src/components/table/table.stories.tsx +64 -0
  240. package/src/components/table/table.test.tsx +149 -1
  241. package/src/components/table/table.tsx +71 -2
  242. package/src/components/tabs/index.ts +10 -1
  243. package/src/components/tabs/tabs.stories.tsx +48 -0
  244. package/src/components/tabs/tabs.test.tsx +67 -0
  245. package/src/components/tabs/tabs.tsx +176 -72
  246. package/src/components/tag-input/tag-input.tsx +6 -3
  247. package/src/components/team-switcher/team-switcher.tsx +35 -6
  248. package/src/components/textarea/textarea.tsx +2 -2
  249. package/src/components/theme-switcher/theme-switcher.stories.tsx +76 -0
  250. package/src/components/theme-switcher/theme-switcher.test.tsx +182 -0
  251. package/src/components/theme-switcher/theme-switcher.tsx +140 -12
  252. package/src/components/theme-switcher/use-theme-transition.ts +2 -2
  253. package/src/components/timeline/timeline.stories.tsx +27 -0
  254. package/src/components/timeline/timeline.tsx +10 -3
  255. package/src/components/toggle/toggle.tsx +7 -2
  256. package/src/components/toggle-group/toggle-group.tsx +2 -0
  257. package/src/components/toolbar/toolbar.stories.tsx +10 -2
  258. package/src/components/tooltip/tooltip.tsx +2 -1
  259. package/src/components/top-nav/top-nav.tsx +1 -1
  260. package/src/components/transfer/transfer.tsx +35 -30
  261. package/src/components/tree/tree.stories.tsx +2 -2
  262. package/src/components/tree/tree.test.tsx +41 -0
  263. package/src/components/tree/tree.tsx +91 -31
  264. package/src/components/tree/use-tree-keyboard.ts +37 -14
  265. package/src/components/tree-select/tree-select.stories.tsx +9 -0
  266. package/src/components/tree-select/tree-select.test.tsx +5 -0
  267. package/src/components/tree-select/tree-select.tsx +11 -14
  268. package/src/components/typography/prose.test.tsx +3 -2
  269. package/src/components/typography/prose.tsx +3 -3
  270. package/src/components/typography/typography.stories.tsx +228 -0
  271. package/src/components/view-toolbar/view-toolbar.stories.tsx +41 -1
  272. package/src/components/view-toolbar/view-toolbar.test.tsx +33 -0
  273. package/src/components/view-toolbar/view-toolbar.tsx +37 -4
  274. package/src/components/virtual-select/virtual-select.stories.tsx +9 -0
  275. package/src/components/virtual-select/virtual-select.test.tsx +29 -3
  276. package/src/components/virtual-select/virtual-select.tsx +56 -18
  277. package/src/components/wizard/wizard.test.tsx +9 -0
  278. package/src/components/wizard/wizard.tsx +13 -5
  279. package/src/components/workspace-picker/index.ts +3 -0
  280. package/src/components/workspace-picker/workspace-picker-state.ts +50 -0
  281. package/src/components/workspace-picker/workspace-picker.stories.tsx +126 -0
  282. package/src/components/workspace-picker/workspace-picker.test.tsx +207 -0
  283. package/src/components/workspace-picker/workspace-picker.tsx +172 -0
  284. package/src/illustrations/empty-list-illustration.tsx +26 -0
  285. package/src/illustrations/error-illustration.tsx +60 -0
  286. package/src/illustrations/first-run-illustration.tsx +27 -0
  287. package/src/illustrations/illustration-base.tsx +108 -0
  288. package/src/illustrations/illustrations.stories.tsx +94 -0
  289. package/src/illustrations/illustrations.test.tsx +98 -0
  290. package/src/illustrations/index.ts +16 -0
  291. package/src/illustrations/no-access-illustration.tsx +45 -0
  292. package/src/illustrations/no-results-illustration.tsx +46 -0
  293. package/src/illustrations/offline-illustration.tsx +37 -0
  294. package/src/illustrations/success-illustration.tsx +40 -0
  295. package/src/index.ts +80 -2
  296. package/src/lib/agent-event-model.test.ts +12 -0
  297. package/src/lib/agent-event-model.ts +42 -0
  298. package/src/lib/approval-option.test.ts +17 -0
  299. package/src/lib/approval-option.ts +53 -0
  300. package/src/lib/check-result.ts +33 -0
  301. package/src/lib/cn.ts +9 -0
  302. package/src/lib/csv.ts +48 -0
  303. package/src/lib/diff-line.test.ts +36 -0
  304. package/src/lib/diff-line.ts +63 -0
  305. package/src/lib/diff-rows.test.ts +76 -0
  306. package/src/lib/diff-rows.ts +94 -0
  307. package/src/lib/format-duration.test.ts +22 -0
  308. package/src/lib/format-duration.ts +30 -0
  309. package/src/lib/has-renderable-content.test.ts +49 -0
  310. package/src/lib/has-renderable-content.ts +32 -0
  311. package/src/lib/merge-refs.ts +2 -0
  312. package/src/lib/operating-mode.test.ts +36 -0
  313. package/src/lib/operating-mode.ts +81 -0
  314. package/src/lib/optional-peer.ts +59 -0
  315. package/src/lib/session-launch.ts +37 -0
  316. package/src/lib/slash-command.test.ts +44 -0
  317. package/src/lib/slash-command.ts +45 -0
  318. package/src/lib/trigger-query.test.ts +94 -0
  319. package/src/lib/trigger-query.ts +88 -0
  320. package/src/lib/use-controllable-state.ts +47 -0
  321. package/src/lib/use-mobile.test.tsx +47 -0
  322. package/src/lib/use-mobile.ts +34 -12
  323. package/src/motion.stories.tsx +6 -0
  324. package/src/templates-object-detail-hub.stories.tsx +2 -2
  325. package/src/templates-screen-states.stories.tsx +5 -5
  326. package/src/templates-settings.stories.tsx +1 -1
  327. package/src/blocks/sidebar-02/app-sidebar.tsx +0 -160
  328. package/src/blocks/sidebar-02/logo.tsx +0 -18
  329. package/src/blocks/sidebar-02/nav-main.tsx +0 -13
  330. package/src/blocks/sidebar-02/nav-notifications.tsx +0 -7
  331. package/src/blocks/sidebar-02/sidebar-02.stories.tsx +0 -23
  332. package/src/blocks/sidebar-02/team-switcher.tsx +0 -7
  333. package/src/blocks/sidebar-04/app-sidebar.tsx +0 -236
  334. package/src/blocks/sidebar-04/mail-context.tsx +0 -29
  335. package/src/blocks/sidebar-04/nav-user.tsx +0 -6
  336. package/src/blocks/sidebar-04/sidebar-04.stories.tsx +0 -150
  337. package/src/blocks/sidebar-05/app-sidebar.tsx +0 -375
  338. package/src/blocks/sidebar-05/sidebar-05.stories.tsx +0 -18
  339. package/src/blocks/sidebar-05/team-switcher.tsx +0 -6
@@ -1,5 +1,26 @@
1
1
  "use client";
2
2
 
3
+ /**
4
+ * ICU cardinal-plural category, per `Intl.PluralRules` ("zero" | "one" | "two"
5
+ * | "few" | "many" | "other"). Locales vary in which categories they use —
6
+ * English has only "one"/"other"; Polish and Russian also use "few"/"many" —
7
+ * so a `PluralMessage` may omit any category it doesn't need. `"other"` is the
8
+ * universal fallback every locale defines.
9
+ */
10
+ export type PluralCategory = Intl.LDMLPluralRule;
11
+
12
+ /**
13
+ * A per-count message: one string per plural category, selected at render
14
+ * time via `Intl.PluralRules(locale).select(count)`. Each string still
15
+ * supports the same `{name}`-style interpolation as a plain message.
16
+ * Note: when passed to `t()`, `count` in `vars` must be a number, not a
17
+ * numeric string, to select the correct plural category.
18
+ */
19
+ export type PluralMessage = Partial<Record<PluralCategory, string>>;
20
+
21
+ /** A single message value: a plain string, or a plural-form map. */
22
+ export type MessageValue = string | PluralMessage;
23
+
3
24
  /**
4
25
  * Shipped English (en-US) default microcopy bundle.
5
26
  *
@@ -7,10 +28,11 @@
7
28
  * translate cleanly into any target locale. Keep this list small and real —
8
29
  * only add a key here when a component actually needs it.
9
30
  */
10
- export const DEFAULT_MESSAGES: Record<string, string> = {
31
+ export const DEFAULT_MESSAGES: Record<string, MessageValue> = {
11
32
  // ── Generic (shared across packages — reuse these before minting a new key) ──
12
33
  close: "Close",
13
34
  copy: "Copy",
35
+ clear: "Clear",
14
36
  previous: "Previous",
15
37
  next: "Next",
16
38
  previousSlide: "Previous slide",
@@ -20,6 +42,11 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
20
42
  loading: "Loading…",
21
43
  more: "More",
22
44
  selectAll: "Select all",
45
+ // Cardinal-plural example (#19) — a shared "N item(s) selected" microcopy
46
+ // any multi-select surface can reuse via `t("itemsSelected", { count })`.
47
+ // Demonstrates the plural-form shape: pick the primitive over inventing a
48
+ // near-duplicate flat string per component.
49
+ itemsSelected: { one: "{count} item selected", other: "{count} items selected" },
23
50
 
24
51
  // ── @elabs-ai/components-ui ───────────────────────────────────────────────────────────────
25
52
  "ui.metricCard.loading": "Loading metric…",
@@ -43,6 +70,24 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
43
70
  "ui.modelPicker.loadFailed": "Couldn't load the list",
44
71
  "ui.modelPicker.nothingYet": "Nothing to show yet",
45
72
  "ui.modelPicker.retry": "Retry",
73
+ // KeyboardShortcuts (#113). `emptyFiltered` keeps the user's own query in the
74
+ // sentence so the empty state says WHY it is empty, not merely that it is.
75
+ "ui.keyboardShortcuts.searchPlaceholder": "Search shortcuts…",
76
+ "ui.keyboardShortcuts.emptyTitle": "No shortcuts found",
77
+ "ui.keyboardShortcuts.emptyFiltered": "No shortcuts match “{query}”.",
78
+ "ui.keyboardShortcuts.empty": "No shortcuts to show.",
79
+ // WorkspacePicker (#111). It composes ModelPicker, so it carries its OWN
80
+ // label/search strings rather than inheriting the generic "Choose a target".
81
+ // `current` is appended into the row's meta so the in-force workspace reaches
82
+ // the option's accessible name as a WORD, not only a check glyph (1.4.1).
83
+ "ui.workspacePicker.label": "Choose a workspace",
84
+ "ui.workspacePicker.placeholder": "No workspace selected",
85
+ "ui.workspacePicker.recent": "Recent workspaces",
86
+ "ui.workspacePicker.searchPlaceholder": "Search workspaces…",
87
+ "ui.workspacePicker.current": "Current",
88
+ "ui.workspacePicker.pathLabel": "Workspace path",
89
+ "ui.workspacePicker.pathPlaceholder": "/path/to/project…",
90
+ "ui.workspacePicker.openPath": "Open",
46
91
  // ViewToolbar (#331). `removeFilter` deliberately WRAPS the visible chip text
47
92
  // so the accessible name contains it (WCAG 2.5.3 Label in Name).
48
93
  "ui.viewToolbar.about": "About this view",
@@ -81,14 +126,212 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
81
126
  "ui.confirmDialog.cancel": "Cancel",
82
127
  "ui.advancedGroup.title": "Advanced",
83
128
  "ui.advancedGroup.changed": "{count} changed",
129
+ // SchemaForm — the spec-driven config-form renderer (issue #22).
130
+ "ui.schemaForm.selectPlaceholder": "Select…",
131
+ "ui.schemaForm.label": "Form",
132
+ "ui.schemaForm.submit": "Submit",
133
+ "ui.schemaForm.submitting": "Submitting…",
134
+ "ui.schemaForm.submitted": "Submitted",
135
+ // SchemaFormTestAction — a form/group-level "Test connection" affordance
136
+ // (issue #22 maintainer ruling, 2026-09-01), independent of field validity
137
+ // and never gating submit.
138
+ "ui.schemaForm.testAction.label": "Test connection",
139
+ "ui.schemaForm.testAction.pending": "Testing…",
140
+ "ui.schemaForm.testAction.success": "Connected",
141
+ "ui.schemaForm.testAction.failure": "Test failed",
142
+ // Pagination's ellipsis (sr-only — the visible glyph is decorative).
143
+ "ui.breadcrumb.label": "breadcrumb",
144
+ "ui.pagination.label": "pagination",
145
+ "ui.pagination.morePages": "More pages",
146
+ // Pagination's prev/next links — the ACCESSIBLE NAME (`aria-label`), distinct
147
+ // from the visible `previous`/`next` text below it: an `aria-label` overrides
148
+ // visible text content as the accessible name, so leaving these hardcoded in
149
+ // English meant a non-English `LocaleProvider` translated the visible label
150
+ // but a screen-reader user still heard English (#12/#53 review, P2).
151
+ "ui.pagination.previous": "Go to previous page",
152
+ "ui.pagination.next": "Go to next page",
153
+ // Sidebar's mobile Sheet title (sr-only header — the sheet itself has no
154
+ // visible chrome, so this is only ever read by assistive tech).
155
+ "ui.sidebar.title": "Sidebar",
156
+ // ContextRail's switcher count phrase (composed into the accessible name,
157
+ // e.g. "Sources 3 items") and its default empty-state copy.
158
+ "ui.contextRail.sectionCount": { one: "{count} item", other: "{count} items" },
159
+ "ui.contextRail.empty": "No sections",
160
+ // SideDock's resize handle — its sr-only label and its live width readout.
161
+ "ui.sideDock.resize": "Resize",
162
+ "ui.sideDock.widthValue": { one: "{size} pixel", other: "{size} pixels" },
163
+ // Tree's error row — shared by the virtualized and non-virtualized branches.
164
+ "ui.tree.failedToLoad": "Failed to load",
165
+ // ThemeSwitcher's "follow the OS" option, in both dropdown and toggle modes.
166
+ "ui.themeSwitcher.system": "System",
167
+ // Group labels + scheme names in the family layout (ADR 0036).
168
+ "ui.themeSwitcher.theme": "Theme",
169
+ "ui.themeSwitcher.mode": "Mode",
170
+ "ui.themeSwitcher.light": "Light",
171
+ "ui.themeSwitcher.dark": "Dark",
172
+ "ui.navNotifications.label": "Notifications",
173
+ "ui.teamSwitcher.label": "Teams",
174
+ "ui.teamSwitcher.addTeam": "Add team",
175
+ // Table's own scroll wrapper (#366). Rendered ONLY when the wrapper actually
176
+ // overflows, so it is never announced for a table that fits — see
177
+ // `packages/ui/src/components/table/table.tsx`. Deliberately a `ui.*`
178
+ // sibling of `data.table.scrollRegion` rather than a shared cross-package
179
+ // key: `Table` is a `@elabs-ai/components-ui` component and each package
180
+ // keeps its own namespace even where the English string coincides (compare
181
+ // `viewer.content` vs this), so a translator can phrase either
182
+ // independently and neither package depends on a string owned by another.
183
+ "ui.table.scrollRegion": "Table contents, scrollable",
184
+ // NumberInput's step buttons (#4 i18n sweep).
185
+ "ui.numberInput.decrease": "Decrease",
186
+ "ui.numberInput.increase": "Increase",
187
+ // Combobox/VirtualSelect/TreeSelect each keep their own placeholder pair
188
+ // (English text coincides, but a translator may phrase a search box
189
+ // differently per surface — same rationale as `ui.table.scrollRegion`).
190
+ "ui.combobox.placeholder": "Select…",
191
+ "ui.combobox.searchPlaceholder": "Search…",
192
+ "ui.virtualSelect.placeholder": "Select…",
193
+ "ui.virtualSelect.searchPlaceholder": "Search…",
194
+ "ui.treeSelect.placeholder": "Select…",
195
+ "ui.treeSelect.moreSelected": "and {count} more selected",
196
+ "ui.fileUpload.selectedFiles": "Selected files",
197
+ "ui.fileUpload.dragDropHere": "Drag & drop files here",
198
+ "ui.fileUpload.browseFiles": "Browse files",
199
+ // Sidebar's mobile Sheet description (sr-only) and the collapse/expand
200
+ // trigger's shared name (SidebarTrigger's sr-only text + SidebarRail's
201
+ // aria-label/title).
202
+ "ui.sidebar.mobileDescription": "Displays the mobile sidebar.",
203
+ "ui.sidebar.toggle": "Toggle Sidebar",
204
+ "ui.revisionTimeline.label": "Revisions",
205
+ "ui.tree.retry": "Retry",
206
+ "ui.tree.retryLoadingChildren": "Retry loading children",
207
+ // Spinner's bare default label — kept WITHOUT an ellipsis (unlike the
208
+ // generic `loading` key) to stay byte-identical to its pre-i18n default.
209
+ "ui.spinner.label": "Loading",
210
+ "ui.statePanel.errorTitle": "Something went wrong",
211
+ "ui.statePanel.errorDescription": "An unexpected error occurred. Please try again.",
212
+ "ui.statePanel.errorEyebrow": "Error",
213
+ "ui.carousel.label": "Carousel",
214
+ // Transfer (#4 i18n sweep). `panelFallback`/`itemsFallback` back the two
215
+ // spots where the panel title isn't a plain string (a ReactNode) and the
216
+ // sr-only text falls back to a generic word instead.
217
+ "ui.transfer.selectAllIn": "Select all in {title}",
218
+ "ui.transfer.panelFallback": "panel",
219
+ "ui.transfer.searchIn": "Search {title}",
220
+ "ui.transfer.itemsFallback": "items",
221
+ "ui.transfer.searchPlaceholder": "Search…",
222
+ "ui.transfer.empty": "No items",
223
+ "ui.transfer.defaultSourceTitle": "Source",
224
+ "ui.transfer.defaultTargetTitle": "Target",
225
+ "ui.transfer.movedToTarget": {
226
+ one: "{count} item moved to the target list.",
227
+ other: "{count} items moved to the target list.",
228
+ },
229
+ "ui.transfer.movedToSource": {
230
+ one: "{count} item moved to the source list.",
231
+ other: "{count} items moved to the source list.",
232
+ },
233
+ "ui.transfer.moveControls": "Move controls",
234
+ "ui.transfer.moveSelectedRight": "Move selected right",
235
+ "ui.transfer.moveAllRight": "Move all right",
236
+ "ui.transfer.moveSelectedLeft": "Move selected left",
237
+ "ui.transfer.moveAllLeft": "Move all left",
238
+ // ChangeReview (#4 i18n sweep).
239
+ "ui.changeReview.heading": "Review changes",
240
+ "ui.changeReview.approvedCount": "{approved} of {total} approved",
241
+ "ui.changeReview.rejectAll": "Reject all",
242
+ "ui.changeReview.rejectAllLabel": "Reject all changes",
243
+ "ui.changeReview.approveAll": "Approve all",
244
+ "ui.changeReview.approveAllLabel": "Approve all changes",
245
+ "ui.changeReview.provenanceLabel": "Change provenance",
246
+ "ui.changeReview.emptyTitle": "No changes to review",
247
+ "ui.changeReview.emptyDescription":
248
+ "When an agent proposes edits, they'll appear here for your approval.",
249
+ "ui.changeReview.statusAdded": "added",
250
+ "ui.changeReview.statusRemoved": "removed",
251
+ "ui.changeReview.statusModified": "modified",
252
+ "ui.changeReview.phaseBefore": "Before",
253
+ "ui.changeReview.phaseAfter": "After",
254
+ "ui.changeReview.checkPassed": "Passed",
255
+ "ui.changeReview.checkFailed": "Failed",
256
+ "ui.changeReview.showDetail": "Show detail",
257
+ "ui.changeReview.hideDetail": "Hide detail",
258
+ "ui.changeReview.rejectHunk": "Reject hunk: {title}",
259
+ "ui.changeReview.approveHunk": "Approve hunk: {title}",
260
+ "ui.changeReview.changeType": "Change type: {status}",
261
+ "ui.changeReview.beforePrefix": "Before: ",
262
+ "ui.changeReview.afterPrefix": "After: ",
263
+ "ui.changeReview.approvedBadge": "Approved",
264
+ "ui.navNotifications.trigger": "Open notifications",
265
+ "ui.colorPicker.pickColor": "Pick color",
266
+ "ui.colorPicker.none": "None",
267
+ "ui.colorPicker.swatches": "Color swatches",
268
+ "ui.colorPicker.customHexLabel": "Custom hex",
269
+ "ui.colorPicker.notThemeAware": "(not theme-aware)",
270
+ "ui.colorPicker.customHex": "Custom hex color",
84
271
 
85
272
  // ── @elabs-ai/components-data ─────────────────────────────────────────────────────────────
86
273
  // The scroll region's accessible name is rendered ONLY when the table actually
87
274
  // overflows its container, so it is never announced for a table that fits.
88
275
  "data.table.scrollRegion": "Table contents, scrollable",
276
+ "data.table.loading": "Loading table data…",
89
277
  // Fallback name for a row's activation control when the row's first cell holds
90
278
  // no primitive value to name it after (see `rowActionLabel`).
91
279
  "data.table.rowAction": "Activate row",
280
+ // createSelectionColumn (#11) — the header select-all checkbox and each row's
281
+ // own checkbox. Kept table-scoped (not the generic `selectAll` key) so a
282
+ // translator can phrase "rows" distinctly from other bulk-select surfaces.
283
+ "data.table.selectAllRows": "Select all rows",
284
+ // `selectRowNamed` names each row's checkbox from its own first data column
285
+ // (#11 I4) so screen-reader users hear "Select Alpha", not `selectRow`'s
286
+ // identical generic label repeated on every row; `selectRow` stays the
287
+ // fallback when no data column value is derivable.
288
+ "data.table.selectRowNamed": "Select {name}",
289
+ "data.table.selectRow": "Select row",
290
+ // Column resizing (#12) — the accessible name for the WAI-ARIA
291
+ // separator-as-slider resize handle at the end of a resizable header cell.
292
+ "data.table.resizeColumn": "Resize column, {name}",
293
+ // #51 — the handle's accessible VALUE, paired with aria-valuenow (which
294
+ // stays a plain number for AT/TanStack). A bare number reads as a
295
+ // dimensionless ordinal; the unit makes it a size. A `PluralMessage` (PR
296
+ // #81 review, "Format the announced resize value for the active locale") —
297
+ // the call site passes both `count` (the raw number, so a locale whose
298
+ // plural rules select something other than "other" for a given size is
299
+ // reachable) and `size` (pre-formatted via `formatNumber`, so a locale
300
+ // override renders locale-appropriate digits/grouping instead of a raw
301
+ // Latin-digit JS number).
302
+ "data.table.resizeColumnValue": { one: "{size} pixel", other: "{size} pixels" },
303
+ // Row drag-reorder (#13). `reorderHandle`/`reorderColumnHeader` name the
304
+ // grip control and its column; the four `reorder*` announcement keys back
305
+ // the aria-live region dnd-kit's `accessibility.announcements` renders on
306
+ // pickup/move/drop/cancel (WCAG 4.1.3) — `position`/`total` are 1-based so
307
+ // the announcement reads like "3 of 8", not a 0-based index.
308
+ "data.table.reorderColumnHeader": "Reorder",
309
+ "data.table.reorderHandle": "Reorder {name}",
310
+ "data.table.reorderPickedUp": "Picked up {name}.",
311
+ "data.table.reorderMoved": "{name} moved to position {position} of {total}.",
312
+ "data.table.reorderDropped": "{name} dropped at position {position} of {total}.",
313
+ "data.table.reorderCancelled":
314
+ "Reordering cancelled. {name} returned to position {position} of {total}.",
315
+ // #98: `@dnd-kit` renders two more AT-visible strings for this same
316
+ // feature that this repo's source never writes — the hidden keyboard
317
+ // usage instructions (wired to the grip via `aria-describedby`) and the
318
+ // activator's `aria-roledescription`. Both ship a hardcoded English
319
+ // default deep inside the library; localizing them means overriding them
320
+ // explicitly with these two keys. The English DEFAULT below is dnd-kit's
321
+ // own default text verbatim (`defaultScreenReaderInstructions.draggable`
322
+ // in `@dnd-kit/core`, and `useSortable`'s `roleDescription: 'sortable'` in
323
+ // `@dnd-kit/sortable`) — not a rewrite — so an English consumer with no
324
+ // `LocaleProvider` override sees byte-identical output to before this
325
+ // fix; only a `messages` override changes it.
326
+ "data.table.reorderInstructions":
327
+ "\n To pick up a draggable item, press the space bar.\n While dragging, use the arrow keys to move the item.\n Press space again to drop the item in its new position, or press escape to cancel.\n ",
328
+ "data.table.reorderRoleDescription": "sortable",
329
+ "data.facetFilter.clearFilters": "Clear filters",
330
+ "data.columnPicker.toggleColumns": "Toggle columns",
331
+ "data.columnPicker.label": "Columns",
332
+ "data.searchInput.label": "Search",
333
+ "data.searchInput.placeholder": "Search…",
334
+ "data.searchInput.clear": "Clear search",
92
335
 
93
336
  // ── @elabs-ai/components-charts ───────────────────────────────────────────────────────────
94
337
  // Shared caption for any bare chart surface's layout-shaped skeleton
@@ -105,6 +348,49 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
105
348
  // Announced when `copyValueOnActivate` puts a datapoint's exact value on the
106
349
  // clipboard — the recovery path for a compact axis label.
107
350
  "charts.datapoint.copied": "Exact value copied",
351
+ // ChartFrame's SVG/PNG export actions (RM-025). Each string is used twice —
352
+ // as the icon button's `aria-label` (its ONLY accessible name) and as the
353
+ // tooltip a sighted user reads — so one key serves both and they cannot
354
+ // drift apart in translation.
355
+ "charts.chartFrame.exportSvg": "Export as SVG",
356
+ "charts.chartFrame.exportPng": "Export as PNG",
357
+ "charts.chartFrame.flipToTable": "Flip to table view",
358
+ "charts.chartFrame.showChart": "Show chart",
359
+ "charts.chartFrame.showAsTable": "Show as table",
360
+ "charts.chartFrame.downloadCsv": "Download CSV",
361
+ "charts.chartFrame.expandChart": "Expand chart",
362
+ "charts.chartFrame.expand": "Expand",
363
+ "charts.chartFrame.summary": "Summary",
364
+ "charts.chartFrame.noDataToSummarize": "No data to summarize.",
365
+ "charts.chartFrame.defaultTitle": "Chart",
366
+ "charts.chartFrame.summaryDetailLabel": "Chart summary",
367
+ "charts.legend.label": "Chart legend",
368
+ "charts.gantt.timeline": "Timeline",
369
+ "charts.gantt.dragToResizeColumn": "Drag to resize column",
370
+ "charts.gantt.viewControls": "Gantt view controls",
371
+ "charts.gantt.viewMode": "View mode",
372
+ "charts.gantt.viewModeLabel": "View mode:",
373
+ "charts.gantt.taskList": "Task list",
374
+ "charts.gantt.taskListLabel": "Tasks",
375
+ "charts.gantt.noTasksToDisplay": "No tasks to display",
376
+ "charts.gantt.unitDay": "Day",
377
+ "charts.gantt.unitWeek": "Week",
378
+ "charts.gantt.unitMonth": "Month",
379
+ "charts.gantt.unitQuarter": "Quarter",
380
+ "charts.gantt.unitHour": "Hour",
381
+ "charts.gantt.unitMinute": "Minute",
382
+ "charts.gantt.unitSecond": "Second",
383
+ "charts.gantt.unitMillisecond": "Millisecond",
384
+
385
+ // ── @elabs-ai/components-maps ─────────────────────────────────────────────────────────────
386
+ "maps.popup.close": "Close popup",
387
+ "maps.canvas.unavailableTitle": "Map unavailable",
388
+ "maps.canvas.unavailableDescription": "This browser can’t render WebGL maps.",
389
+
390
+ // ── @elabs-ai/components-flow ─────────────────────────────────────────────────────────────
391
+ "flow.inspectorPanel.close": "Close inspector",
392
+ "flow.inspectorPanel.title": "Inspector",
393
+ "flow.inspectorPanel.emptyMessage": "Select a node to see its details.",
108
394
 
109
395
  // ── @elabs-ai/components-ai ───────────────────────────────────────────────────────────────
110
396
  // Namespaced `ai.<area>.<key>` so package microcopy can't collide with the
@@ -120,25 +406,71 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
120
406
  "ai.promptInput.uploadFiles": "Upload files",
121
407
  "ai.promptInput.submit": "Submit",
122
408
  "ai.promptInput.stop": "Stop",
409
+ "ai.agent.instructions": "Instructions",
410
+ "ai.agent.tools": "Tools",
411
+ "ai.agent.outputSchema": "Output Schema",
123
412
  "ai.codeBlock.generating": "Generating…",
124
413
  "ai.composer.placeholder": "Ask me anything…",
125
- "ai.context.usage": "Model context usage",
414
+ "ai.tokenUsage.usage": "Model context usage",
415
+ "ai.tokenUsage.totalCost": "Total cost",
416
+ "ai.tokenUsage.input": "Input",
417
+ "ai.tokenUsage.output": "Output",
418
+ "ai.tokenUsage.reasoning": "Reasoning",
419
+ "ai.tokenUsage.cache": "Cache",
126
420
  "ai.contextPanel.back": "Back to context",
127
421
  "ai.contextPanel.toggle": "Toggle context panel",
422
+ // The mobile Sheet's sr-only title (#18) — distinct from `contextPanel.toggle`,
423
+ // which labels the button that opens/closes it.
424
+ "ai.contextPanel.title": "Context panel",
425
+ "ai.contextPanel.description": "Displays the chat context panel.",
128
426
  "ai.environmentVariables.toggleVisibility": "Toggle value visibility",
129
427
  "ai.gallery.label": "Image gallery",
130
428
  "ai.gallery.expandImage": "Expand image",
131
429
  "ai.gallery.downloadImage": "Download image",
430
+ "ai.gallery.noImages": "No images",
431
+ "ai.gallery.noDetails": "No details",
432
+ // Shared by Tool's technical view and SchemaDisplay's request panel.
433
+ "ai.schemaDisplay.parameters": "Parameters",
434
+ "ai.schemaDisplay.response": "Response",
435
+ "ai.schemaDisplay.requestBody": "Request Body",
436
+ // ReasoningTrigger's default not-yet-timed message (before `duration` is known).
437
+ "ai.reasoning.thoughtDefault": "Thought for a few seconds",
438
+ "ai.reasoning.thinking": "Thinking…",
439
+ "ai.reasoning.thoughtForDuration": "Thought for {duration} seconds",
440
+ "ai.stackTrace.empty": "No stack frames",
441
+ "ai.webPreview.noConsoleOutput": "No console output",
442
+ // PlanTrigger's icon-only collapse/expand control (sr-only).
443
+ "ai.plan.togglePlan": "Toggle plan",
132
444
  "ai.message.actions": "Message actions",
133
445
  "ai.message.editMessage": "Edit message",
134
446
  "ai.message.feedback": "Message feedback",
135
447
  "ai.message.previousBranch": "Previous branch",
136
448
  "ai.message.nextBranch": "Next branch",
449
+ "ai.messageCompare.error": "Error",
450
+ "ai.messageCompare.tabs": "Compare responses",
137
451
  "ai.persona.idle": "Assistant idle",
138
452
  "ai.persona.listening": "Assistant listening",
139
453
  "ai.persona.thinking": "Assistant thinking…",
140
454
  "ai.persona.speaking": "Assistant speaking",
141
455
  "ai.persona.asleep": "Assistant asleep",
456
+ // Optional-peer lazy engines (issue #33) — one shared pair every lazy
457
+ // boundary's missing-dependency panel resolves through, plus one
458
+ // `feature`-name key per boundary that renders it (mermaid, the terminal).
459
+ "ai.error.engineMissing": "{feature} unavailable",
460
+ "ai.error.engineMissingBody": "{feature} needs {packages} to be installed.",
461
+ "ai.error.retry": "Try again",
462
+ "ai.mermaid.feature": "Mermaid diagrams",
463
+ "ai.mermaid.renderError": "Diagram couldn't be drawn",
464
+ "ai.terminal.feature": "Interactive terminal",
465
+ "ai.terminal.renderError": "Terminal couldn't start",
466
+ "ai.audioPlayer.feature": "Audio player",
467
+ "ai.audioPlayer.renderError": "Audio player couldn't load",
468
+ // AudioVisualizer. Announced through a throttled `role="status"` region —
469
+ // the canvas itself is decorative (see .claude/rules/loading-states.md and
470
+ // issue #21's accessibility guidance).
471
+ "ai.audioVisualizer.idle": "Microphone not connected",
472
+ "ai.audioVisualizer.silent": "No input detected",
473
+ "ai.audioVisualizer.active": "Microphone active",
142
474
  "ai.selectionToolbar.label": "Selection actions",
143
475
  "ai.webPreview.urlPlaceholder": "Enter URL...",
144
476
  "ai.micSelector.searchPlaceholder": "Search microphones...",
@@ -148,6 +480,60 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
148
480
  "ai.voiceSelector.playPreview": "Play preview",
149
481
  "ai.voiceSelector.pausePreview": "Pause preview",
150
482
 
483
+ // TurnStatus / SessionStatusBar (#105). `label` itself is caller-supplied
484
+ // ("Working…", "Editing files…") and rendered verbatim, so it needs no key
485
+ // here — only the component-owned completed-turn sentence and controls do.
486
+ "ai.turnStatus.completedIn": "Turn completed in {elapsed}",
487
+ "ai.turnStatus.completed": "Turn completed",
488
+ "ai.turnStatus.scrollToBottom": "Scroll to bottom",
489
+ "ai.sessionStatusBar.connections": "{connected} of {total} connections",
490
+ "ai.sessionStatusBar.connecting": "Connecting…",
491
+ // SessionHeader (#110). The section headings and the quick-action group name
492
+ // are component-owned chrome; the capability/what's-new item text itself is
493
+ // caller-supplied and rendered verbatim.
494
+ "ai.sessionHeader.capabilities": "Capabilities",
495
+ "ai.sessionHeader.whatsNew": "What’s new",
496
+ "ai.sessionHeader.quickActions": "Quick actions",
497
+ // PermissionModeSelect (#104). The in-force marker is a WORD inside the mode
498
+ // label, so the current mode survives greyscale and reaches the accessible name.
499
+ "ai.permissionModeSelect.current": "Current",
500
+ // AgentEvent (#109). The pass/fail WORD is the non-colour channel — the tone
501
+ // is redundant with it, so a check outcome survives greyscale (WCAG 1.4.1).
502
+ "ai.agentEvent.checkPassed": "Passed",
503
+ "ai.agentEvent.checkFailed": "Failed",
504
+ "ai.agentEvent.checksSummary": "{passed}/{ran} checks passed",
505
+ "ai.agentEvent.phaseBefore": "Before",
506
+ "ai.agentEvent.phaseAfter": "After",
507
+ "ai.agentEvent.phaseLifecycle": "Lifecycle",
508
+ // DiffView (#102). `addedLine` / `removedLine` are the sr-only polarity
509
+ // prefixes — the +/− glyph is aria-hidden, so these WORDS are the channel a
510
+ // greyscale or screen-reader user recovers the polarity from (WCAG 1.4.1).
511
+ // They intentionally end in a space so they read as a prefix to the code line.
512
+ "ai.diffView.addedLine": "Added: ",
513
+ "ai.diffView.removedLine": "Removed: ",
514
+ "ai.diffView.statsSummary": "{additions} additions, {deletions} deletions",
515
+ "ai.diffView.showMore": { one: "Show {count} more line", other: "Show {count} more lines" },
516
+ "ai.diffView.pagerLegend": "Arrow keys scroll, Page Up/Down page, Home/End jump",
517
+ "ai.diffView.regionLabel": "Code diff",
518
+ "ai.diffView.loading": "Loading diff…",
519
+ // ApprovalCard (#103). The SCOPE sentence is what makes an N-option
520
+ // permission prompt safe to answer: "Yes" and "Yes, and don't ask again" look
521
+ // alike and mean very different things. Each option links its scope sentence
522
+ // through aria-describedby, so the blast radius of a choice reaches assistive
523
+ // tech as words — never as a colour or a data-* attribute.
524
+ "ai.approvalCard.scopeOnceDescription": "Applies to this action only.",
525
+ "ai.approvalCard.scopeSessionDescription":
526
+ "Applies to actions like this for the rest of this session.",
527
+ "ai.approvalCard.scopeAlwaysDescription": "Applies to actions like this from now on.",
528
+ "ai.approvalCard.scopeDenyDescription": "Rejects this action.",
529
+ "ai.approvalCard.reasonLabel": "Reason",
530
+ "ai.approvalCard.reasonPlaceholder": "Add a reason (optional)…",
531
+ // PromptInputSlash (#106). `listLabel` goes to cmdk's own `label` prop, not
532
+ // `aria-label` — cmdk overwrites a consumer `aria-label` on CommandList and
533
+ // reads its accessible name off `label` instead.
534
+ "ai.promptInputSlash.listLabel": "Commands",
535
+ "ai.promptInputSlash.empty": "No matching commands.",
536
+
151
537
  // ── Streamdown chrome (third-party rendering surface) ──────────────────────
152
538
  // `streamdown` renders its OWN controls inside every streamed-markdown block
153
539
  // (code header, table menus, Mermaid toolbar, external-link interstitial) and
@@ -195,6 +581,185 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
195
581
  "ai.streamdown.copyLink": "Copy link",
196
582
  "ai.streamdown.copied": "Copied",
197
583
  "ai.streamdown.openLink": "Open link",
584
+ // Tool (#4 i18n sweep).
585
+ "ai.tool.showTechnicalDetails": "Show technical details",
586
+ "ai.tool.error": "Error",
587
+ "ai.tool.result": "Result",
588
+ // AssetPreview.
589
+ "ai.assetPreview.rowCount": { one: "{count} row", other: "{count} rows" },
590
+ "ai.assetPreview.noPreview": "No preview available…",
591
+ "ai.assetPreview.preview": "Preview",
592
+ "ai.assetPreview.raw": "Raw",
593
+ // WebPreview's sandboxed iframe accessible name.
594
+ "ai.webPreview.previewTitle": "Preview",
595
+ // FileTree's expand/collapse chevron control.
596
+ "ai.fileTree.expandFolder": "Expand {name}",
597
+ "ai.fileTree.collapseFolder": "Collapse {name}",
598
+ "ai.fileTree.noAssetsProduced": "No assets produced yet.",
599
+ "ai.conversation.download": "Download conversation",
600
+ // OpenInChat. `provider` is a brand name (ChatGPT, Claude, …) — kept
601
+ // untranslated data, interpolated into the translated "Open in …" phrase.
602
+ "ai.openInChat.trigger": "Open in chat",
603
+ "ai.openInChat.openInProvider": "Open in {provider}",
604
+ // TestResults.
605
+ "ai.testResults.passedCount": { one: "{count} passed", other: "{count} passed" },
606
+ "ai.testResults.failedCount": { one: "{count} failed", other: "{count} failed" },
607
+ "ai.testResults.skippedCount": { one: "{count} skipped", other: "{count} skipped" },
608
+ "ai.testResults.testsPassed": "{passed}/{total} tests passed",
609
+
610
+ // ── @elabs-ai/components-editor ───────────────────────────────────────────
611
+ // DecisionCard (ai-objects). Status vocabulary + the card's own accessible name
612
+ // and the "Alternatives considered" section (shared by its heading and its list's
613
+ // aria-label).
614
+ "editor.decisionCard.statusAccepted": "Accepted",
615
+ "editor.decisionCard.statusRejected": "Rejected",
616
+ "editor.decisionCard.statusProposed": "Proposed",
617
+ "editor.decisionCard.statusSuperseded": "Superseded",
618
+ "editor.decisionCard.label": "Decision: {label}",
619
+ "editor.decisionCard.alternativesConsidered": "Alternatives considered",
620
+ // KnowledgeCard (ai-objects).
621
+ "editor.knowledgeCard.label": "Knowledge fact",
622
+ "editor.knowledgeCard.heading": "Knowledge",
623
+ "editor.knowledgeCard.sources": "Sources",
624
+ "editor.knowledgeCard.source": "Source: {name}",
625
+ "editor.knowledgeCard.sourceUnresolved": "Source (unresolved): {name}",
626
+ // CalcBlock. `equals` is the sr-only prefix read before a computed value —
627
+ // intentionally ends in a space so it reads as a prefix (mirrors `ai.diffView.addedLine`).
628
+ "editor.calcBlock.error": "Error: {message}",
629
+ "editor.calcBlock.equals": "equals ",
630
+ "editor.calcBlock.total": "Total",
631
+ "editor.calcBlock.emptyBlock": "Empty calc block.",
632
+ // CopyButton. The "Copy" state reuses the shared generic `copy` key.
633
+ "editor.copyButton.copied": "Copied",
634
+ // EditorToolbar.
635
+ "editor.editorToolbar.language": "Language",
636
+ // Citations (markdown-academic).
637
+ "editor.citations.citationLabel": "Citation: {name}",
638
+ "editor.citations.unresolvedCitation": "Unresolved citation",
639
+ "editor.citations.unresolvedKey": "Unresolved: @{key}",
640
+ "editor.citations.references": "References",
641
+ // Math (markdown-academic).
642
+ "editor.math.renderError": "Could not render math",
643
+ "editor.math.renderErrorLabel": "Math (could not render): {tex}",
644
+ // CompletionMenu (markdown-editor).
645
+ "editor.completions.suggestions": "Suggestions",
646
+ "editor.completions.noSuggestions": "No suggestions",
647
+ // SlashMenu (markdown-editor).
648
+ "editor.slashMenu.insertBlock": "Insert block",
649
+ "editor.slashMenu.noMatchingBlocks": "No matching blocks",
650
+ // TableControlsView (markdown-editor/table-view — the WYSIWYG GFM table toolbar).
651
+ "editor.tableView.tableControls": "Table controls",
652
+ "editor.tableView.row": "Row",
653
+ "editor.tableView.col": "Col",
654
+ "editor.tableView.addRowAbove": "Add row above",
655
+ "editor.tableView.addRowBelow": "Add row below",
656
+ "editor.tableView.deleteRow": "Delete row",
657
+ "editor.tableView.addColumnLeft": "Add column left",
658
+ "editor.tableView.addColumnRight": "Add column right",
659
+ "editor.tableView.deleteColumn": "Delete column",
660
+ // Directive node-views (markdown-editor/directive-views — Milkdown WYSIWYG).
661
+ "editor.directiveViews.cardTitle": "Card title",
662
+ "editor.directiveViews.calloutTitle": "Callout title",
663
+ "editor.directiveViews.unknownBlock": "Unknown block: {name}",
664
+ "editor.directiveViews.unknownInlineBlock": "Unknown inline block:",
665
+ "editor.directiveViews.metricLabel": "Metric label",
666
+ "editor.directiveViews.metricLabelPlaceholder": "Label",
667
+ "editor.directiveViews.metricValue": "Metric value",
668
+ "editor.directiveViews.metricValuePlaceholder": "0",
669
+ "editor.directiveViews.editIteration": "Edit iteration…",
670
+ "editor.directiveViews.changeLayout": "Change layout",
671
+ "editor.directiveViews.transpose": "Transpose",
672
+ "editor.directiveViews.convertToStatic": "Convert to static",
673
+ "editor.directiveViews.needsEmbeddedValues": "— needs embedded values",
674
+ "editor.directiveViews.pivot": "Pivot",
675
+ "editor.directiveViews.iterate": "Iterate",
676
+ "editor.directiveViews.perItem": "· per {as}",
677
+ "editor.directiveViews.templateSuffix": "— template",
678
+ "editor.directiveViews.iterationActions": "Iteration actions",
679
+ "editor.directiveViews.iterationActionsTitle": "Iteration actions…",
680
+ // IterationBuilderDialog (markdown-iteration) — the guided iterate/pivot authoring modal.
681
+ "editor.iterationBuilder.pivotNoun": "pivot",
682
+ "editor.iterationBuilder.iterationNoun": "iteration",
683
+ "editor.iterationBuilder.editTitle": "Edit {noun}",
684
+ "editor.iterationBuilder.insertTitle": "Insert {noun}",
685
+ "editor.iterationBuilder.pivotDescription":
686
+ "Pick the row and column values, then write the per-cell template. The matrix below fills in live.",
687
+ "editor.iterationBuilder.iterationDescription":
688
+ "Add the list values, then write the per-row template. The result below fills in live.",
689
+ "editor.iterationBuilder.bindName": "Bind name",
690
+ "editor.iterationBuilder.bindNamePlaceholder": "item",
691
+ "editor.iterationBuilder.bindNameHintPrefix": "Use ",
692
+ "editor.iterationBuilder.bindNameHintSuffix": " in the template.",
693
+ "editor.iterationBuilder.rowValues": "Row values",
694
+ "editor.iterationBuilder.values": "Values",
695
+ "editor.iterationBuilder.valuePlaceholder": "Type a value, press Enter…",
696
+ "editor.iterationBuilder.columnValues": "Column values",
697
+ "editor.iterationBuilder.layout": "Layout",
698
+ "editor.iterationBuilder.perCellTemplate": "Per-cell template",
699
+ "editor.iterationBuilder.perRowTemplate": "Per-row template",
700
+ "editor.iterationBuilder.livePreview": "Live preview",
701
+ "editor.iterationBuilder.cancel": "Cancel",
702
+ "editor.iterationBuilder.save": "Save",
703
+ "editor.iterationBuilder.insert": "Insert",
704
+ // IterationTemplateDialog (markdown-iteration) — the lighter template-only modal.
705
+ "editor.templateDialog.editPivotTitle": "Edit pivot template",
706
+ "editor.templateDialog.editIterationTitle": "Edit iteration template",
707
+ "editor.templateDialog.descriptionPrefix": "The per-{unit} template. Use ",
708
+ "editor.templateDialog.descriptionMiddle": " placeholders (e.g. ",
709
+ "editor.templateDialog.descriptionSuffix":
710
+ ") — each is filled per {unit} when the block renders.",
711
+ "editor.templateDialog.editorLabel": "Iteration template editor",
712
+ "editor.templateDialog.cancel": "Cancel",
713
+ "editor.templateDialog.saveTemplate": "Save template",
714
+ // DocumentOutline (markdown-outline).
715
+ "editor.documentOutline.label": "Document outline",
716
+ "editor.documentOutline.empty": "No headings yet.",
717
+ // MarkdownToolbar (the source-pane formatting bar). Directive snippet labels
718
+ // are the Insert menu's fallback item text (`DIRECTIVE_SNIPPETS`); the snippet
719
+ // MARKDOWN they insert is example document content, not UI chrome, and stays
720
+ // English (see the `i18n-exempt` comments at each snippet).
721
+ "editor.markdownToolbar.label": "Markdown formatting",
722
+ "editor.markdownToolbar.bold": "Bold",
723
+ "editor.markdownToolbar.italic": "Italic",
724
+ "editor.markdownToolbar.inlineCode": "Inline code",
725
+ "editor.markdownToolbar.link": "Link",
726
+ "editor.markdownToolbar.headingLevel": "Heading level",
727
+ "editor.markdownToolbar.heading": "Heading",
728
+ "editor.markdownToolbar.headingLevelItem": "Heading {level}",
729
+ "editor.markdownToolbar.quote": "Quote",
730
+ "editor.markdownToolbar.bulletList": "Bullet list",
731
+ "editor.markdownToolbar.numberedList": "Numbered list",
732
+ "editor.markdownToolbar.divider": "Divider",
733
+ "editor.markdownToolbar.insertBlock": "Insert block",
734
+ "editor.markdownToolbar.insert": "Insert",
735
+ "editor.markdownToolbar.insertBrandBlock": "Insert brand block",
736
+ "editor.markdownToolbar.directiveCard": "Card",
737
+ "editor.markdownToolbar.directiveCallout": "Callout",
738
+ "editor.markdownToolbar.directiveMetric": "Metric",
739
+ "editor.markdownToolbar.directiveTimeline": "Timeline",
740
+ // MarkdownWorkspace's focus-writing toggle.
741
+ "editor.markdownWorkspace.focusWriting": "Focus writing",
742
+ "editor.markdownWorkspace.focus": "Focus",
743
+ "editor.markdownWorkspace.focusWritingHint": "Typewriter scrolling · inactive paragraphs dim",
744
+ // MermaidDiagram (the inline renderer).
745
+ "editor.mermaidDiagram.label": "Diagram",
746
+ "editor.mermaidDiagram.expand": "Expand diagram",
747
+ "editor.mermaidDiagram.downloadSvg": "Download diagram as SVG",
748
+ "editor.mermaidDiagram.copySource": "Copy diagram source",
749
+ "editor.mermaidDiagram.renderFailed": "Diagram failed to render",
750
+ "editor.mermaidDiagram.rendering": "Rendering diagram…",
751
+ // MermaidViewer (the expanded zoom/pan/search surface). `nodeCountHint` mirrors
752
+ // the ORIGINAL (pre-i18n) copy exactly, including its always-plural "nodes" —
753
+ // it is a static hint ("N nodes · type to filter"), not a real plural form, so
754
+ // it stays a plain interpolated string rather than a `PluralMessage`.
755
+ "editor.mermaidViewer.findPlaceholder": "Find in diagram…",
756
+ "editor.mermaidViewer.findLabel": "Find in diagram",
757
+ "editor.mermaidViewer.matchCount": { one: "{count} node", other: "{count} nodes" },
758
+ "editor.mermaidViewer.nodeCountHint": "{count} nodes · type to filter",
759
+ "editor.mermaidViewer.zoomOut": "Zoom out",
760
+ "editor.mermaidViewer.zoomIn": "Zoom in",
761
+ "editor.mermaidViewer.resetZoom": "Reset zoom to 100%",
762
+ "editor.mermaidViewer.fitDiagram": "Fit diagram",
198
763
 
199
764
  // ── @elabs-ai/components-viewer (ADR 0024) ─────────────────────────
200
765
  // FileViewer chrome. Every control here is icon-only, so these ARE the
@@ -297,4 +862,259 @@ export const DEFAULT_MESSAGES: Record<string, string> = {
297
862
  "viewer.highlight.previous": "Previous passage",
298
863
  "viewer.highlight.next": "Next passage",
299
864
  "viewer.highlight.count": "Passage {index} of {total}",
865
+
866
+ // ── @elabs-ai/components-terminal ─────────────────────────────────────────
867
+ // Terminal (the read-only ANSI log): the ONE live-region announcement for
868
+ // its `isStreaming` rung. The blinking cursor block is the only other
869
+ // streaming signal and it is purely visual, so without this a screen-reader
870
+ // user attached to a running build or deploy log gets no indication that
871
+ // anything is still arriving.
872
+ "terminal.output.streaming": "Streaming output…",
873
+ // TerminalTranscriptRow (#117 T2): the gutter's meaning as words, so the
874
+ // "who spoke / what it printed / did it fail" grammar survives greyscale
875
+ // and reaches assistive tech, not only the glyph + colour.
876
+ "terminal.transcriptRow.user": "Prompt",
877
+ "terminal.transcriptRow.agent": "Agent",
878
+ "terminal.transcriptRow.output": "Output",
879
+ "terminal.transcriptRow.error": "Error",
880
+ "terminal.transcriptRow.exitCode": "Exit {code}",
881
+ // TerminalTodoList (#117 T5): the three-state checklist's announced word
882
+ // per row — the second, non-colour channel beside the ✔ / ◼ / ◻ glyph and
883
+ // the strikethrough/bold treatment. Wording verified 2026-09-01 against
884
+ // Claude Code v2.1.207's own upstream state words, parentheses included.
885
+ "terminal.todoList.done": "(completed)",
886
+ "terminal.todoList.active": "(in progress)",
887
+ "terminal.todoList.pending": "(pending)",
888
+ // TerminalEventLine (#117 T6): the lifecycle/hook event line. `outcome*` is
889
+ // the sr-only word beside the (always aria-hidden) StatusIcon glyph —
890
+ // never omitted for the default "ok" case, so "succeeded" is exactly as
891
+ // recoverable as "failed". `hooksTotal`/`hooksResult` are the terminal's
892
+ // own literal `[hooks: …]` bracket vocabulary, verified live 2026-09-01
893
+ // against Grok CLI v0.2.93; `hooksFailed` is the sr-only, count-aware
894
+ // "N hooks failed" that makes a partial hook failure (e.g. `3/1`) read as
895
+ // bad without relying on colour.
896
+ "terminal.eventLine.outcomeOk": "Succeeded",
897
+ "terminal.eventLine.outcomeBlocked": "Blocked",
898
+ "terminal.eventLine.outcomeFailed": "Failed",
899
+ "terminal.eventLine.phaseBefore": "Before",
900
+ "terminal.eventLine.phaseAfter": "After",
901
+ "terminal.eventLine.phaseLifecycle": "Lifecycle",
902
+ "terminal.eventLine.hooksTotal": "[hooks: {total}]",
903
+ "terminal.eventLine.hooksResult": "[hooks: {ran}/{passed}]",
904
+ "terminal.eventLine.hooksFailed": {
905
+ one: "{count} hook failed",
906
+ other: "{count} hooks failed",
907
+ },
908
+ // TerminalWorking (#117 T3): the in-turn footer's default label and its two
909
+ // icon-only controls' accessible names. No generic "stop"/"scroll to
910
+ // bottom" key exists yet (only the ai-namespaced `ai.promptInput.stop` /
911
+ // `ai.turnStatus.scrollToBottom`), so these are minted under the package's
912
+ // own namespace rather than borrowed cross-package.
913
+ "terminal.working.label": "Waiting for response…",
914
+ "terminal.working.stop": "Stop",
915
+ "terminal.working.scrollToBottom": "Scroll to bottom",
916
+ // TerminalStatusBar (#117 T4): the ambient chrome row's accessible name and
917
+ // the sr-only words beside its aria-hidden numerals/glyphs. Mirrors
918
+ // `ai.sessionStatusBar.connections`' wording under this package's own
919
+ // namespace (cannot import the `ai` key — sibling packages never import
920
+ // each other, `.claude/rules/terminal-components.md` § Reuse means
921
+ // promotion). `disconnected` and `stepsComplete` ("steps complete") are
922
+ // verified live 2026-09-01 against Grok CLI v0.2.93.
923
+ "terminal.statusBar.label": "Session status",
924
+ "terminal.statusBar.connecting": "Connecting…",
925
+ "terminal.statusBar.connections": "{connected} of {total} connections",
926
+ "terminal.statusBar.disconnected": "Disconnected",
927
+ "terminal.statusBar.context": "{used} of {limit} context used",
928
+ "terminal.statusBar.stepsComplete": "{current} of {total} steps complete",
929
+ // TerminalBanner (#117 T7): the launch card above an empty transcript.
930
+ // Mirrors `ai.sessionHeader.*`'s wording under this package's own
931
+ // namespace — sibling packages never import each other's locale keys
932
+ // (`.claude/rules/terminal-components.md` § Reuse means promotion).
933
+ "terminal.banner.capabilities": "Capabilities",
934
+ "terminal.banner.whatsNew": "What’s new",
935
+ "terminal.banner.quickActions": "Quick actions",
936
+ // TerminalToolCall (#117 T8): the tool-call row's status word (announced
937
+ // beside the glyph via TerminalRow's gutterLabel — never colour alone),
938
+ // the result row's swapped heading (mirrors ToolOutput's own "Result"/
939
+ // "Error" heading swap, `@elabs-ai/components-ai`), and the expand
940
+ // trigger's accessible name. Unlike upstream's inert `"(ctrl+o to
941
+ // expand)"` hint text, this IS a real focusable control's label — no CLI
942
+ // chord required.
943
+ "terminal.toolCall.succeeded": "Succeeded",
944
+ "terminal.toolCall.failed": "Failed",
945
+ "terminal.toolCall.running": "Running",
946
+ "terminal.toolCall.result": "Result",
947
+ "terminal.toolCall.error": "Error",
948
+ "terminal.toolCall.expandHint": "Show details",
949
+ // TerminalDiffHunk (#117 T9): the header's fixed "Update ({file})" phrase,
950
+ // and the collapsed-context-run disclosure's count-aware label. Mirrors
951
+ // `ai.diffView.showMore`'s wording under this package's own namespace —
952
+ // sibling packages never import each other's locale keys
953
+ // (`.claude/rules/terminal-components.md` § Reuse means promotion). The
954
+ // per-line `add`/`del` polarity words are NOT duplicated here: they ride
955
+ // the shared `diffLineAccessibleLabel()` keys (`ai.diffView.addedLine` /
956
+ // `ai.diffView.removedLine`) above, the actual promoted channel.
957
+ "terminal.diffHunk.header": "Update ({file})",
958
+ "terminal.diffHunk.showMore": {
959
+ one: "Show {count} more line",
960
+ other: "Show {count} more lines",
961
+ },
962
+ // TerminalPermission (#117 T10): the per-call scoped approval prompt's
963
+ // default title/question and its three scoped option labels, verified
964
+ // live 2026-09-01 against Claude Code v2.1.207. The third option is the
965
+ // vendor-free `deny` scope: upstream names its own product in this label,
966
+ // ours reads "the agent" instead (#117 acceptance criterion). The reason
967
+ // field reuses `ai.approvalCard.reasonLabel`/`reasonPlaceholder` and the
968
+ // option descriptions reuse `APPROVAL_SCOPE_DESCRIPTION_KEYS` — both
969
+ // already generic, so nothing new is minted for either.
970
+ "terminal.permission.title": "Bash command",
971
+ "terminal.permission.question": "Do you want to proceed?",
972
+ "terminal.permission.optionOnce": "Yes",
973
+ "terminal.permission.optionSession": "Yes, and don’t ask again this session",
974
+ "terminal.permission.optionDeny": "No, and tell the agent what to do differently",
975
+ // TerminalComposer (#117 T11): the prompt composer's placeholder, its
976
+ // merged submit/stop affordance's accessible name (mirrors
977
+ // `ai.promptInput.submit`/`stop` under this package's own namespace —
978
+ // sibling packages never import each other's locale keys,
979
+ // `.claude/rules/terminal-components.md` § Reuse means promotion), the
980
+ // effort scale's default accessible name, and the shortcut-hint row's
981
+ // three default words.
982
+ "terminal.composer.placeholder": "Type your next instruction…",
983
+ "terminal.composer.submit": "Send",
984
+ "terminal.composer.stop": "Stop",
985
+ "terminal.composer.effort": "Effort",
986
+ "terminal.composer.shortcutSend": "send",
987
+ "terminal.composer.shortcutNewline": "newline",
988
+ "terminal.composer.shortcutCancel": "cancel",
989
+ // TerminalSlashMenu (#117 T12): the `/`-command palette's listbox
990
+ // accessible name (mirrors `ai.promptInputSlash.listLabel`'s wording under
991
+ // this package's own namespace — sibling packages never import each
992
+ // other's locale keys, `.claude/rules/terminal-components.md` § Reuse
993
+ // means promotion). The empty state reuses the generic `noResults` key
994
+ // rather than minting a second one.
995
+ "terminal.slashMenu.listLabel": "Commands",
996
+ // ── @elabs-ai/components-process ──────────────────────────────────────────────────────────
997
+ // ProcessMap (RM-051). Namespaced `process.<area>.<key>` like every other
998
+ // package; a sibling package's keys are never reused across the boundary.
999
+ // The table twin's column headers are here because that table IS the
1000
+ // accessible reading of the canvas — an untranslated header would leave a
1001
+ // screen-reader user with numbers and no measure name.
1002
+ "process.map.loading": "Discovering the process…",
1003
+ "process.map.empty": "No activities to map",
1004
+ "process.map.emptyBody":
1005
+ "This log has no events, or the abstraction hid every activity. Widen the abstraction or load a different log.",
1006
+ "process.map.label": "Process map",
1007
+ "process.map.filter": "Filter…",
1008
+ "process.map.activityCaption": "Activities — {metric} per activity",
1009
+ "process.map.transitionCaption": "Transitions — {metric} per directly-follows pair",
1010
+ "process.map.columnActivity": "Activity",
1011
+ "process.map.columnRole": "Role",
1012
+ "process.map.columnRework": "Rework",
1013
+ "process.map.columnFrom": "From",
1014
+ "process.map.columnTo": "To",
1015
+ "process.map.columnShape": "Shape",
1016
+ // State column (#373) — the table twin's own channel for the selection/filter state the
1017
+ // canvas already says through real text in its accessible names; the twin used to carry
1018
+ // it only as a `data-*` attribute, which reaches no user.
1019
+ "process.map.columnState": "State",
1020
+ // The State column's own CELL values (#413 review, PRRT_kwDOT6D7ts6gJX2C). #373 localized
1021
+ // the column HEADER but left every cell printing the literal English word — since that cell
1022
+ // is also the row's screen-reader channel for its selection/filter state
1023
+ // (`selectionStateLabel` below is the un-localized canonical word `process-map.tsx` maps
1024
+ // through these keys at the render site), a non-English `LocaleProvider` left both the
1025
+ // visible AND the accessible content untranslated. `"associated"` (the ordinary case) has
1026
+ // no key: it prints nothing, by design.
1027
+ "process.map.stateSelected": "Selected",
1028
+ "process.map.stateExcluded": "Excluded",
1029
+ // Filter-intent menu item ACCESSIBLE names (#346). A transition's menu offers the same
1030
+ // four intents once per endpoint (eight items total); the visible text
1031
+ // (`PROCESS_FILTER_INTENT_LABELS` in map-model.ts) stays unsuffixed and compact, so these
1032
+ // compose the activity into the item's real accessible name instead — the channel a
1033
+ // screen-reader user actually hears, and the one the visible-only text left ambiguous.
1034
+ "process.map.filterIntentWith": "Keep cases containing {activity}",
1035
+ "process.map.filterIntentWithout": "Keep cases without {activity}",
1036
+ "process.map.filterIntentStartsWith": "Keep cases starting with {activity}",
1037
+ "process.map.filterIntentEndsWith": "Keep cases ending with {activity}",
1038
+ // The map's own live-region summary (#375) — one polite `role="status"` announces what
1039
+ // the active selection/filter just changed, mirroring `AbstractionControls`' hidden-count
1040
+ // summary below: two independently-pluralized fragments, composed at the call site
1041
+ // (`process-map.tsx`) rather than merged into one message, for the same reason.
1042
+ "process.map.excludedActivities": {
1043
+ one: "{count} of {total} activity excluded",
1044
+ other: "{count} of {total} activities excluded",
1045
+ },
1046
+ "process.map.excludedTransitions": {
1047
+ one: "{count} of {total} transition excluded",
1048
+ other: "{count} of {total} transitions excluded",
1049
+ },
1050
+ // The live region's AFFECTED-SET fragments (#413 review, PRRT_kwDOT6D7ts6gJX2I). Counts
1051
+ // alone are not enough: two selections/filters that exclude DIFFERENT elements but land on
1052
+ // the SAME counts produce byte-for-byte identical text, so the polite region — which
1053
+ // announces content CHANGES, not model changes — stays silent even though the map
1054
+ // re-inked. Naming which elements are excluded makes the string track the actual set, not
1055
+ // only its size. Composed at the call site alongside the two counts above, and omitted
1056
+ // entirely when nothing of that kind is excluded (so the common case stays as short as
1057
+ // before this fix).
1058
+ "process.map.excludedActivityNames": "excluding {names}",
1059
+ "process.map.excludedTransitionNames": "excluding {names}",
1060
+ // AbstractionControls (RM-052, issue #227). The two sliders' own labels,
1061
+ // the invert switch, the "Auto" heuristic button, and the hidden-count
1062
+ // status line. `hiddenActivities`/`hiddenPaths` are separate `PluralMessage`s
1063
+ // (not one combined key) because `t()` selects its plural category from a
1064
+ // SINGLE `count` var — see `resolveMessage` in `locale-provider.tsx` — so
1065
+ // the two independently-pluralizable counts are composed as two `t()` calls
1066
+ // joined at the call site (`abstraction-controls.tsx`), not one key with two
1067
+ // numbers.
1068
+ "process.abstractionControls.label": "Abstraction",
1069
+ "process.abstractionControls.activities": "Activities",
1070
+ "process.abstractionControls.paths": "Paths",
1071
+ "process.abstractionControls.invert": "Invert",
1072
+ "process.abstractionControls.auto": "Auto",
1073
+ "process.abstractionControls.hiddenActivities": {
1074
+ one: "{count} activity hidden",
1075
+ other: "{count} activities hidden",
1076
+ },
1077
+ "process.abstractionControls.hiddenPaths": {
1078
+ one: "{count} path hidden",
1079
+ other: "{count} paths hidden",
1080
+ },
1081
+ // MetricLayerSwitch (RM-052, issue #227). The Frequency/Performance/Rework
1082
+ // toggle group, the node/edge metric selects' field labels, and the lock
1083
+ // toggle's two accessible-name states. The per-VALUE metric labels
1084
+ // ("Occurrences", "Share of transitions", …) are deliberately NOT
1085
+ // duplicated here — they are read straight from `nodeMetricLabel`/
1086
+ // `edgeMetricLabel` (`process-map/map-model.ts`), which already resolve
1087
+ // each `ProcessMetric` to its correct, audience-specific text; see
1088
+ // `metric-layer-switch.tsx`'s module docblock.
1089
+ "process.metricLayerSwitch.label": "Metric",
1090
+ "process.metricLayerSwitch.layer": "Metric layer",
1091
+ "process.metricLayerSwitch.frequency": "Frequency",
1092
+ "process.metricLayerSwitch.performance": "Performance",
1093
+ "process.metricLayerSwitch.rework": "Rework",
1094
+ "process.metricLayerSwitch.node": "Activity",
1095
+ "process.metricLayerSwitch.edge": "Transition",
1096
+ "process.metricLayerSwitch.lockOn": "Unlock activity and transition metrics",
1097
+ "process.metricLayerSwitch.lockOff": "Lock activity and transition metrics together",
1098
+ // ProcessKpiStrip (RM-052, issue #227). Six tiles' labels, plus the
1099
+ // conformance tile's genuine "not available" state (no conformance model
1100
+ // has run yet) — a real state, not a fabricated 0%.
1101
+ "process.kpiStrip.cases": "Cases",
1102
+ "process.kpiStrip.events": "Events",
1103
+ "process.kpiStrip.variants": "Variants",
1104
+ "process.kpiStrip.medianThroughput": "Median throughput",
1105
+ "process.kpiStrip.reworkRate": "Rework rate",
1106
+ "process.kpiStrip.conformance": "Conformance",
1107
+ "process.kpiStrip.conformanceUnavailable": "Not available",
1108
+ "process.kpiStrip.conformanceUnavailableHint": "Run conformance checking to see this metric.",
1109
+ // Sparkline text alternative (#359). `Sparkline`'s own default alt text is more
1110
+ // informative than the tile's own label — passing the label straight through
1111
+ // overrode it and left a screen-reader user hearing the tile's name twice with no
1112
+ // sense of the trend. `direction` is itself a resolved `t()` value (trendRising/
1113
+ // Falling/Steady), composed into this template the same way `hiddenSummary`
1114
+ // (abstraction-controls.tsx) composes two already-localized fragments.
1115
+ "process.kpiStrip.trendAlt":
1116
+ "{subject}, {periods}-period trend, {direction} from {first} to {last}",
1117
+ "process.kpiStrip.trendRising": "rising",
1118
+ "process.kpiStrip.trendFalling": "falling",
1119
+ "process.kpiStrip.trendSteady": "steady",
300
1120
  };