@eifi1/ui-kit 0.53.0 → 0.55.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 (902) hide show
  1. package/README.md +96 -82
  2. package/dist/account/delete-account-setting.d.ts +3 -2
  3. package/dist/account/delete-account-setting.js.map +1 -1
  4. package/dist/admin/plan-change.js +2 -2
  5. package/dist/admin/plan-change.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +6 -0
  7. package/dist/admin/user-roster.js +4 -0
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/auth-errors.d.ts +36 -0
  10. package/dist/auth/auth-errors.js +20 -0
  11. package/dist/auth/auth-errors.js.map +1 -1
  12. package/dist/billing/billing-labels.d.ts +26 -0
  13. package/dist/billing/billing-labels.js +9 -0
  14. package/dist/billing/billing-labels.js.map +1 -1
  15. package/dist/billing/subscription-standing.d.ts +61 -0
  16. package/dist/billing/subscription-standing.js +51 -0
  17. package/dist/billing/subscription-standing.js.map +1 -0
  18. package/dist/billing/usage-meter.d.ts +52 -0
  19. package/dist/billing/usage-meter.js +52 -0
  20. package/dist/billing/usage-meter.js.map +1 -0
  21. package/dist/chart.d.ts +1 -0
  22. package/dist/chart.js +4 -0
  23. package/dist/chart.js.map +1 -1
  24. package/dist/components/alert-banner.js +2 -3
  25. package/dist/components/alert-banner.js.map +1 -1
  26. package/dist/components/amount-input.d.ts +2 -1
  27. package/dist/components/amount-input.js +3 -15
  28. package/dist/components/amount-input.js.map +1 -1
  29. package/dist/components/autocomplete.js +2 -2
  30. package/dist/components/autocomplete.js.map +1 -1
  31. package/dist/components/bulk-action-bar.js +2 -1
  32. package/dist/components/bulk-action-bar.js.map +1 -1
  33. package/dist/components/button.d.ts +35 -1
  34. package/dist/components/button.js +5 -9
  35. package/dist/components/button.js.map +1 -1
  36. package/dist/components/calendar-heatmap.js +6 -4
  37. package/dist/components/calendar-heatmap.js.map +1 -1
  38. package/dist/components/camera-button.d.ts +89 -0
  39. package/dist/components/camera-button.js +122 -0
  40. package/dist/components/camera-button.js.map +1 -0
  41. package/dist/components/camera-capture.d.ts +66 -0
  42. package/dist/components/camera-capture.js +190 -0
  43. package/dist/components/camera-capture.js.map +1 -0
  44. package/dist/components/camera-labels.d.ts +20 -0
  45. package/dist/components/camera-labels.js +15 -0
  46. package/dist/components/camera-labels.js.map +1 -0
  47. package/dist/components/chart-edge.d.ts +9 -0
  48. package/dist/components/chart-edge.js +14 -3
  49. package/dist/components/chart-edge.js.map +1 -1
  50. package/dist/components/chart-zoom-core.js +34 -29
  51. package/dist/components/chart-zoom-core.js.map +1 -1
  52. package/dist/components/checkbox-group.js +1 -1
  53. package/dist/components/checkbox-group.js.map +1 -1
  54. package/dist/components/combobox-core.js +3 -2
  55. package/dist/components/combobox-core.js.map +1 -1
  56. package/dist/components/combobox.js +2 -2
  57. package/dist/components/combobox.js.map +1 -1
  58. package/dist/components/consent-again-notice-labels.d.ts +25 -0
  59. package/dist/components/consent-again-notice-labels.js +15 -0
  60. package/dist/components/consent-again-notice-labels.js.map +1 -0
  61. package/dist/components/consent-again-notice.d.ts +85 -0
  62. package/dist/components/consent-again-notice.js +94 -0
  63. package/dist/components/consent-again-notice.js.map +1 -0
  64. package/dist/components/country-select.js +2 -7
  65. package/dist/components/country-select.js.map +1 -1
  66. package/dist/components/currency-option-list.d.ts +2 -1
  67. package/dist/components/currency-option-list.js +10 -3
  68. package/dist/components/currency-option-list.js.map +1 -1
  69. package/dist/components/currency-select.d.ts +3 -1
  70. package/dist/components/currency-select.js +2 -2
  71. package/dist/components/currency-select.js.map +1 -1
  72. package/dist/components/danger-confirm.d.ts +22 -1
  73. package/dist/components/danger-confirm.js +52 -30
  74. package/dist/components/danger-confirm.js.map +1 -1
  75. package/dist/components/data-table-column-settings.js +2 -2
  76. package/dist/components/data-table-column-settings.js.map +1 -1
  77. package/dist/components/data-table-filter-popover.d.ts +2 -1
  78. package/dist/components/data-table-filter-popover.js +3 -2
  79. package/dist/components/data-table-filter-popover.js.map +1 -1
  80. package/dist/components/data-table-filters.d.ts +33 -3
  81. package/dist/components/data-table-filters.js +6 -4
  82. package/dist/components/data-table-filters.js.map +1 -1
  83. package/dist/components/data-table-fit.js.map +1 -1
  84. package/dist/components/data-table-virtual.js +13 -3
  85. package/dist/components/data-table-virtual.js.map +1 -1
  86. package/dist/components/date-field.js +2 -2
  87. package/dist/components/date-field.js.map +1 -1
  88. package/dist/components/date-picker.js +2 -2
  89. package/dist/components/date-picker.js.map +1 -1
  90. package/dist/components/date-range-picker.js +2 -6
  91. package/dist/components/date-range-picker.js.map +1 -1
  92. package/dist/components/dialog-frame.d.ts +5 -1
  93. package/dist/components/dialog-frame.js +6 -2
  94. package/dist/components/dialog-frame.js.map +1 -1
  95. package/dist/components/dropdown.js +7 -3
  96. package/dist/components/dropdown.js.map +1 -1
  97. package/dist/components/entity-combobox.js +2 -2
  98. package/dist/components/entity-combobox.js.map +1 -1
  99. package/dist/components/field-parts.d.ts +108 -6
  100. package/dist/components/field-parts.js +59 -6
  101. package/dist/components/field-parts.js.map +1 -1
  102. package/dist/components/field-suggestion-labels.d.ts +2 -0
  103. package/dist/components/field-suggestion-labels.js +10 -0
  104. package/dist/components/field-suggestion-labels.js.map +1 -0
  105. package/dist/components/field.js +1 -1
  106. package/dist/components/field.js.map +1 -1
  107. package/dist/components/file-button.d.ts +1 -1
  108. package/dist/components/file-button.js.map +1 -1
  109. package/dist/components/floating-panel.d.ts +7 -6
  110. package/dist/components/floating-panel.js +3 -17
  111. package/dist/components/floating-panel.js.map +1 -1
  112. package/dist/components/form-actions.d.ts +3 -2
  113. package/dist/components/form-actions.js.map +1 -1
  114. package/dist/components/hover-menu.js +3 -2
  115. package/dist/components/hover-menu.js.map +1 -1
  116. package/dist/components/icon-button.d.ts +23 -5
  117. package/dist/components/icon-button.js +6 -8
  118. package/dist/components/icon-button.js.map +1 -1
  119. package/dist/components/inline-confirm.js +2 -1
  120. package/dist/components/inline-confirm.js.map +1 -1
  121. package/dist/components/inline-entity-combobox.js +2 -2
  122. package/dist/components/inline-entity-combobox.js.map +1 -1
  123. package/dist/components/input.js +2 -2
  124. package/dist/components/input.js.map +1 -1
  125. package/dist/components/jump-calendar.js +3 -3
  126. package/dist/components/jump-calendar.js.map +1 -1
  127. package/dist/components/legal.d.ts +13 -1
  128. package/dist/components/legal.js +12 -0
  129. package/dist/components/legal.js.map +1 -1
  130. package/dist/components/legended-series-chart.d.ts +80 -0
  131. package/dist/components/legended-series-chart.js +70 -0
  132. package/dist/components/legended-series-chart.js.map +1 -0
  133. package/dist/components/lightbox.js +2 -1
  134. package/dist/components/lightbox.js.map +1 -1
  135. package/dist/components/mini-calendar.js +16 -7
  136. package/dist/components/mini-calendar.js.map +1 -1
  137. package/dist/components/modal.d.ts +48 -0
  138. package/dist/components/modal.js +26 -6
  139. package/dist/components/modal.js.map +1 -1
  140. package/dist/components/month-picker.d.ts +1 -1
  141. package/dist/components/month-picker.js +6 -6
  142. package/dist/components/month-picker.js.map +1 -1
  143. package/dist/components/multi-entity-combobox.js +2 -2
  144. package/dist/components/multi-entity-combobox.js.map +1 -1
  145. package/dist/components/multi-select.js +2 -6
  146. package/dist/components/multi-select.js.map +1 -1
  147. package/dist/components/number-input.js +2 -2
  148. package/dist/components/number-input.js.map +1 -1
  149. package/dist/components/page-header-layout.d.ts +18 -0
  150. package/dist/components/page-header-layout.js +23 -0
  151. package/dist/components/page-header-layout.js.map +1 -0
  152. package/dist/components/page-header-skeleton.d.ts +41 -0
  153. package/dist/components/page-header-skeleton.js +52 -0
  154. package/dist/components/page-header-skeleton.js.map +1 -0
  155. package/dist/components/page-header.js +4 -17
  156. package/dist/components/page-header.js.map +1 -1
  157. package/dist/components/popover.js +7 -2
  158. package/dist/components/popover.js.map +1 -1
  159. package/dist/components/release-notes-labels.d.ts +16 -0
  160. package/dist/components/release-notes-labels.js +18 -0
  161. package/dist/components/release-notes-labels.js.map +1 -0
  162. package/dist/components/release-notes.d.ts +65 -0
  163. package/dist/components/release-notes.js +65 -0
  164. package/dist/components/release-notes.js.map +1 -0
  165. package/dist/components/select.js +2 -2
  166. package/dist/components/select.js.map +1 -1
  167. package/dist/components/series-chart-labels.d.ts +9 -0
  168. package/dist/components/series-chart-labels.js +3 -1
  169. package/dist/components/series-chart-labels.js.map +1 -1
  170. package/dist/components/series-chart.d.ts +5 -0
  171. package/dist/components/series-chart.js +89 -36
  172. package/dist/components/series-chart.js.map +1 -1
  173. package/dist/components/stat-tile.d.ts +21 -2
  174. package/dist/components/stat-tile.js +4 -2
  175. package/dist/components/stat-tile.js.map +1 -1
  176. package/dist/components/toggle-group.js +3 -2
  177. package/dist/components/toggle-group.js.map +1 -1
  178. package/dist/components/treemap-layout.js.map +1 -1
  179. package/dist/components/treemap.d.ts +17 -15
  180. package/dist/components/treemap.js +167 -85
  181. package/dist/components/treemap.js.map +1 -1
  182. package/dist/currencies.d.ts +16 -2
  183. package/dist/currencies.js +2 -1
  184. package/dist/currencies.js.map +1 -1
  185. package/dist/eslint/index.js +1 -1
  186. package/dist/eslint/role-utilities.d.ts +2 -2
  187. package/dist/eslint/role-utilities.js.map +1 -1
  188. package/dist/feedback/feedback-context-labels.js +2 -1
  189. package/dist/feedback/feedback-context-labels.js.map +1 -1
  190. package/dist/feedback/feedback-context.d.ts +19 -3
  191. package/dist/feedback/feedback-context.js +12 -1
  192. package/dist/feedback/feedback-context.js.map +1 -1
  193. package/dist/feedback/feedback-diagnostics.d.ts +17 -0
  194. package/dist/feedback/feedback-diagnostics.js +27 -0
  195. package/dist/feedback/feedback-diagnostics.js.map +1 -0
  196. package/dist/feedback/feedback-dialog.d.ts +10 -0
  197. package/dist/feedback/feedback-dialog.js +6 -4
  198. package/dist/feedback/feedback-dialog.js.map +1 -1
  199. package/dist/feedback/feedback-inbox-page.d.ts +260 -0
  200. package/dist/feedback/feedback-inbox-page.js +207 -0
  201. package/dist/feedback/feedback-inbox-page.js.map +1 -0
  202. package/dist/feedback/feedback-record.d.ts +5 -0
  203. package/dist/feedback/feedback-record.js.map +1 -1
  204. package/dist/feedback/feedback-row-detail-labels.js +1 -0
  205. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  206. package/dist/feedback/feedback-row-detail.d.ts +3 -0
  207. package/dist/feedback/feedback-row-detail.js +3 -0
  208. package/dist/feedback/feedback-row-detail.js.map +1 -1
  209. package/dist/feedback/feedback-submit-dialog.d.ts +5 -1
  210. package/dist/feedback/feedback-submit-dialog.js +15 -3
  211. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  212. package/dist/feedback/feedback-submit.d.ts +30 -2
  213. package/dist/feedback/feedback-submit.js +5 -2
  214. package/dist/feedback/feedback-submit.js.map +1 -1
  215. package/dist/feedback/feedback-vocabulary.js +2 -2
  216. package/dist/feedback/feedback-vocabulary.js.map +1 -1
  217. package/dist/feedback.d.ts +1 -0
  218. package/dist/feedback.js +6 -0
  219. package/dist/feedback.js.map +1 -1
  220. package/dist/hooks/use-debounce.d.ts +32 -5
  221. package/dist/hooks/use-debounce.js +6 -5
  222. package/dist/hooks/use-debounce.js.map +1 -1
  223. package/dist/hooks/use-file-picker.d.ts +3 -1
  224. package/dist/hooks/use-file-picker.js.map +1 -1
  225. package/dist/hooks/use-focus-trap.d.ts +19 -3
  226. package/dist/hooks/use-focus-trap.js +15 -5
  227. package/dist/hooks/use-focus-trap.js.map +1 -1
  228. package/dist/hooks/use-has-camera.d.ts +36 -0
  229. package/dist/hooks/use-has-camera.js +53 -0
  230. package/dist/hooks/use-has-camera.js.map +1 -0
  231. package/dist/hooks/use-hotkey.js +2 -1
  232. package/dist/hooks/use-hotkey.js.map +1 -1
  233. package/dist/hooks/use-overlay-history.d.ts +6 -1
  234. package/dist/hooks/use-overlay-history.js +10 -2
  235. package/dist/hooks/use-overlay-history.js.map +1 -1
  236. package/dist/i18n/defaults.js +11 -1
  237. package/dist/i18n/defaults.js.map +1 -1
  238. package/dist/i18n/german.js +78 -4
  239. package/dist/i18n/german.js.map +1 -1
  240. package/dist/i18n/kit-labels.d.ts +20 -0
  241. package/dist/i18n/kit-labels.js.map +1 -1
  242. package/dist/i18n/locales/en.js +95 -5
  243. package/dist/i18n/locales/en.js.map +3 -3
  244. package/dist/i18n/locales/es.js +74 -4
  245. package/dist/i18n/locales/es.js.map +1 -1
  246. package/dist/i18n/locales/fr.js +75 -3
  247. package/dist/i18n/locales/fr.js.map +1 -1
  248. package/dist/i18n/locales/hu.js +77 -4
  249. package/dist/i18n/locales/hu.js.map +1 -1
  250. package/dist/i18n/locales/it.js +77 -4
  251. package/dist/i18n/locales/it.js.map +1 -1
  252. package/dist/i18n/locales/zh.js +74 -4
  253. package/dist/i18n/locales/zh.js.map +1 -1
  254. package/dist/i18n/review.js +22 -1
  255. package/dist/i18n/review.js.map +1 -1
  256. package/dist/index.d.ts +27 -8
  257. package/dist/index.js +41 -2
  258. package/dist/index.js.map +1 -1
  259. package/dist/landing/landing-labels.d.ts +8 -0
  260. package/dist/landing/landing-labels.js +2 -1
  261. package/dist/landing/landing-labels.js.map +1 -1
  262. package/dist/lib/changelog.d.ts +130 -0
  263. package/dist/lib/changelog.js +137 -0
  264. package/dist/lib/changelog.js.map +1 -0
  265. package/dist/lib/clipboard-minus.js +2 -2
  266. package/dist/lib/clipboard-minus.js.map +1 -1
  267. package/dist/lib/dates.d.ts +36 -4
  268. package/dist/lib/dates.js +49 -13
  269. package/dist/lib/dates.js.map +1 -1
  270. package/dist/lib/editable-content.d.ts +33 -0
  271. package/dist/lib/editable-content.js +15 -0
  272. package/dist/lib/editable-content.js.map +1 -0
  273. package/dist/lib/focus-left.d.ts +47 -1
  274. package/dist/lib/focus-left.js +11 -2
  275. package/dist/lib/focus-left.js.map +1 -1
  276. package/dist/lib/format.d.ts +37 -2
  277. package/dist/lib/format.js +25 -1
  278. package/dist/lib/format.js.map +1 -1
  279. package/dist/lib/intl-cache.d.ts +4 -2
  280. package/dist/lib/intl-cache.js.map +1 -1
  281. package/dist/lib/server-wake.d.ts +49 -9
  282. package/dist/lib/server-wake.js +25 -14
  283. package/dist/lib/server-wake.js.map +1 -1
  284. package/dist/lib/session-refresh.d.ts +53 -0
  285. package/dist/lib/session-refresh.js +27 -1
  286. package/dist/lib/session-refresh.js.map +1 -1
  287. package/dist/lib/translation-review.d.ts +26 -0
  288. package/dist/lib/translation-review.js +14 -0
  289. package/dist/lib/translation-review.js.map +1 -1
  290. package/dist/rhf/fields.js +3 -3
  291. package/dist/rhf/fields.js.map +1 -1
  292. package/dist/shell/app-shell.d.ts +25 -1
  293. package/dist/shell/app-shell.js +3 -1
  294. package/dist/shell/app-shell.js.map +1 -1
  295. package/dist/shell/dev-menu.d.ts +3 -0
  296. package/dist/shell/dev-menu.js +4 -2
  297. package/dist/shell/dev-menu.js.map +1 -1
  298. package/dist/shell/held-changes-banner-labels.d.ts +23 -0
  299. package/dist/shell/held-changes-banner-labels.js +14 -0
  300. package/dist/shell/held-changes-banner-labels.js.map +1 -0
  301. package/dist/shell/held-changes-banner.d.ts +75 -0
  302. package/dist/shell/held-changes-banner.js +106 -0
  303. package/dist/shell/held-changes-banner.js.map +1 -0
  304. package/dist/shell/sync-status-indicator.d.ts +27 -12
  305. package/dist/shell/sync-status-indicator.js +13 -4
  306. package/dist/shell/sync-status-indicator.js.map +1 -1
  307. package/dist/shell/top-bar-parts-labels.d.ts +5 -0
  308. package/dist/shell/top-bar-parts-labels.js +3 -1
  309. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  310. package/dist/shell.d.ts +2 -0
  311. package/dist/shell.js +3 -0
  312. package/dist/shell.js.map +1 -1
  313. package/dist/sync/engine.d.ts +11 -2
  314. package/dist/sync/engine.js +13 -5
  315. package/dist/sync/engine.js.map +1 -1
  316. package/dist/sync/hooks.d.ts +3 -2
  317. package/dist/sync/hooks.js +4 -2
  318. package/dist/sync/hooks.js.map +1 -1
  319. package/dist/testing/css-variables.generated.d.ts +2 -2
  320. package/dist/testing/css-variables.generated.js +1 -1
  321. package/dist/testing/css-variables.generated.js.map +1 -1
  322. package/dist/theme/chart-palette.d.ts +18 -0
  323. package/dist/theme/chart-palette.js +17 -1
  324. package/dist/theme/chart-palette.js.map +1 -1
  325. package/dist/theme/contrast-tokens.d.ts +3 -3
  326. package/dist/theme/contrast-tokens.js.map +1 -1
  327. package/dist/theme/contrast.d.ts +4 -4
  328. package/dist/theme/contrast.js.map +1 -1
  329. package/dist/tour/tour-overlay.js +2 -1
  330. package/dist/tour/tour-overlay.js.map +1 -1
  331. package/package.json +5 -9
  332. package/tokens.css +19 -54
  333. package/src/account/account-parts.ts +0 -47
  334. package/src/account/account-settings-labels.ts +0 -240
  335. package/src/account/account-settings.tsx +0 -698
  336. package/src/account/data-export-setting-labels.ts +0 -20
  337. package/src/account/data-export-setting.tsx +0 -200
  338. package/src/account/delete-account-setting-labels.ts +0 -33
  339. package/src/account/delete-account-setting.tsx +0 -341
  340. package/src/account/email-change-setting-labels.ts +0 -31
  341. package/src/account/email-change-setting.tsx +0 -443
  342. package/src/account/passkeys-setting.tsx +0 -445
  343. package/src/account/sessions-setting-labels.ts +0 -24
  344. package/src/account/sessions-setting.tsx +0 -283
  345. package/src/admin/admin-action-confirm-labels.ts +0 -19
  346. package/src/admin/admin-action-confirm.tsx +0 -509
  347. package/src/admin/admin-action-log-labels.ts +0 -83
  348. package/src/admin/admin-action-log.tsx +0 -464
  349. package/src/admin/admin-area-labels.ts +0 -114
  350. package/src/admin/admin-forbidden.tsx +0 -75
  351. package/src/admin/admin-overview.tsx +0 -258
  352. package/src/admin/admin-parts.ts +0 -68
  353. package/src/admin/admin-sections.tsx +0 -208
  354. package/src/admin/admin-tool-links.tsx +0 -90
  355. package/src/admin/invitations-panel-labels.ts +0 -31
  356. package/src/admin/invitations-panel.tsx +0 -516
  357. package/src/admin/mail-test-card-labels.ts +0 -19
  358. package/src/admin/mail-test-card.tsx +0 -189
  359. package/src/admin/plan-change-labels.ts +0 -5
  360. package/src/admin/plan-change.tsx +0 -474
  361. package/src/admin/reviewer-scope-editor-labels.ts +0 -16
  362. package/src/admin/reviewer-scope-editor.tsx +0 -224
  363. package/src/admin/role-select-labels.ts +0 -16
  364. package/src/admin/role-select.tsx +0 -282
  365. package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
  366. package/src/admin/transfer-ownership-dialog.tsx +0 -162
  367. package/src/admin/user-roster-labels.ts +0 -17
  368. package/src/admin/user-roster.tsx +0 -559
  369. package/src/auth/accept-invitation-labels.ts +0 -21
  370. package/src/auth/accept-invitation.tsx +0 -210
  371. package/src/auth/auth-errors-labels.ts +0 -23
  372. package/src/auth/auth-errors.ts +0 -370
  373. package/src/auth/complete-name-dialog-labels.ts +0 -16
  374. package/src/auth/complete-name-dialog.tsx +0 -253
  375. package/src/auth/email-tag.ts +0 -75
  376. package/src/auth/forgot-password-form-labels.ts +0 -20
  377. package/src/auth/forgot-password-form.tsx +0 -221
  378. package/src/auth/form-rules.ts +0 -40
  379. package/src/auth/new-password-fields.tsx +0 -85
  380. package/src/auth/not-found-page-labels.ts +0 -12
  381. package/src/auth/not-found-page.tsx +0 -95
  382. package/src/auth/register-form-labels.ts +0 -31
  383. package/src/auth/register-form.tsx +0 -456
  384. package/src/auth/reset-password-form-labels.ts +0 -27
  385. package/src/auth/reset-password-form.tsx +0 -341
  386. package/src/auth/sign-in-form-labels.ts +0 -44
  387. package/src/auth/sign-in-form.tsx +0 -858
  388. package/src/auth/status-parts.tsx +0 -132
  389. package/src/auth/verify-email-labels.ts +0 -26
  390. package/src/auth/verify-email.tsx +0 -445
  391. package/src/billing/billing-banner.tsx +0 -258
  392. package/src/billing/billing-labels.ts +0 -381
  393. package/src/billing/billing-lock.ts +0 -108
  394. package/src/billing/billing-overview.ts +0 -157
  395. package/src/billing/billing-standing.ts +0 -80
  396. package/src/billing/cancel-request.ts +0 -97
  397. package/src/billing/cancel-subscription.tsx +0 -98
  398. package/src/billing/checkout-processing.ts +0 -394
  399. package/src/billing/checkout-return.ts +0 -90
  400. package/src/billing/pay-page.ts +0 -330
  401. package/src/billing/plan-card.tsx +0 -562
  402. package/src/billing/plan-change-labels.ts +0 -66
  403. package/src/billing/plan-limit-notice.tsx +0 -148
  404. package/src/billing/plan-limit-toast.tsx +0 -90
  405. package/src/billing/plan-limit.ts +0 -52
  406. package/src/billing/plan-price.ts +0 -98
  407. package/src/billing/plan-switch.tsx +0 -216
  408. package/src/billing/provider-page.ts +0 -84
  409. package/src/billing/subscription-actions.tsx +0 -247
  410. package/src/billing/subscription-status-chip.tsx +0 -95
  411. package/src/chart.ts +0 -103
  412. package/src/components/account-chips-labels.ts +0 -16
  413. package/src/components/account-chips.tsx +0 -474
  414. package/src/components/alert-banner.tsx +0 -670
  415. package/src/components/amount-input.tsx +0 -815
  416. package/src/components/appearance-labels.ts +0 -63
  417. package/src/components/authed-image-labels.ts +0 -11
  418. package/src/components/authed-image.tsx +0 -209
  419. package/src/components/autocomplete.tsx +0 -645
  420. package/src/components/breadcrumbs-labels.ts +0 -10
  421. package/src/components/breadcrumbs.tsx +0 -205
  422. package/src/components/bulk-action-bar-labels.ts +0 -11
  423. package/src/components/bulk-action-bar.tsx +0 -443
  424. package/src/components/button-group.tsx +0 -223
  425. package/src/components/button-look.ts +0 -256
  426. package/src/components/button.tsx +0 -339
  427. package/src/components/calculator.tsx +0 -341
  428. package/src/components/calendar-heatmap-labels.ts +0 -15
  429. package/src/components/calendar-heatmap.tsx +0 -594
  430. package/src/components/card.tsx +0 -377
  431. package/src/components/chart-edge.ts +0 -106
  432. package/src/components/chart-figure.tsx +0 -35
  433. package/src/components/chart-height.ts +0 -30
  434. package/src/components/chart-zoom-core.tsx +0 -627
  435. package/src/components/chart-zoom.tsx +0 -22
  436. package/src/components/chart.tsx +0 -554
  437. package/src/components/checkbox-group.tsx +0 -247
  438. package/src/components/checkbox.tsx +0 -346
  439. package/src/components/chip-input.tsx +0 -370
  440. package/src/components/chip-labels.ts +0 -13
  441. package/src/components/chip.tsx +0 -843
  442. package/src/components/choice-card.tsx +0 -544
  443. package/src/components/column-mapper-labels.ts +0 -38
  444. package/src/components/column-mapper.tsx +0 -655
  445. package/src/components/combobox-core.tsx +0 -1321
  446. package/src/components/combobox-suggestions.tsx +0 -131
  447. package/src/components/combobox.tsx +0 -716
  448. package/src/components/confirm-context.ts +0 -7
  449. package/src/components/confirm-dialog-labels.ts +0 -13
  450. package/src/components/confirm-dialog.tsx +0 -341
  451. package/src/components/copy-button-labels.ts +0 -13
  452. package/src/components/copy-button.tsx +0 -192
  453. package/src/components/country-select-labels.ts +0 -11
  454. package/src/components/country-select.tsx +0 -539
  455. package/src/components/currency-option-list.tsx +0 -180
  456. package/src/components/currency-select.tsx +0 -341
  457. package/src/components/danger-confirm-labels.ts +0 -20
  458. package/src/components/danger-confirm.tsx +0 -934
  459. package/src/components/data-table-cells.tsx +0 -118
  460. package/src/components/data-table-column-settings.tsx +0 -198
  461. package/src/components/data-table-filter-popover.tsx +0 -281
  462. package/src/components/data-table-filters.ts +0 -287
  463. package/src/components/data-table-fit.ts +0 -301
  464. package/src/components/data-table-groups.tsx +0 -288
  465. package/src/components/data-table-head.tsx +0 -280
  466. package/src/components/data-table-labels.ts +0 -198
  467. package/src/components/data-table-leading-row.tsx +0 -58
  468. package/src/components/data-table-mobile.tsx +0 -1117
  469. package/src/components/data-table-pagination.tsx +0 -202
  470. package/src/components/data-table-resize.ts +0 -188
  471. package/src/components/data-table-row-actions.tsx +0 -302
  472. package/src/components/data-table-row.tsx +0 -496
  473. package/src/components/data-table-selection.ts +0 -71
  474. package/src/components/data-table-shared.tsx +0 -287
  475. package/src/components/data-table-sort.ts +0 -163
  476. package/src/components/data-table-virtual.ts +0 -576
  477. package/src/components/data-table.tsx +0 -2598
  478. package/src/components/date-field.tsx +0 -684
  479. package/src/components/date-format-setting.tsx +0 -64
  480. package/src/components/date-picker.tsx +0 -404
  481. package/src/components/date-range-picker.tsx +0 -518
  482. package/src/components/description-list-labels.ts +0 -9
  483. package/src/components/description-list.tsx +0 -386
  484. package/src/components/dialog-frame-labels.ts +0 -7
  485. package/src/components/dialog-frame.tsx +0 -276
  486. package/src/components/disclosure.tsx +0 -476
  487. package/src/components/dropdown.tsx +0 -461
  488. package/src/components/empty-state.tsx +0 -136
  489. package/src/components/entity-combobox.tsx +0 -422
  490. package/src/components/error-boundary-labels.ts +0 -20
  491. package/src/components/error-boundary.tsx +0 -675
  492. package/src/components/facing-pair.ts +0 -93
  493. package/src/components/field-base-labels.ts +0 -20
  494. package/src/components/field-base.tsx +0 -656
  495. package/src/components/field-parts.tsx +0 -825
  496. package/src/components/field-strip.tsx +0 -129
  497. package/src/components/field.tsx +0 -206
  498. package/src/components/file-button.tsx +0 -295
  499. package/src/components/file-dropzone.tsx +0 -559
  500. package/src/components/floating-panel-labels.ts +0 -10
  501. package/src/components/floating-panel.tsx +0 -828
  502. package/src/components/focus-ring.ts +0 -16
  503. package/src/components/form-actions-labels.ts +0 -11
  504. package/src/components/form-actions.tsx +0 -578
  505. package/src/components/full-bleed-dialog.tsx +0 -294
  506. package/src/components/grouped-picker.tsx +0 -184
  507. package/src/components/help-popover.tsx +0 -117
  508. package/src/components/hover-menu.tsx +0 -343
  509. package/src/components/iban-input-labels.ts +0 -14
  510. package/src/components/iban-input.tsx +0 -236
  511. package/src/components/icon-button.tsx +0 -959
  512. package/src/components/icon-picker-labels.ts +0 -13
  513. package/src/components/icon-picker.tsx +0 -232
  514. package/src/components/image-grid-labels.ts +0 -11
  515. package/src/components/image-grid.tsx +0 -202
  516. package/src/components/inline-confirm.tsx +0 -116
  517. package/src/components/inline-edit-field-labels.ts +0 -11
  518. package/src/components/inline-edit-field.tsx +0 -552
  519. package/src/components/inline-entity-combobox.tsx +0 -849
  520. package/src/components/input.tsx +0 -394
  521. package/src/components/jump-calendar.tsx +0 -265
  522. package/src/components/language-select.tsx +0 -93
  523. package/src/components/legal-labels.ts +0 -105
  524. package/src/components/legal-page.tsx +0 -711
  525. package/src/components/legal.tsx +0 -668
  526. package/src/components/lightbox-labels.ts +0 -18
  527. package/src/components/lightbox.tsx +0 -399
  528. package/src/components/line-items-labels.ts +0 -14
  529. package/src/components/line-items.tsx +0 -602
  530. package/src/components/list-labels.ts +0 -12
  531. package/src/components/list.tsx +0 -870
  532. package/src/components/loading-state.tsx +0 -78
  533. package/src/components/measured-grid-labels.ts +0 -25
  534. package/src/components/measured-grid.tsx +0 -837
  535. package/src/components/menu-item.tsx +0 -241
  536. package/src/components/mini-calendar-labels.ts +0 -17
  537. package/src/components/mini-calendar.tsx +0 -789
  538. package/src/components/modal.tsx +0 -505
  539. package/src/components/money-draft.ts +0 -83
  540. package/src/components/money-field.tsx +0 -76
  541. package/src/components/month-grid.tsx +0 -229
  542. package/src/components/month-picker-labels.ts +0 -22
  543. package/src/components/month-picker.tsx +0 -1087
  544. package/src/components/multi-entity-combobox.tsx +0 -369
  545. package/src/components/multi-select.tsx +0 -376
  546. package/src/components/nav-pills.tsx +0 -198
  547. package/src/components/number-field.tsx +0 -327
  548. package/src/components/number-input.tsx +0 -558
  549. package/src/components/numeric-field.ts +0 -169
  550. package/src/components/numpad-sheet.tsx +0 -285
  551. package/src/components/one-time-code-input.tsx +0 -270
  552. package/src/components/page-contents-labels.ts +0 -9
  553. package/src/components/page-contents.tsx +0 -282
  554. package/src/components/page-header-context.ts +0 -8
  555. package/src/components/page-header.tsx +0 -298
  556. package/src/components/password-strength-labels.ts +0 -22
  557. package/src/components/password-strength.tsx +0 -241
  558. package/src/components/phone-input-labels.ts +0 -10
  559. package/src/components/phone-input.tsx +0 -282
  560. package/src/components/picker-sheet.tsx +0 -330
  561. package/src/components/pie-chart-labels.ts +0 -40
  562. package/src/components/pie-chart.tsx +0 -480
  563. package/src/components/popover-labels.ts +0 -14
  564. package/src/components/popover.tsx +0 -268
  565. package/src/components/progress-bar-labels.ts +0 -10
  566. package/src/components/progress-bar.tsx +0 -479
  567. package/src/components/qr-code.tsx +0 -76
  568. package/src/components/reauth-dialog-labels.ts +0 -13
  569. package/src/components/reauth-dialog.tsx +0 -265
  570. package/src/components/row-actions-labels.ts +0 -12
  571. package/src/components/row-actions.tsx +0 -795
  572. package/src/components/scroll-area.tsx +0 -123
  573. package/src/components/search-field.tsx +0 -192
  574. package/src/components/select.tsx +0 -294
  575. package/src/components/separator.tsx +0 -33
  576. package/src/components/series-chart-axes.ts +0 -342
  577. package/src/components/series-chart-budget.ts +0 -150
  578. package/src/components/series-chart-labels.ts +0 -33
  579. package/src/components/series-chart-thin.ts +0 -180
  580. package/src/components/series-chart-ticks.ts +0 -194
  581. package/src/components/series-chart.tsx +0 -1906
  582. package/src/components/server-wake-labels.ts +0 -11
  583. package/src/components/server-wake.tsx +0 -129
  584. package/src/components/settings-fields.tsx +0 -237
  585. package/src/components/share-card-labels.ts +0 -30
  586. package/src/components/share-card.tsx +0 -675
  587. package/src/components/signature-pad-labels.ts +0 -24
  588. package/src/components/signature-pad.tsx +0 -747
  589. package/src/components/signed-amount-labels.ts +0 -10
  590. package/src/components/signed-amount.tsx +0 -336
  591. package/src/components/skeleton.tsx +0 -80
  592. package/src/components/slider.tsx +0 -338
  593. package/src/components/sparkline-labels.ts +0 -14
  594. package/src/components/sparkline.tsx +0 -322
  595. package/src/components/spinner.tsx +0 -81
  596. package/src/components/stat-tile-labels.ts +0 -15
  597. package/src/components/stat-tile.tsx +0 -725
  598. package/src/components/status-dot.tsx +0 -170
  599. package/src/components/swatch-picker-labels.ts +0 -10
  600. package/src/components/swatch-picker.tsx +0 -283
  601. package/src/components/swipeable-row.tsx +0 -386
  602. package/src/components/switch.tsx +0 -273
  603. package/src/components/table-skeleton.tsx +0 -136
  604. package/src/components/table.tsx +0 -647
  605. package/src/components/tabs-labels.ts +0 -13
  606. package/src/components/tabs.tsx +0 -693
  607. package/src/components/text-link.tsx +0 -359
  608. package/src/components/text.tsx +0 -150
  609. package/src/components/textarea.tsx +0 -330
  610. package/src/components/tile-radio.tsx +0 -332
  611. package/src/components/time-input.tsx +0 -121
  612. package/src/components/toast-labels.ts +0 -12
  613. package/src/components/toast.tsx +0 -519
  614. package/src/components/toggle-group.tsx +0 -752
  615. package/src/components/toggle-legend.tsx +0 -394
  616. package/src/components/tooltip.tsx +0 -1149
  617. package/src/components/translation-review-editor.tsx +0 -290
  618. package/src/components/translation-review-labels.ts +0 -181
  619. package/src/components/translation-review-parts.tsx +0 -272
  620. package/src/components/translation-review.tsx +0 -1240
  621. package/src/components/tree-view.tsx +0 -589
  622. package/src/components/treemap-labels.ts +0 -47
  623. package/src/components/treemap-layout.ts +0 -210
  624. package/src/components/treemap.tsx +0 -1184
  625. package/src/components/trigger-aria.ts +0 -44
  626. package/src/components/use-anchor-dir.ts +0 -20
  627. package/src/components/use-mobile-reveal.ts +0 -62
  628. package/src/components/use-table-state.ts +0 -753
  629. package/src/components/user-avatar.tsx +0 -116
  630. package/src/components/wizard-stepper.tsx +0 -42
  631. package/src/components/write-lock-labels.ts +0 -10
  632. package/src/components/write-lock.tsx +0 -253
  633. package/src/currencies.ts +0 -70
  634. package/src/data-table.ts +0 -42
  635. package/src/demo/demo-banner.tsx +0 -153
  636. package/src/demo/demo-countdown.ts +0 -136
  637. package/src/demo/demo-ended.tsx +0 -90
  638. package/src/demo/demo-labels.ts +0 -78
  639. package/src/demo/demo-session.ts +0 -32
  640. package/src/demo/demo-start.tsx +0 -156
  641. package/src/eslint/index.ts +0 -161
  642. package/src/eslint/role-utilities.generated.ts +0 -146
  643. package/src/eslint/role-utilities.ts +0 -233
  644. package/src/feedback/feedback-attachment-download.tsx +0 -77
  645. package/src/feedback/feedback-attachment-labels.ts +0 -37
  646. package/src/feedback/feedback-attachment.tsx +0 -1239
  647. package/src/feedback/feedback-capture.ts +0 -287
  648. package/src/feedback/feedback-comment-thread.tsx +0 -353
  649. package/src/feedback/feedback-context-labels.ts +0 -10
  650. package/src/feedback/feedback-context.tsx +0 -213
  651. package/src/feedback/feedback-crash.ts +0 -547
  652. package/src/feedback/feedback-dialog-labels.ts +0 -29
  653. package/src/feedback/feedback-dialog.tsx +0 -440
  654. package/src/feedback/feedback-entry-file.tsx +0 -150
  655. package/src/feedback/feedback-inbox.tsx +0 -552
  656. package/src/feedback/feedback-labels.ts +0 -144
  657. package/src/feedback/feedback-menu-labels.ts +0 -12
  658. package/src/feedback/feedback-menu.tsx +0 -177
  659. package/src/feedback/feedback-mutation.ts +0 -20
  660. package/src/feedback/feedback-page-link.ts +0 -90
  661. package/src/feedback/feedback-public-labels.ts +0 -81
  662. package/src/feedback/feedback-public-list.tsx +0 -524
  663. package/src/feedback/feedback-public-switch.tsx +0 -102
  664. package/src/feedback/feedback-public-types.ts +0 -101
  665. package/src/feedback/feedback-record.ts +0 -423
  666. package/src/feedback/feedback-replies-context.ts +0 -9
  667. package/src/feedback/feedback-replies.tsx +0 -343
  668. package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
  669. package/src/feedback/feedback-row-detail-labels.ts +0 -30
  670. package/src/feedback/feedback-row-detail.tsx +0 -858
  671. package/src/feedback/feedback-status-undo.ts +0 -109
  672. package/src/feedback/feedback-submit-dialog.tsx +0 -118
  673. package/src/feedback/feedback-submit.tsx +0 -237
  674. package/src/feedback/feedback-swipe.tsx +0 -192
  675. package/src/feedback/feedback-table-labels.ts +0 -27
  676. package/src/feedback/feedback-table.tsx +0 -912
  677. package/src/feedback/feedback-thread-labels.ts +0 -22
  678. package/src/feedback/feedback-thread-parts-labels.ts +0 -41
  679. package/src/feedback/feedback-thread.tsx +0 -905
  680. package/src/feedback/feedback-vocabulary.ts +0 -263
  681. package/src/feedback/feedback-vote-button.tsx +0 -124
  682. package/src/feedback.ts +0 -116
  683. package/src/hooks/use-anchored-panel.ts +0 -396
  684. package/src/hooks/use-anchored-rect.ts +0 -77
  685. package/src/hooks/use-announce.ts +0 -85
  686. package/src/hooks/use-authed-src.ts +0 -167
  687. package/src/hooks/use-body-scroll-lock.ts +0 -119
  688. package/src/hooks/use-breakpoint.ts +0 -133
  689. package/src/hooks/use-close-transition.ts +0 -107
  690. package/src/hooks/use-copy-to-clipboard.ts +0 -122
  691. package/src/hooks/use-debounce.ts +0 -114
  692. package/src/hooks/use-dismiss.ts +0 -371
  693. package/src/hooks/use-document-title.ts +0 -33
  694. package/src/hooks/use-file-drop.ts +0 -121
  695. package/src/hooks/use-file-picker-labels.ts +0 -28
  696. package/src/hooks/use-file-picker.tsx +0 -359
  697. package/src/hooks/use-focus-trap.ts +0 -199
  698. package/src/hooks/use-hotkey.ts +0 -189
  699. package/src/hooks/use-large-text.ts +0 -41
  700. package/src/hooks/use-long-press.ts +0 -140
  701. package/src/hooks/use-media-query.ts +0 -39
  702. package/src/hooks/use-mounted.ts +0 -39
  703. package/src/hooks/use-noindex.ts +0 -37
  704. package/src/hooks/use-overlay-history.ts +0 -714
  705. package/src/hooks/use-row-swipe.ts +0 -207
  706. package/src/hooks/use-search-param-state.ts +0 -434
  707. package/src/hooks/use-windowed-rows.ts +0 -225
  708. package/src/i18n/defaults.ts +0 -267
  709. package/src/i18n/german.ts +0 -2057
  710. package/src/i18n/kit-labels.tsx +0 -1003
  711. package/src/i18n/languages.ts +0 -349
  712. package/src/i18n/locales/de-CH.ts +0 -24
  713. package/src/i18n/locales/en.ts +0 -267
  714. package/src/i18n/locales/es.ts +0 -1997
  715. package/src/i18n/locales/fr.ts +0 -2035
  716. package/src/i18n/locales/hu.ts +0 -2002
  717. package/src/i18n/locales/it.ts +0 -2012
  718. package/src/i18n/locales/zh.ts +0 -1910
  719. package/src/i18n/plurals.ts +0 -86
  720. package/src/i18n/review.ts +0 -475
  721. package/src/i18n/shared-labels.ts +0 -138
  722. package/src/i18n/swiss.ts +0 -29
  723. package/src/index.ts +0 -1179
  724. package/src/landing/access.ts +0 -93
  725. package/src/landing/landing-actions.tsx +0 -97
  726. package/src/landing/landing-labels.ts +0 -59
  727. package/src/landing/landing-sections.tsx +0 -341
  728. package/src/landing/page-seo.ts +0 -261
  729. package/src/landing/public-header.tsx +0 -121
  730. package/src/landing/public-page.tsx +0 -106
  731. package/src/landing/routing.tsx +0 -225
  732. package/src/lib/broadcast.ts +0 -52
  733. package/src/lib/calc.ts +0 -323
  734. package/src/lib/chip-edge-snap.ts +0 -212
  735. package/src/lib/clipboard-minus.ts +0 -107
  736. package/src/lib/clipping.ts +0 -34
  737. package/src/lib/cn.ts +0 -28
  738. package/src/lib/column-mapping.ts +0 -234
  739. package/src/lib/countries.ts +0 -91
  740. package/src/lib/data-attributes.ts +0 -14
  741. package/src/lib/date-format.ts +0 -227
  742. package/src/lib/dates.ts +0 -266
  743. package/src/lib/day-format.ts +0 -96
  744. package/src/lib/decimal-marks.ts +0 -108
  745. package/src/lib/describe-failure.ts +0 -18
  746. package/src/lib/device-settings.ts +0 -147
  747. package/src/lib/direction.ts +0 -74
  748. package/src/lib/document-navigation.ts +0 -17
  749. package/src/lib/document-title.ts +0 -41
  750. package/src/lib/download.ts +0 -172
  751. package/src/lib/float-clearance.ts +0 -70
  752. package/src/lib/floating-stack.ts +0 -70
  753. package/src/lib/focus-left.ts +0 -156
  754. package/src/lib/format.ts +0 -522
  755. package/src/lib/http-status.ts +0 -38
  756. package/src/lib/iban.ts +0 -251
  757. package/src/lib/ime.ts +0 -24
  758. package/src/lib/intl-cache.ts +0 -25
  759. package/src/lib/iso-day.ts +0 -13
  760. package/src/lib/lazy-with-fallback.ts +0 -82
  761. package/src/lib/logger.ts +0 -69
  762. package/src/lib/minus.ts +0 -50
  763. package/src/lib/path-pattern.ts +0 -55
  764. package/src/lib/person-name.ts +0 -155
  765. package/src/lib/phone.ts +0 -208
  766. package/src/lib/plain-text.ts +0 -22
  767. package/src/lib/presence.ts +0 -18
  768. package/src/lib/qr-encode.ts +0 -444
  769. package/src/lib/quiet-focus.ts +0 -32
  770. package/src/lib/reduced-motion.ts +0 -22
  771. package/src/lib/refs.ts +0 -22
  772. package/src/lib/round.ts +0 -43
  773. package/src/lib/safe-storage.ts +0 -119
  774. package/src/lib/server-wake.ts +0 -315
  775. package/src/lib/session-refresh.ts +0 -472
  776. package/src/lib/share-format.ts +0 -18
  777. package/src/lib/status-tone.ts +0 -43
  778. package/src/lib/strip-fade.ts +0 -287
  779. package/src/lib/table-text.ts +0 -554
  780. package/src/lib/text-case.ts +0 -19
  781. package/src/lib/text-match.ts +0 -169
  782. package/src/lib/thenable.ts +0 -19
  783. package/src/lib/translation-review.ts +0 -717
  784. package/src/notifications/device-zone-report.ts +0 -67
  785. package/src/notifications/feedback-notification-types-labels.ts +0 -33
  786. package/src/notifications/feedback-notification-types.ts +0 -71
  787. package/src/notifications/notification-bell.tsx +0 -119
  788. package/src/notifications/notification-inbox.tsx +0 -309
  789. package/src/notifications/notification-parts.tsx +0 -81
  790. package/src/notifications/notification-types-setting.tsx +0 -406
  791. package/src/notifications/notification-types.ts +0 -89
  792. package/src/notifications/notifications-labels.ts +0 -287
  793. package/src/notifications/push-device-setting.tsx +0 -230
  794. package/src/notifications/push-devices-setting.tsx +0 -374
  795. package/src/notifications/push-worker.ts +0 -168
  796. package/src/notifications/quiet-hours-setting.tsx +0 -238
  797. package/src/notifications/unsubscribe-panel.tsx +0 -200
  798. package/src/notifications/use-push-device.ts +0 -448
  799. package/src/offline/local-first.ts +0 -100
  800. package/src/offline/pwa-update-prompt-labels.ts +0 -9
  801. package/src/offline/pwa-update-prompt.tsx +0 -78
  802. package/src/offline/query-persistence.ts +0 -391
  803. package/src/offline/service-worker-updates.ts +0 -81
  804. package/src/offline/shared-computer-labels.ts +0 -8
  805. package/src/offline/shared-computer.tsx +0 -87
  806. package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
  807. package/src/offline/sign-out-pending-dialog.tsx +0 -135
  808. package/src/offline.ts +0 -37
  809. package/src/push-worker.ts +0 -25
  810. package/src/rhf/fields.tsx +0 -2187
  811. package/src/rhf/form-core.tsx +0 -324
  812. package/src/rhf/form.tsx +0 -12
  813. package/src/rhf/line-items.tsx +0 -135
  814. package/src/rhf/use-rhf-wizard-step.ts +0 -104
  815. package/src/rhf.ts +0 -21
  816. package/src/search/command-palette-labels.ts +0 -16
  817. package/src/search/command-palette.tsx +0 -624
  818. package/src/search/global-search-labels.ts +0 -13
  819. package/src/search/global-search.tsx +0 -660
  820. package/src/search/search-index.ts +0 -283
  821. package/src/search/use-command-key.ts +0 -23
  822. package/src/search.ts +0 -14
  823. package/src/settings/device-reset-setting-labels.ts +0 -20
  824. package/src/settings/device-reset-setting.tsx +0 -116
  825. package/src/settings/settings-card-classes.ts +0 -11
  826. package/src/settings/settings-catalogue.ts +0 -150
  827. package/src/settings/settings-context.ts +0 -33
  828. package/src/settings/settings-heading.tsx +0 -67
  829. package/src/settings/settings-labels.ts +0 -87
  830. package/src/settings/settings-layout.tsx +0 -471
  831. package/src/settings/settings-section.tsx +0 -108
  832. package/src/settings/use-account-appearance.ts +0 -148
  833. package/src/settings/use-account-language.ts +0 -146
  834. package/src/settings/use-settings-focus.ts +0 -128
  835. package/src/settings/use-settings-route.ts +0 -258
  836. package/src/shell/account-menu-language.tsx +0 -130
  837. package/src/shell/account-menu-text-size.tsx +0 -80
  838. package/src/shell/admin-menu.tsx +0 -183
  839. package/src/shell/app-shell-labels.ts +0 -11
  840. package/src/shell/app-shell.tsx +0 -1560
  841. package/src/shell/auth-layout.tsx +0 -186
  842. package/src/shell/company-switcher-labels.ts +0 -13
  843. package/src/shell/company-switcher.tsx +0 -136
  844. package/src/shell/dev-menu.tsx +0 -257
  845. package/src/shell/fill-height.tsx +0 -45
  846. package/src/shell/language-code.ts +0 -5
  847. package/src/shell/option-switcher-menu.tsx +0 -84
  848. package/src/shell/sync-status-indicator.tsx +0 -303
  849. package/src/shell/top-bar-actions.tsx +0 -87
  850. package/src/shell/top-bar-brand.tsx +0 -78
  851. package/src/shell/top-bar-parts-labels.ts +0 -148
  852. package/src/shell/top-bar.tsx +0 -40
  853. package/src/shell/topbar-action-menu.tsx +0 -567
  854. package/src/shell/topbar-controls.tsx +0 -269
  855. package/src/shell/tour-menu.tsx +0 -187
  856. package/src/shell.ts +0 -108
  857. package/src/sync/db.ts +0 -129
  858. package/src/sync/engine.ts +0 -1011
  859. package/src/sync/hooks.ts +0 -98
  860. package/src/sync/testing.ts +0 -73
  861. package/src/sync/wire.ts +0 -61
  862. package/src/sync.ts +0 -37
  863. package/src/table-text.ts +0 -8
  864. package/src/testing/css-variables.generated.ts +0 -112
  865. package/src/testing/css-variables.ts +0 -189
  866. package/src/testing/index.ts +0 -29
  867. package/src/testing/react.ts +0 -58
  868. package/src/testing/tour.ts +0 -41
  869. package/src/testing/viewport.ts +0 -357
  870. package/src/theme/chart-palette.ts +0 -232
  871. package/src/theme/color.ts +0 -272
  872. package/src/theme/contrast-tokens.ts +0 -268
  873. package/src/theme/contrast.ts +0 -250
  874. package/src/theme/palette-derive.ts +0 -825
  875. package/src/theme/palette-presets.ts +0 -544
  876. package/src/theme/palette-store.ts +0 -108
  877. package/src/theme/text-size.ts +0 -289
  878. package/src/theme/theme-store.ts +0 -157
  879. package/src/tour/tour-catalogue-labels.ts +0 -75
  880. package/src/tour/tour-catalogue.tsx +0 -335
  881. package/src/tour/tour-dock.ts +0 -134
  882. package/src/tour/tour-engine.ts +0 -37
  883. package/src/tour/tour-gates.ts +0 -218
  884. package/src/tour/tour-guide-button.tsx +0 -64
  885. package/src/tour/tour-guide-labels.ts +0 -15
  886. package/src/tour/tour-info.ts +0 -144
  887. package/src/tour/tour-labels.ts +0 -23
  888. package/src/tour/tour-overlay.tsx +0 -1170
  889. package/src/tour/tour.tsx +0 -750
  890. package/src/tour/use-tour-progress.ts +0 -671
  891. package/src/tour/use-tour-url-sync.ts +0 -266
  892. package/src/tour.ts +0 -21
  893. package/src/vite-env.d.ts +0 -23
  894. package/src/wizard/stepper-nav.tsx +0 -360
  895. package/src/wizard/types.ts +0 -275
  896. package/src/wizard/use-wizard.ts +0 -560
  897. package/src/wizard/validation.ts +0 -39
  898. package/src/wizard/wizard-context.tsx +0 -74
  899. package/src/wizard/wizard-labels.ts +0 -29
  900. package/src/wizard/wizard-step.tsx +0 -76
  901. package/src/wizard/wizard-summary.tsx +0 -84
  902. package/src/wizard.ts +0 -15
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/treemap.tsx"],"sourcesContent":["// A tile chart (\"treemap\"): one rectangle per node, its AREA the node's share of what\n// is drawn. Lifted out of keksdose's reports, where five cards drew it (spending by\n// group and by category, net worth by account and by type, allocation, payees, food\n// groups) — every domain word is gone and every string it could print is a prop.\n//\n// It uses the kit's own chart shell (`ChartContainer`, `ChartTooltipContent`) and the\n// kit's own ink rule (`textOn`), so it re-skins with the palette like every other\n// chart. The one thing it cannot take from a CSS var is the tile colour: the label ink\n// is MEASURED against the fill, and a `var(--chart-3)` string measures as nothing — see\n// `useChartTokenHex` below for how the concrete hex is found instead.\n//\n// Since 0.52 a node can hold `children`: a group, drawn as a labelled frame round its\n// tiles (keksdose dev #642, shops by chain). Where every node is drawn is the map's own\n// layout (treemap-layout.ts), not recharts': see \"Groups: a frame round their tiles\".\nimport {\n createContext,\n useCallback,\n useContext,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type { ComponentProps, ComponentPropsWithoutRef, KeyboardEvent, ReactElement, ReactNode } from \"react\";\nimport { Treemap as RechartsTreemap, useChartHeight, useChartWidth } from \"recharts\";\nimport { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from \"./chart\";\nimport { CHART_ROLES, PALETTE_HEX, PALETTE_SIZE, lerpHex, textOn } from \"../theme/chart-palette\";\nimport { resolveChartColor, useChartEdge, useEdgesInForce, type ChartEdges } from \"./chart-edge\";\nimport { parseHex } from \"../theme/color\";\nimport { dirOf, rovingTarget, type Direction } from \"../lib/direction\";\nimport { shareFormat } from \"../lib/share-format\";\nimport { cn } from \"../lib/cn\";\nimport { useTextSize } from \"../theme/text-size\";\nimport { chartHeightProps, type ChartHeight } from \"./chart-height\";\nimport { formatWithMinus } from \"../lib/minus\";\nimport { useKitLabels, useKitLocale, type LabelOverride } from \"../i18n/kit-labels\";\nimport { DEFAULT_TREEMAP_LABELS, type TreemapLabels } from \"./treemap-labels\";\nimport { groupBody, layoutTreemap, type Box, type Placed } from \"./treemap-layout\";\n\n/**\n * One tile.\n *\n * `value`, not keksdose's `total`: a treemap's areas are not always money (keksdose\n * already sizes one by transaction COUNT), and `value` is the word every other chart\n * in recharts uses for the number a mark is sized by. Migrating is a rename at the\n * mapping step — `{ id, name, value: row.total }`.\n */\nexport interface TreemapNode {\n /** Stable key, handed back by {@link TreemapProps.onNodeClick} and\n * {@link TreemapProps.nodeNote}. Names can collide; ids must not among a group's own\n * nodes (or the top's). A group and a tile inside it may share one — chain 12 and shop\n * 12 from two tables — and `onNodeClick`'s `groups` tells them apart (0.52). */\n id: string;\n name: string;\n /** The tile's area. A node whose value is not a positive finite number is NOT\n * drawn — an area cannot be negative, and recharts lays a negative size out as\n * nonsense rather than refusing it. Saying so is the caller's job: a group that\n * vanishes silently between two views of the same data is a bug report.\n *\n * A GROUP's own `value` is not read: its area is its drawn children's together. Pass\n * that sum — the field stays required so that every reader of a `TreemapNode[]` keeps\n * a `number`. */\n value: number;\n /** Overrides the by-index palette colour for this tile — e.g. to paint every child\n * in its parent group's colour, so the groups stay legible in one picture. A\n * concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured — so\n * does `CHART_ROLES.other` (`var(--chart-muted)`), the colour for a \"rest\" bucket,\n * which should not wrap round to a hue the reader matches to a real node;\n * anything else (another var, a `color-mix()`) gets `currentColor` ink, because\n * there is nothing to measure — set `labelColor` then.\n *\n * On a group, the colour of every tile inside it that sets none (0.52): keksdose's\n * \"hue = chain\" is a `fill` per chain. A group left without one takes the ramp by its\n * index, as a tile does, and hands that down; its frame is drawn in the page's own\n * colours whatever it is. */\n fill?: string;\n /** The label's ink, for a `fill` the kit cannot measure (a `var()` or `color-mix()`):\n * keksdose's recency plot fills with `color-mix(in srgb, var(--chart-1) N%,\n * transparent)`, where `currentColor` put dark page text on full-strength indigo\n * (~1.5:1). Wins over the measured ink when given. A tile that takes its group's\n * `fill` takes the group's `labelColor` with it; one with a `fill` of its own, or one\n * that `shade` turns into another colour, is measured again. */\n labelColor?: string;\n /**\n * The tile's colour strength, 0–1 (0.52): its `fill` mixed into the card's colour\n * (`--bg-surface`) as a concrete hex, `1` the full hue. For a second, ordered quantity\n * on top of the hue — keksdose's recency steps (1, 0.78, 0.55) over the chain's hue —\n * without a `color-mix()`: the result is measured like any hex, so the label ink stays\n * automatic and no `labelColor` is needed. keksdose found a tile under ~0.55 stops\n * reading as its hue and passes for the muted \"other\" (#642).\n *\n * Only a fill the kit can measure is shaded (a hex, a bare `var(--chart-N)` or another\n * bare token), and only on a page that declares `--bg-surface` as a hex; anything else\n * keeps its full hue. On a group, the default for its tiles, as `fill` is.\n */\n shade?: number;\n /**\n * Makes this node a GROUP (0.52, keksdose dev #642: \"Is there already a distinction\n * between shop and chain?\"): a labelled frame, its name in a band along the top, its\n * tiles laid out inside it by the map's own layout, each at its share of the whole\n * picture (see \"Groups: a frame round their tiles\"). Groups nest. A group with nothing\n * drawable inside is not drawn; an empty array is a tile.\n *\n * The frame is in the page's colours (the well, `--bg-surface-2`, under\n * `--text-primary`, at least 7.4:1 in every preset and contrast), and under More\n * contrast it is outlined in `--chart-ink` as a pale tile is. Its band is drawn only\n * where the group has the room for it — see `TreemapCell`.\n */\n children?: readonly TreemapNode[];\n}\n\n/**\n * Everything a `<div>` takes reaches the chart's root (`ChartContainer`), so a test id\n * or a `data-tour` anchor can find it. `children` is the chart's own.\n *\n * The root is a `role=\"figure\"` (0.52, refactor audit 2026-10-30, DSP-5): name it with\n * `aria-label` (\"Spending by category\"), and say what it shows with {@link\n * TreemapProps.description}.\n */\nexport interface TreemapProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n data: readonly TreemapNode[];\n /**\n * Formats a node's value in the tooltip. Handed the node's SHARE of what is drawn\n * (0–1) as a second argument, because a tile is read for how big it is relative to\n * the rest, and \"€412.30\" alone makes the reader estimate that ratio off the areas.\n * The share is `undefined` when the drawn total is not positive.\n *\n * A tile's and a group's accessible name (`labels.tile`, `labels.group`, 0.52) says the\n * value too, asked of the formatter as the tooltip asks it, share and all, and its text\n * is used; markup is said as the bare number. The name says the share itself, so a\n * formatter that writes it into its text has it said twice — say so with\n * {@link TreemapProps.shareInValue}.\n *\n * Default: the bare number through `Intl.NumberFormat` in the provider's locale (0.52,\n * DOC-10: before, the runtime's). A host with a currency passes this — the kit knows\n * none.\n */\n valueFormatter?: (value: number, share: number | undefined) => ReactNode;\n /**\n * The `valueFormatter`'s text already says the share (\"CHF 412.30 · 12%\"), so a tile's\n * and a group's accessible name leave their own \" (12%)\" out (0.53, keksdose after 0.52):\n * the map hands `labels.tile` and `labels.group` the share as `\"\"`, and every catalogue's\n * template drops it there, in its own typography. Before, the only way out was to replace\n * both templates, and with them the kit's seven catalogues. The tooltip is the same either\n * way: it prints what the formatter prints. A formatter whose value is markup is said as\n * the bare number, so its share is then not said at all.\n *\n * Default `false`: the name says the share.\n */\n shareInValue?: boolean;\n /**\n * What the map SHOWS, in a sentence — \"Rent is three fifths of the month.\" — the\n * figure's accessible description (0.52, DSP-5). Not drawn; a caption the eye should\n * read too belongs in the page.\n */\n description?: string;\n /** Per-map strings over `<UiKitProvider labels={{ treemap }}>`. */\n labels?: LabelOverride<TreemapLabels>;\n /** Chart height: a CSS length (`\"20rem\"`, `\"min(20rem, 50dvh)\"`), a Tailwind class,\n * or px. Default `\"20rem\"` — 320 px at Normal, growing with the text size like the\n * labels in the tiles (0.32, docs/text-size-harmonization.md §10.10). */\n height?: ChartHeight;\n /**\n * A tile was activated — by click, or by Enter/Space on a focused tile. Setting this\n * makes every tile a `role=\"button\"`.\n *\n * Either way every tile is reachable (0.52, DSP-5): ONE tab stop for the map, the\n * arrows (←/→ along the reading direction, ↓/↑), Home and End between tiles in the\n * order given, each named \"name: value (share)\" (`labels.tile`) — a `role=\"img\"` to read\n * when nothing listens, as PieChart's slices. Before, a map nobody clicked was nothing\n * to a screen reader, and a clickable tile said its name alone, one tab stop each.\n *\n * In a map with groups (`TreemapNode.children`, 0.52) the walk is the data's own order,\n * depth first: a group's frame, then its tiles, then the next group. A frame is named\n * by `labels.group` (\"Interspar, group: €1,240 (40%)\"), a tile inside one by\n * `labels.inGroup` round its own name (\"Rankweil: €412 (12%), in Interspar\"). Only\n * tiles reach this callback (a frame reaches `onGroupClick`); a tile inside a group is\n * handed a third argument, the ids of its groups, outermost first. Optional in the type,\n * so a forwarder calling this with two arguments still type-checks — and absent for a\n * tile at the top, so a flat map calls it exactly as before.\n */\n onNodeClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /**\n * A group's frame was activated — by click on its band or margin, or by Enter/Space on\n * the focused frame (0.52). Setting this makes every frame a `role=\"button\"`; without it\n * a frame is a `role=\"img\"`, still a stop of the map's keyboard so its total is heard.\n * `groups` as for {@link TreemapProps.onNodeClick}: the groups round this one, passed\n * only for a group inside another.\n */\n onGroupClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /**\n * The node names are the user's own data (payees, not categories): the tile labels\n * and the whole tooltip bubble get `data-private`, the same attribute the kit's\n * `Tooltip redact` and `ChartTooltipContent`'s value cell carry, so the host's one\n * privacy rule reaches them. Off by default — a category name is a label of the app.\n *\n * Every tile is tagged as a whole too, clickable or not (0.52): its accessible name\n * (`labels.tile`) says the name and the value, in an attribute a host's redaction\n * reaches only through the tag.\n *\n * - `true`: every node — the tiles, and a group's frame (its `<g>` and its band's name)\n * and tooltip bubble too.\n * - `\"tiles\"` (0.53, keksdose after 0.52): the tiles as `true` tags them, and no group —\n * for groups that are public names over private ones, as chains (Migros, Coop) over\n * the shops a user paid. Its frame's band, its frame and its bubble's name are\n * untagged, and so is the frame's accessible name (`labels.group`), which says the\n * group's TOTAL: use it only where the groups' names and totals are not the private\n * part. The bubble's value cell is tagged whatever this says, as in every chart.\n * A tile's name still says its group's (`labels.inGroup`), under the tile's tag.\n */\n redactNames?: boolean | \"tiles\";\n /**\n * A second, smaller line under a tile's label — one extra figure per node that the\n * name cannot carry (a year-on-year `+12%`, `new`). Return `undefined` for a node\n * with nothing to add. Never tagged `data-private`: a ratio reveals no amount.\n *\n * ON THE TILE rather than in the tooltip, for two reasons. The tooltip identifies a\n * node by name or by raw value, so a per-node note would be keyed on an amount (two\n * nodes of equal size would swap notes). And a tooltip is a pointer affordance: a\n * phone never hovers, so a figure that only exists on hover does not exist there.\n *\n * Asked for tiles only: a group's band holds its name and nothing else.\n */\n nodeNote?: (id: string) => string | undefined;\n /**\n * Draw at most this many tiles (the first N drawable nodes, in the order given —\n * sort first). Unbounded by default. A tile under ~44x20px has no room for a label,\n * and a map made of unlabelled rectangles answers nothing; around 24 is where the\n * smallest tile of a skewed series reaches that floor on a phone-width card. The\n * caller owns the \"N more not shown\" line, since it knows how many it passed.\n *\n * With groups, the tiles are counted across them in the order given, depth first; a\n * group left with none is not drawn, and a group's frame is not a tile.\n */\n maxTiles?: number;\n /**\n * The categorical ramp tiles take by index, as concrete hex. Default: the page's\n * live `--chart-1…8` tokens (see `useChartTokenHex`), cycled after eight like\n * `paletteFor` (0.49). Pass one when the host\n * already holds the active palette — e.g. `createPaletteStore`'s `useChartHex()` —\n * so a caller colouring a legend beside the map is guaranteed the same list.\n */\n colors?: readonly string[];\n /**\n * How tiles are parted (0.51.6, keksdose dev #642, Marcel: \"The borders do not look\n * nice when changing from tile to tile. Can this be uniform black?\" — and then, on the\n * contrast rule that would go with it: follow the contrast setting).\n *\n * - `\"auto\"` (default): `\"uniform\"` at Standard contrast, `\"contrast\"` while More\n * contrast is in force (`<html data-contrast=\"more\">`: the reader chose it, or chose\n * System on a device that asks for more). Followed live.\n * - `\"uniform\"`: one 2px gutter in the page's ground colour (`--bg-page`, near-black\n * in the dark theme) round every tile, and nothing else. Tiles always abut, so the\n * gutter parts them; each tile is named by its label.\n * - `\"contrast\"`: the 0.49 rule. The gutter is the card's colour (`--bg-surface`),\n * and a tile whose hue is under 3:1 against the card gets a 1px ink edge inside it\n * (WCAG 1.4.11, `useChartEdge`). In the dark theme that ink is light, so some tiles\n * carry a white outline and their neighbours none — the unevenness #642 saw, and\n * the price of the edge for a reader who asked for it.\n */\n edges?: TreemapEdges;\n}\n\n/** {@link TreemapProps.edges}. The switch itself is chart-edge's `useEdgesInForce`,\n * which SeriesChart's bars and areas follow too since 0.52 (DSP-18). */\nexport type TreemapEdges = ChartEdges;\n\n// ── The categorical ramp, as concrete hex ──────────────────────────────────\n//\n// The label ink is measured against the tile's fill, so the fill has to be a colour\n// and not a reference to one. keksdose read it from its palette STORE, which is an\n// app object the kit cannot import. What the kit does own is the token layer: whether\n// a palette arrives through `applyTokenSet` (inline properties on <html>) or through\n// tokens.css (`:root` / `.dark`), the resolved `--chart-N` on <html> is the colour\n// every other chart on the page is being painted with. So that is what is read, and\n// re-read whenever <html>'s class or style changes — which is exactly when a theme\n// toggle or a palette switch lands.\n\nconst CHART_TOKENS = PALETTE_SIZE;\n\n/** The resolved ramp, and after a `|` the card's colour, joined into ONE string so\n * `useSyncExternalStore` compares a primitive: a fresh array per read would never be\n * `Object.is`-equal to the last and would re-render forever. */\nfunction readChartTokens(): string {\n const root = document.documentElement;\n const fallback = root.classList.contains(\"dark\") ? PALETTE_HEX.dark : PALETTE_HEX.light;\n const style = getComputedStyle(root);\n const ramp = Array.from({ length: CHART_TOKENS }, (_, i) => {\n const v = style.getPropertyValue(`--chart-${i + 1}`).trim();\n // An unresolved token (a host that never loaded tokens.css, a test DOM) falls\n // back to the built-in ramp for the current theme rather than to no colour.\n return parseHex(v) ? v : fallback[i];\n }).join(\",\");\n // The card, which `TreemapNode.shade` mixes a hue into (0.52). No fallback: a page that\n // declares no `--bg-surface` hex leaves nothing to mix into, and the tile keeps its hue.\n const card = style.getPropertyValue(\"--bg-surface\").trim();\n return `${ramp}|${parseHex(card) ? card : \"\"}`;\n}\n\nfunction subscribeChartTokens(onChange: () => void): () => void {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"data-theme\"],\n });\n return () => observer.disconnect();\n}\n\nconst SERVER_TOKENS = `${PALETTE_HEX.light.join(\",\")}|`;\n\ninterface ChartTokens {\n ramp: readonly string[];\n /** `--bg-surface` as hex, or null when the page declares none. */\n card: string | null;\n}\n\nfunction useChartTokenHex(): ChartTokens {\n const joined = useSyncExternalStore(subscribeChartTokens, readChartTokens, () => SERVER_TOKENS);\n // One object per reading, not per render: the map's nodes are memoised on it.\n return useMemo(() => {\n const [ramp = \"\", card = \"\"] = joined.split(\"|\");\n return { ramp: ramp.split(\",\"), card: card || null };\n }, [joined]);\n}\n\nconst CHART_TOKEN_REF = /^var\\(\\s*--chart-(\\d+)\\s*\\)$/;\n\n/**\n * The hex a tile fill measures as: the fill itself when it is hex, the live token when\n * it is a bare `var(--chart-N)` (what `paletteFor` returns), otherwise null. Without\n * the token case a hand-built recharts map coloured with the kit's own `paletteFor`\n * got `currentColor` ink — dark page text on dark indigo in the light theme.\n */\nfunction measurableFill(fill: string, tokens: readonly string[]): string | null {\n if (parseHex(fill)) return fill;\n const m = CHART_TOKEN_REF.exec(fill.trim());\n const hex = m ? tokens[Number(m[1]) - 1] : undefined;\n if (hex && parseHex(hex)) return hex;\n // Any other bare token the page declares — `var(--chart-muted)` for a rest bucket,\n // `var(--chart-9)`, a money colour — read off <html> as it resolves.\n if (typeof document === \"undefined\") return null;\n return resolveChartColor(fill, getComputedStyle(document.documentElement));\n}\n\n// ── What a tile reads from its map ───────────────────────────────────────\n//\n// 0.52 (refactor audit 2026-10-30, DSP-22): every tile resolved the token ramp, the ink\n// edge and the edge rule itself — three stores, each with a MutationObserver on <html>, so\n// 72 on keksdose's 24-tile maps, and up to 29 custom properties re-read on every render of\n// every tile. `Treemap` resolves them once and hands them down here; a TreemapCell drawn\n// in a caller's own recharts `<Treemap>` has no map above it and resolves its own.\n\ninterface TileContext {\n tokens: readonly string[];\n edgeFor: (fill: string | undefined) => string | undefined;\n edges: \"uniform\" | \"contrast\";\n /** The map's keyboard (DSP-5): absent for a standalone cell. */\n keys?: TileKeys;\n /** Where every node is drawn in a chart of this size (see \"Groups\"), by path: absent\n * for a standalone cell, which draws where recharts laid it. */\n layout?: (width: number, height: number, scale: number) => ReadonlyMap<string, Placed>;\n /** The pointer is on the node drawn at `box`: where the map's tooltip stands. */\n hover?: (box: Box) => void;\n}\n\n/**\n * Keyed by a node's PATH (`MapNode.path`), not its id: a group and a tile may share an id\n * — chain 12 and shop 12 come from two tables — and keyed by id the map had two tab stops,\n * the frame said the tile's name and the arrows stuck (0.52 code review of treemap.tsx,\n * finding 3).\n */\ninterface TileKeys {\n /** A tile's or a group's accessible name (`labels.tile`, `labels.group`). */\n name: (path: string) => string | undefined;\n onFocus: (path: string) => void;\n /** A node's element as it mounts (and `null` as it goes): the map sets its `tabindex` and\n * gives it back the focus its previous element held (see the map's keyboard). */\n register: (path: string, element: SVGGElement | null) => void;\n /** Moves focus for an arrow, Home or End pressed on node `path`; `false` for any other key. */\n move: (path: string, event: KeyboardEvent<SVGGElement>) => boolean;\n}\n\nconst TreemapTiles = createContext<TileContext | null>(null);\n\n// ── Groups: a frame round their tiles ─────────────────────────────────────\n//\n// 0.52 (keksdose dev #642). A group is a frame with its name in a band along the top, its\n// tiles inside it. recharts lays nested data out edge to edge, with no room for a band, so\n// the map lays it out ITSELF (treemap-layout.ts): squarified as recharts does it, each\n// group given its frame's area on top of its tiles' — so a tile's area stays its share of\n// the picture in any group (0.52 code review of treemap.tsx, finding 1) — and computed up\n// front from the drawn nodes and the chart's size, which every cell reads by its path.\n// Before, a group's cell made the room during render and left it in a module cache for its\n// children's cells, which held only while recharts rendered a group before its children\n// (finding 8). recharts still renders each node's cell, and its tooltip; the map stands the\n// tooltip at the tile as drawn (`TreemapTooltip`), since recharts' own anchor is the centre\n// of the rectangle IT laid out.\n\n// ── Tile ──────────────────────────────────────────────────────────────────\n\n/** The name's size in px at Normal; a tile draws it × the text size's scale (§10.10), and\n * every distance below that is measured in lines grows with it. */\nconst LABEL_FONT_SIZE = 12;\n/** The optional second line, a step down from the name so the tile reads name-first. */\nconst NOTE_FONT_SIZE = 11;\n\n/**\n * Fit a label to its tile: the text itself when it fits, an ellipsised head when it\n * does not, `null` when not even three characters would. Without this a long name ran\n * on across its neighbours and collided with THEIR labels. 0.58em is the average\n * advance of a UI sans at this weight; it only needs to be close, since the tile keeps\n * 6px of padding either side. The full name stays in the tooltip.\n */\nexport function fitLabel(name: string, boxWidth: number, fontSize: number): string | null {\n const max = Math.floor((boxWidth - 12) / (fontSize * 0.58));\n if (max < 3) return null;\n return name.length <= max ? name : `${name.slice(0, max - 1).trimEnd()}…`;\n}\n\n/** A tile's text, painted over a halo in its tile's colour (see `TreemapCell`). */\nconst haloOf = (halo: string) => ({\n stroke: halo,\n strokeWidth: 2.5,\n strokeLinejoin: \"round\" as const,\n paintOrder: \"stroke\",\n});\n\n/** A band's name: the page's text on the well, 8.2:1 at the least in every preset, 7.4:1\n * under More contrast (measured 0.52) — no halo, nothing to stand out from; `none`\n * against the inherited chart stroke. */\nconst BAND_INK = { fill: \"var(--text-primary)\", stroke: \"none\" };\n\n/** What recharts hands a `content` element for each node, plus the three options the\n * chart forwards. Everything optional because recharts clones the element it was\n * given and fills the geometry in — the element as written carries only the options. */\nexport interface TreemapCellProps {\n depth?: number;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n name?: string;\n fill?: string;\n /** See {@link TreemapNode.labelColor}; recharts hands each node's fields to its cell. */\n labelColor?: string;\n id?: string;\n /** Called with `groups` as its third argument when the cell has any; see\n * {@link TreemapProps.onNodeClick}. */\n onNodeClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /** A group's cell's click; see {@link TreemapProps.onGroupClick}. */\n onGroupClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /** The ids of the groups round the node, outermost first. `Treemap` puts it on every\n * node it hands recharts; a cell drawn in a caller's own map has it if the data does. */\n groups?: readonly string[];\n /** The node's place in the tree, which `Treemap` puts on every node it hands recharts:\n * its key in the map's keyboard, where an id may repeat between a group and a tile. */\n path?: string;\n /** Filled in by recharts for a GROUP: the nodes laid out inside it. A cell with\n * children draws a frame, not a tile (0.52). */\n children?: readonly object[] | null;\n /** See {@link TreemapProps.redactNames}: `\"tiles\"` tags a cell with no `children`, and\n * leaves a group's frame and band untagged (0.53). */\n redactNames?: boolean | \"tiles\";\n nodeNote?: (id: string) => string | undefined;\n /** The page's reading direction, which `Treemap` reads off its own root. In `rtl`\n * the label stands in the tile's top RIGHT corner and is shaped right-to-left, so a\n * clipped name ends in its ellipsis on the reading end. Default `ltr`. */\n dir?: Direction;\n /** See {@link TreemapProps.edges}. Default `auto`. Read by a cell drawn on its own; a\n * `Treemap` hands its tiles its own `edges`. */\n edges?: TreemapEdges;\n}\n\n/**\n * One tile. Exported so a caller drawing its own recharts `<Treemap>` gets the same\n * label rules.\n *\n * recharts' default cell inherits the chart-level `stroke`, an SVG presentation\n * attribute that lands on the `<svg>` root and cascades into the label — so the glyphs\n * were painted with a 1px outline ON TOP of themselves, which reads as blur. This cell\n * draws its own label with a halo in the TILE colour painted BEHIND the fill\n * (`paint-order: stroke`), which defeats any inherited stroke and keeps the text solid\n * where it overlaps a lighter or darker part of its own tile.\n *\n * Inside a `Treemap` it is one stop of the map's keyboard, named with its value and\n * share (0.52); drawn on its own it is a stop only as a button (`onNodeClick`), named by\n * `name`.\n *\n * A node with `children` is a GROUP (0.52): the cell draws a frame in the well's colour\n * (`--bg-surface-2`), with the group's name in a band along its top where the group has\n * the room (61px at Normal, treemap-layout's `BODY_SHARE`). Inside a `Treemap` its tiles are laid out\n * in the frame's body; in a caller's own nested recharts `<Treemap>` they are drawn where\n * recharts lays them, over the frame — only `Treemap` makes the room.\n */\nexport function TreemapCell(props: TreemapCellProps) {\n const map = useContext(TreemapTiles);\n return map ? <MapTile {...props} map={map} /> : <StandaloneTile {...props} />;\n}\n\n/** A cell inside a `Treemap`: drawn where the map's layout puts its node, read by path\n * for the chart's size as recharts measured it. */\nfunction MapTile(props: TreemapCellProps & { map: TileContext }) {\n const width = useChartWidth();\n const height = useChartHeight();\n const { scale } = useTextSize();\n const place =\n props.path !== undefined && width && height ? props.map.layout?.(width, height, scale).get(props.path) : undefined;\n return <Tile {...props} place={place} />;\n}\n\n/** A cell with no `Treemap` above it: it resolves what the map would have handed it. */\nfunction StandaloneTile(props: TreemapCellProps) {\n const tokens = useChartTokenHex().ramp;\n const edgeFor = useChartEdge();\n const edges = useEdgesInForce(props.edges ?? \"auto\");\n const map = useMemo(() => ({ tokens, edgeFor, edges }), [tokens, edgeFor, edges]);\n return <Tile {...props} map={map} />;\n}\n\nfunction Tile({\n depth,\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n name,\n fill,\n labelColor,\n id,\n onNodeClick,\n onGroupClick,\n groups,\n path,\n children,\n redactNames,\n nodeNote,\n dir = \"ltr\",\n map,\n place,\n}: TreemapCellProps & { map: TileContext; place?: Placed }) {\n // Before the early return: a hook has to run on every render of the cell.\n const { scale } = useTextSize();\n const { tokens: tokenHex, edgeFor, edges, keys } = map;\n // Depth 0 is recharts' synthetic root, which spans the whole chart.\n if (!depth) return null;\n // Inside a Treemap, where the map's layout draws the node; on its own, where recharts\n // laid it out.\n const box = place?.box ?? { x, y, width, height };\n // Whole pixels: a rect on a half pixel renders its edge across two half-lit\n // columns, and the text baseline inherits that blur.\n const px = Math.round(box.x);\n const py = Math.round(box.y);\n const pw = Math.round(box.width);\n const ph = Math.round(box.height);\n const isGroup = children != null && children.length > 0;\n const group = !isGroup\n ? undefined\n : place?.body\n ? { banded: place.banded ?? false, body: place.body }\n : groupBody({ x: px, y: py, width: pw, height: ph }, scale);\n const tileFill = fill ?? \"var(--chart-1)\";\n const measured = measurableFill(tileFill, tokenHex);\n const ink = labelColor ?? (measured ? textOn(measured) : \"currentColor\");\n // `contrast`: a tile in a hue under 3:1 against the card gets a 1px ink edge inside\n // the gutter (WCAG 1.4.11, anyplot's rule); a hue that clears it stays bare.\n // `uniform`: no per-tile edge; every tile is parted by the same gutter.\n // A group's frame is the well, 1.09–1.24:1 from the card in every preset and contrast\n // (measured 0.52): under `contrast` it always takes the edge.\n const edge =\n edges !== \"contrast\" ? undefined : group ? CHART_ROLES.outline : measured ? edgeFor(measured) : undefined;\n // The halo only on an OPAQUE fill we could measure. A translucent fill (a\n // `color-mix(… transparent)`) composited twice — once as the tile, once as the halo\n // over it — draws a darker ring round every glyph, which read as doubled text on the\n // faded tiles. `none` still overrides the inherited chart `stroke`.\n const halo = measured ? tileFill : \"none\";\n // Below ~44x20 there is no room for a readable word, so no label at all rather than\n // a one-letter stub bleeding over the tile edge. A group's name only in its band.\n const labelSize = LABEL_FONT_SIZE * scale;\n const noteSize = NOTE_FONT_SIZE * scale;\n const roomForName = group ? group.banded : pw >= 44 * scale && ph >= 20 * scale;\n const label = roomForName ? fitLabel(name ?? \"\", pw, labelSize) : null;\n // Only under a label that is itself drawn — a stray \"+12%\" on an unnamed rectangle\n // names nothing — and only where a second baseline fits: the note's baseline sits\n // 29px down and an 11px glyph hangs ~2px below it, so 36 leaves the same ~5px under\n // the note that the label keeps under itself at the 20px floor. Never in a band.\n const rawNote = !group && id != null && nodeNote ? nodeNote(id) : undefined;\n const note = label != null && rawNote != null && ph >= 36 * scale ? fitLabel(rawNote, pw, noteSize) : null;\n // The label's START edge, 6px in from the tile's. The tiles themselves are laid out\n // physically (the plot is not mirrored, see `ChartContainer`) and the chart's SVG is\n // pinned `ltr`, so the direction is set on each <text> explicitly: `start` is then\n // the right end in RTL, and the label grows leftwards from the tile's right edge.\n const textX = dir === \"rtl\" ? px + pw - 6 : px + 6;\n // A tile's click is `onNodeClick`, a frame's `onGroupClick`. The groups go along only\n // when there are any, so a flat map calls back exactly as it did before 0.52.\n const click = group ? onGroupClick : onNodeClick;\n const activate =\n id != null && click\n ? () => (groups && groups.length > 0 ? click(id, name ?? \"\", groups) : click(id, name ?? \"\"))\n : undefined;\n // Inside a Treemap every tile and frame is a stop of the map's one roving tab stop,\n // named with its value and share; a standalone cell is a stop of its own only when it\n // is a button.\n const roving = path != null && keys ? keys : undefined;\n const focusable = roving !== undefined || activate !== undefined;\n // `\"tiles\"` (0.53, keksdose after 0.52) leaves a group's frame and band untagged: its\n // groups are public chain names, and its blur on `[data-private]` blurred them with the\n // payees. A truthy test on the union would have tagged them still.\n const redact = redactNames === true || (redactNames === \"tiles\" && !group);\n const onKeyDown = focusable\n ? (e: KeyboardEvent<SVGGElement>) => {\n if (roving?.move(path!, e)) {\n e.preventDefault();\n return;\n }\n if (!activate || (e.key !== \"Enter\" && e.key !== \" \")) return;\n e.preventDefault();\n activate();\n }\n : undefined;\n return (\n <g\n ref={roving ? refFor(roving.register, path!) : undefined}\n onClick={activate}\n onKeyDown={onKeyDown}\n onFocus={roving ? () => roving.onFocus(path!) : undefined}\n onMouseEnter={map.hover ? () => map.hover!({ x: px, y: py, width: pw, height: ph }) : undefined}\n // A clickable tile is a button to a keyboard and a screen reader too; the\n // drilldown it opens was mouse-only in the app this came from. Under `redactNames`\n // the group carries `data-private` whenever it carries a name, button or not: the\n // name says the node's name and value, and an untagged `aria-label` is read by a\n // session replay or a redaction keyed on `[data-private]` (0.52, code review of\n // feat/0.52.0 — DSP-5 named every tile, and this tagged only the buttons).\n role={activate ? \"button\" : roving ? \"img\" : undefined}\n // In a map, -1 as drawn: the map moves its one stop on the elements in place.\n tabIndex={roving ? -1 : activate ? 0 : undefined}\n aria-label={focusable ? (roving?.name(path!) ?? name) : undefined}\n data-private={focusable && redact ? \"\" : undefined}\n data-group={group ? \"\" : undefined}\n cursor={activate ? \"pointer\" : undefined}\n // CSS beats a presentation attribute, so this recolours the rect's own stroke\n // for as long as the tile has keyboard focus.\n className={focusable ? \"outline-none [&:focus-visible>rect]:stroke-[var(--brand)]\" : undefined}\n >\n {/* The 2px gutter: the page's ground (`uniform`, the same dark line round every\n tile in the dark theme) or the card showing through (`contrast`). Never a white\n grid: on a dark surface white lines glare harder than the tiles. A group's frame\n is the well, the inset surface: the page's own colour in light, a step up from\n it in dark, and never one of the hues its tiles are told apart by. */}\n <rect\n x={px}\n y={py}\n width={pw}\n height={ph}\n fill={group ? \"var(--bg-surface-2)\" : tileFill}\n stroke={edges === \"contrast\" ? \"var(--bg-surface)\" : \"var(--bg-page)\"}\n strokeWidth={2}\n rx={3}\n />\n {edge && pw > 3 && ph > 3 && (\n <rect\n data-edge=\"\"\n x={px + 1.5}\n y={py + 1.5}\n width={pw - 3}\n height={ph - 3}\n fill=\"none\"\n stroke={edge}\n strokeWidth={1}\n rx={2}\n style={{ pointerEvents: \"none\" }}\n />\n )}\n {label != null && (\n <text\n // A plain attribute, so the host's `[data-private]` rule reaches an SVG\n // <text> exactly as it reaches a table cell.\n data-private={redact ? \"\" : undefined}\n x={textX}\n // A band's name centred in its 20px; a tile's where its floor leaves room below.\n y={py + (group ? 14 : 15) * scale}\n direction={dir}\n {...(group ? BAND_INK : { fill: ink, ...haloOf(halo) })}\n fontSize={labelSize}\n fontWeight={600}\n style={{ pointerEvents: \"none\" }}\n >\n {label}\n </text>\n )}\n {note != null && (\n <text\n x={textX}\n y={py + 29 * scale}\n direction={dir}\n fill={ink}\n fillOpacity={0.85}\n {...haloOf(halo)}\n fontSize={noteSize}\n fontWeight={500}\n style={{ pointerEvents: \"none\" }}\n >\n {note}\n </text>\n )}\n </g>\n );\n}\n\n// ── Chart ─────────────────────────────────────────────────────────────────\n\n// Nodes carry their own `fill`, and `ChartTooltipContent` falls back to it for the\n// swatch, so no series config is needed.\nconst TREEMAP_CONFIG: ChartConfig = {};\n\n/**\n * The shared tooltip with the whole bubble tagged `data-private`, for names that are\n * the user's own data.\n *\n * The bubble, not just the name: `ChartTooltipContent` prints the name either from\n * `item.name` or from a config `label`, and only the config route could be tagged — but\n * a config entry keyed by a payee name is not a CSS identifier, and `ChartStyle` would\n * (rightly) refuse it. The bubble holds the name and the value, and the value cell is\n * already `data-private`, so tagging the wrapper hides nothing extra.\n *\n * recharts CLONES the `content` element to inject `active`/`payload`, so this has to BE\n * that element — a `<div>` wrapped around `<ChartTooltipContent />` in the JSX would\n * take those props itself and hand the tooltip nothing.\n *\n * `groups: false` is `redactNames=\"tiles\"` (0.53): a group's bubble is left untagged, as its\n * frame is. The payload says which node is hovered — recharts hands the node itself, with\n * its laid-out `children` for a group and `null` for a tile, as the cell is told — so\n * anything else, or no payload, is tagged.\n */\nfunction RedactedTooltipContent({\n groups = true,\n ...props\n}: ComponentProps<typeof ChartTooltipContent> & { groups?: boolean }) {\n const children = props.payload?.[0]?.payload?.children;\n const group = Array.isArray(children) && children.length > 0;\n return (\n <div data-private={groups || !group ? true : undefined}>\n <ChartTooltipContent {...props} />\n </div>\n );\n}\n\n/** Where the pointer last entered a node, as drawn: a store of one box, so that only the\n * tooltip re-renders when it moves — not the map, nor (through recharts) every tile. */\ninterface Anchor {\n read: () => Box | null;\n subscribe: (onChange: () => void) => () => void;\n set: (box: Box) => void;\n}\n\nfunction createAnchor(): Anchor {\n let at: Box | null = null;\n const listeners = new Set<() => void>();\n return {\n read: () => at,\n subscribe: (onChange) => {\n listeners.add(onChange);\n return () => listeners.delete(onChange);\n },\n set: (box) => {\n if (at && at.x === box.x && at.y === box.y && at.width === box.width && at.height === box.height) return;\n at = box;\n listeners.forEach((onChange) => onChange());\n },\n };\n}\n\n/**\n * The map's tooltip, stood at the tile the pointer is on AS DRAWN. recharts anchors its\n * tooltip at the centre of the rectangle it laid the node out in, and since 0.52 the map\n * draws its nodes where its own layout puts them (see \"Groups\") — a tooltip anchored by\n * recharts could stand over a neighbour. It stands 10px off the tile's centre, towards the\n * chart's middle, so it does not run off the side the tile is on. `content` is the bubble,\n * as recharts' own `content`.\n */\nfunction TreemapTooltip({ content, anchor }: { content: ReactElement; anchor: Anchor }) {\n const at = useSyncExternalStore(anchor.subscribe, anchor.read, anchor.read);\n const width = useChartWidth() ?? 0;\n const height = useChartHeight() ?? 0;\n if (!at) return <ChartTooltip content={content} />;\n const cx = at.x + at.width / 2;\n const cy = at.y + at.height / 2;\n const left = cx > width / 2;\n const up = cy > height / 2;\n return (\n <ChartTooltip\n content={content}\n position={{ x: cx + (left ? -10 : 10), y: cy + (up ? -10 : 10) }}\n // `translate` composes with the `transform` recharts sets for `position`.\n wrapperStyle={{ translate: `${left ? \"-100%\" : \"0\"} ${up ? \"-100%\" : \"0\"}` }}\n />\n );\n}\n\n/** What `Treemap` hands recharts per node: a tile, or a group with `children`. recharts\n * hands each node's fields to its cell; a field left out of this never reaches\n * `TreemapCell` (0.8.0 shipped `labelColor` without it — keksdose). A type, not an\n * interface: recharts' `data` wants an index signature, which only an alias meets. */\ntype MapNode = {\n id: string;\n name: string;\n /** A tile's value; a group's, its drawn tiles' together. recharts sums a group again\n * for its layout, and its tooltip reads this field. */\n size: number;\n fill: string;\n labelColor?: string;\n /** The ids of the groups round the node, outermost first. */\n groups: readonly string[];\n /** The node's key in the map's keyboard and names: its id and its groups' (see TileKeys). */\n path: string;\n children?: MapNode[];\n};\n\n/** What a group hands down to the tiles inside it that set none of their own. */\ninterface Paint {\n fill: string;\n labelColor?: string;\n shade?: number;\n}\n\n/**\n * `data` as drawn: tiles with a positive value, at most `maxTiles` of them in the order\n * given (depth first), and the groups that still hold any. `shaded` turns a fill and a\n * `shade` into the fill drawn.\n *\n * Coloured by index into `data` AS GIVEN, before anything is dropped: a caller painting a\n * legend or a second map from the same list must land on the same hue for the same row,\n * and filtering first would shift every colour after a gap. Below the top a node takes\n * its group's colour, so a group's tiles share one hue unless they set their own.\n */\nfunction mapNodes(\n data: readonly TreemapNode[],\n ramp: readonly string[],\n maxTiles: number | null | undefined,\n shaded: (fill: string, shade: number | undefined) => string,\n): MapNode[] {\n // Whole tiles, as `slice(0, maxTiles ?? undefined)` counted them up to 0.51: 2.5 draws 2,\n // NaN none and null every one (0.52 code review of treemap.tsx, finding 4 — a bare\n // counter drew 3, and every tile; deep review, charts, finding 2 — Math.floor(null) is 0,\n // and a JS caller's or a `number | null` setting's null drew an empty card).\n let left = maxTiles == null ? Infinity : Number.isNaN(maxTiles) ? 0 : Math.floor(maxTiles);\n const walk = (list: readonly TreemapNode[], groups: readonly string[], paint: Paint | undefined): MapNode[] =>\n list.flatMap((d, idx): MapNode[] => {\n const fill = d.fill ?? paint?.fill ?? ramp[idx % ramp.length]!;\n // An inherited fill brings its ink along (a `color-mix()` group needs its\n // `labelColor`); a fill of the tile's own is measured again.\n const labelColor = d.labelColor ?? (d.fill === undefined ? paint?.labelColor : undefined);\n const shade = d.shade ?? paint?.shade;\n if (d.children && d.children.length > 0) {\n const children = walk(d.children, [...groups, d.id], { fill, labelColor, shade });\n if (children.length === 0) return [];\n const size = children.reduce((acc, c) => acc + c.size, 0);\n return [{ id: d.id, name: d.name, size, fill, groups, path: pathOf(groups, d.id), children }];\n }\n if (!(Number.isFinite(d.value) && d.value > 0) || left <= 0) return [];\n left -= 1;\n const drawn = shaded(fill, shade);\n // An ink the GROUP chose was chosen for the group's colour: once `shade` makes the\n // tile another hex, that hex is measured instead (0.52 code review of treemap.tsx,\n // finding 5 — white ink on a tile shaded almost to the card, ~1.3:1). The tile's\n // own `labelColor` still wins: the caller set it for this tile.\n const ink = d.labelColor ?? (drawn === fill ? labelColor : undefined);\n return [\n { id: d.id, name: d.name, size: d.value, fill: drawn, labelColor: ink, groups, path: pathOf(groups, d.id) },\n ];\n });\n return walk(data, [], undefined);\n}\n\n/** The last layout made for each drawn tree, by size and text size: a memo, not state — it\n * is a pure function of its key, so whichever cell asks first, every cell gets the same. */\nconst LAYOUTS = new WeakMap<readonly MapNode[], { key: string; placed: ReadonlyMap<string, Placed> }>();\n\nfunction layoutFor(nodes: readonly MapNode[], width: number, height: number, scale: number) {\n const key = `${width}x${height}@${scale}`;\n const last = LAYOUTS.get(nodes);\n if (last?.key === key) return last.placed;\n const placed = layoutTreemap(nodes, width, height, scale);\n LAYOUTS.set(nodes, { key, placed });\n return placed;\n}\n\n/** One ref callback per map and path, the same on every render: a new function each time\n * makes React detach and attach the element on every render of the tile. A path no longer\n * drawn is let go as the map lays out its new data (see the map's keyboard). */\nconst REFS = new WeakMap<TileKeys[\"register\"], Map<string, (element: SVGGElement | null) => void>>();\n\nfunction refFor(register: TileKeys[\"register\"], path: string) {\n let byPath = REFS.get(register);\n if (!byPath) {\n byPath = new Map();\n REFS.set(register, byPath);\n }\n let ref = byPath.get(path);\n if (!ref) {\n ref = (element) => register(path, element);\n byPath.set(path, ref);\n }\n return ref;\n}\n\n/** A node's path: its groups' ids and its own, as JSON so that no id can fake a separator. */\nconst pathOf = (groups: readonly string[], id: string) => JSON.stringify([...groups, id]);\n\n/** `nodes` in the order the keyboard walks them: each group, then what is inside it. */\nfunction depthFirst(nodes: readonly MapNode[]): MapNode[] {\n return nodes.flatMap((n) => [n, ...(n.children ? depthFirst(n.children) : [])]);\n}\n\n/**\n * Tile chart: one rectangle per {@link TreemapNode}, sized by `value`, coloured by\n * index from the chart ramp (or by the node's own `fill`), labelled in whichever of the\n * kit's two inks measures better against it. Hover shows the name and the formatted\n * value; every tile is a keyboard stop named with its value and share (see\n * `onNodeClick`), and `onNodeClick` turns the tiles into buttons.\n *\n * A node with `children` is a group (0.52): a labelled frame with its tiles inside, in\n * its colour unless they set their own — see {@link TreemapNode.children}.\n *\n * Renders nothing when no node has a positive value — an empty box is a hole in the\n * card, and the caller's own sentence is the better answer there.\n *\n * Needs `recharts`, like everything behind `@eifi1/ui-kit/chart`.\n */\nexport function Treemap({\n data,\n valueFormatter,\n height = \"20rem\",\n onNodeClick,\n onGroupClick,\n redactNames = false,\n shareInValue = false,\n nodeNote,\n maxTiles,\n colors,\n edges = \"auto\",\n description,\n labels: labelsProp,\n style,\n ...rest\n}: TreemapProps) {\n // Resolved once for every tile, which reads them from `TreemapTiles` (DSP-22).\n const { ramp: tokenHex, card } = useChartTokenHex();\n const edgeFor = useChartEdge();\n const edgesInForce = useEdgesInForce(edges);\n const labels = useKitLabels(\"treemap\", DEFAULT_TREEMAP_LABELS, labelsProp);\n // The provider's locale (0.52, refactor audit 2026-10-30, DOC-10): this was the one\n // formatter default in the kit that took the runtime's.\n const locale = useKitLocale();\n const numberFormat = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n // One decimal under 10 %, the rule PieChart says its shares by too (lib/share-format):\n // a tile of 0.4 % named \"0%\" says it is not there.\n const shareText = useMemo(() => shareFormat(locale), [locale]);\n // Read off the DOM after mount: the `dir` a treemap sits under is an ancestor's\n // attribute, and SVG text anchoring cannot follow it by CSS (see TreemapCell). Again\n // on new data, because a map that had nothing to draw rendered no root to read.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n useLayoutEffect(() => {\n setDir(dirOf(rootRef.current));\n }, [data]);\n const ramp = colors && colors.length > 0 ? colors : tokenHex;\n // Memoised: moving the tab stop re-renders the map, and fresh nodes would re-lay it.\n const nodes = useMemo(\n () =>\n mapNodes(data, ramp, maxTiles, (fill, shade) => {\n // `shade` (0.52): the hue mixed into the card as a hex, so it is measured like\n // any other. A fill or a card it cannot read keeps the full hue.\n if (shade === undefined || !Number.isFinite(shade) || shade >= 1 || !card) return fill;\n const hex = measurableFill(fill, tokenHex);\n return hex ? lerpHex(card, hex, shade) : fill;\n }),\n [data, ramp, maxTiles, tokenHex, card],\n );\n // The keyboard walks groups and tiles alike, in the data's order (see `onNodeClick`).\n const order = useMemo(() => depthFirst(nodes), [nodes]);\n // The map's layout (see \"Groups\"), for the size recharts measured: each cell asks for it\n // by that size, and `layoutFor` makes it once per size and text size.\n const layout = useMemo(\n () => (width: number, height: number, scale: number) => layoutFor(nodes, width, height, scale),\n [nodes],\n );\n // Where the pointer is, for the tooltip to stand at (`TreemapTooltip`).\n const [anchor] = useState(createAnchor);\n\n // The keyboard (DSP-5): one tab stop for the map, moved between tiles in `order` as\n // PieChart's is between slices. The tiles are drawn by recharts, so they register\n // their element here rather than being looked up.\n //\n // The stop is moved on the elements IN PLACE, as PieChart moves its own, and never\n // reaches the tiles' context: kept there, every arrow-key step was a new context and\n // every tile drew again — layout, fitted labels, notes (0.52 code review of treemap.tsx,\n // finding 6). And recharts keys a node by where IT lays it out (`x-y-name`), so new data\n // or a resize remounts the focused node: its element leaves holding the focus, which\n // fell to <body> (finding 7). The path it held is kept, and its new element takes the\n // focus back as it mounts — or, if the node is gone, the stop does.\n const tiles = useRef(new Map<string, SVGGElement>());\n const orderRef = useRef(order);\n const stopRef = useRef<string | undefined>(undefined);\n const restoreRef = useRef<string | null>(null);\n /** The path holding the stop: the last one focused while it is drawn, else the first. */\n const stopOf = useCallback(() => {\n const drawn = orderRef.current;\n return drawn.some((n) => n.path === stopRef.current) ? stopRef.current : drawn[0]?.path;\n }, []);\n const applyStop = useCallback(() => {\n const at = stopOf();\n tiles.current.forEach((el, path) => el.setAttribute(\"tabindex\", path === at ? \"0\" : \"-1\"));\n return at;\n }, [stopOf]);\n const register = useCallback(\n (path: string, element: SVGGElement | null) => {\n if (element) {\n tiles.current.set(path, element);\n element.setAttribute(\"tabindex\", path === stopOf() ? \"0\" : \"-1\");\n if (restoreRef.current === path) {\n restoreRef.current = null;\n element.focus();\n }\n return;\n }\n // Detached before it is removed: still the focused element if it held the focus.\n // Kept for this commit only — the node's new element mounts in it, or the effect below\n // hands the focus on. A node that left with no new data (`<Activity mode=\"hidden\">`, a\n // panel 0 wide) took the focus back whenever it was shown again, from wherever the\n // reader was by then (0.52 code review, deep, charts, finding 3).\n if (tiles.current.get(path) === document.activeElement) {\n restoreRef.current = path;\n queueMicrotask(() => {\n if (restoreRef.current === path) restoreRef.current = null;\n });\n }\n tiles.current.delete(path);\n },\n [stopOf],\n );\n const onFocus = useCallback(\n (path: string) => {\n stopRef.current = path;\n applyStop();\n },\n [applyStop],\n );\n // New data: the order the stop is read against, the stop set again on what is drawn now,\n // the focus of a node that is gone handed to the stop — and the ref callbacks of the paths\n // no longer drawn let go: a map that switched data (a month picker over payees) kept one\n // for every node it had ever drawn (0.52 code review, deep, charts, finding 11).\n useLayoutEffect(() => {\n orderRef.current = order;\n const refs = REFS.get(register);\n if (refs) {\n const drawn = new Set(order.map((n) => n.path));\n for (const path of refs.keys()) if (!drawn.has(path)) refs.delete(path);\n }\n const at = applyStop();\n if (restoreRef.current === null) return;\n restoreRef.current = null;\n if (at) tiles.current.get(at)?.focus();\n }, [order, applyStop, register]);\n const move = useCallback((path: string, event: KeyboardEvent<SVGGElement>) => {\n const drawn = orderRef.current;\n const at = drawn.findIndex((n) => n.path === path);\n // The ends hold: a map is a plane, not PieChart's ring. → is the next tile in any page:\n // the tiles are laid out left to right whatever the reading direction (the plot is not\n // mirrored, see `ChartContainer`), and → read off the page walked an RTL page's map\n // backwards, against the picture (0.52 code review, deep, charts, finding 1).\n const to = at < 0 ? undefined : rovingTarget(event.key, \"ltr\", at, drawn.length, false);\n if (to === undefined) return false;\n const target = drawn[to];\n if (target) tiles.current.get(target.path)?.focus();\n return true;\n }, []);\n\n // Everything below is memoised over what it reads (0.52, code review of feat/0.52.0). A\n // tile re-renders when its context or its recharts element changes, and recharts' map is\n // a PureComponent that redraws every node when `content` or its tooltip child is a new\n // element: built afresh per render, as they were, a page re-rendering the card or one\n // arrow-key step redrew every tile and ran `labels.tile` and two `Intl` formats per node\n // again — the cost DSP-22 had just taken out of the tiles.\n //\n // The share is of what is DRAWN, so it always reads as \"of this picture\" — a group's\n // and a tile's inside it alike.\n const total = useMemo(() => nodes.reduce((acc, n) => acc + n.size, 0), [nodes]);\n const format = useCallback(\n (v: number): ReactNode =>\n valueFormatter ? valueFormatter(v, total > 0 ? v / total : undefined) : formatWithMinus(numberFormat, v),\n [valueFormatter, total, numberFormat],\n );\n const names = useMemo(() => {\n // The value as the tooltip says it: the caller's formatter, handed the node's share as\n // it is there, when it gives text; a value it formats as markup is said as a number.\n //\n // It was asked with the share `undefined` — which, with anything drawn, it had never\n // been before 0.52 — so a formatter leaning on the share printed \"NaN%\" into every name\n // or threw and took the chart down (0.52 code review of treemap.tsx, finding 2). A guard\n // caught both; every named node has its share, so it is handed over instead (deep\n // review, charts, finding 13).\n const valueText = (v: number) => {\n const out = format(v);\n return typeof out === \"string\" || typeof out === \"number\" ? String(out) : formatWithMinus(numberFormat, v);\n };\n // A group's name by its path; a node's groups by theirs, its own path's prefixes.\n const groupName = new Map(order.filter((n) => n.children).map((n) => [n.path, n.name]));\n // `\"\"` when the formatter's text says the share already, which every template drops\n // (0.53, `shareInValue`).\n const shareOf = (v: number) => (shareInValue ? \"\" : shareText(total > 0 ? v / total : 0));\n return new Map(\n order.map((n) => {\n const say = n.children ? labels.group : labels.tile;\n const own = say(n.name, valueText(n.size), shareOf(n.size));\n // The innermost group first: \"Rent: …, in Housing, in Fixed costs\".\n const named = n.groups\n .map((g, i) => groupName.get(pathOf(n.groups.slice(0, i), g)) ?? g)\n .reduceRight((acc, group) => labels.inGroup(acc, group), own);\n return [n.path, named] as const;\n }),\n );\n }, [order, labels, format, numberFormat, shareText, shareInValue, total]);\n const keys = useMemo<TileKeys>(\n () => ({ name: (path) => names.get(path), onFocus, register, move }),\n [names, onFocus, register, move],\n );\n const context = useMemo<TileContext>(\n () => ({ tokens: tokenHex, edgeFor, edges: edgesInForce, keys, layout, hover: anchor.set }),\n [tokenHex, edgeFor, edgesInForce, keys, layout, anchor],\n );\n const cell = useMemo(\n () => (\n <TreemapCell\n onNodeClick={onNodeClick}\n onGroupClick={onGroupClick}\n redactNames={redactNames}\n nodeNote={nodeNote}\n dir={dir}\n />\n ),\n [onNodeClick, onGroupClick, redactNames, nodeNote, dir],\n );\n const tooltip = useMemo(\n () => (\n <TreemapTooltip\n content={\n redactNames ? (\n <RedactedTooltipContent hideLabel valueFormatter={format} groups={redactNames === true} />\n ) : (\n <ChartTooltipContent hideLabel valueFormatter={format} />\n )\n }\n anchor={anchor}\n />\n ),\n [redactNames, format, anchor],\n );\n\n if (nodes.length === 0) return null;\n const box = chartHeightProps(height);\n return (\n // Round the container, not inside it: ResponsiveContainer sizes its ONE child, the chart.\n <TreemapTiles.Provider value={context}>\n <ChartContainer\n role=\"figure\"\n {...rest}\n // Read through `aria-describedby`, after a caller's own ids; `hidden`, inside the\n // figure (0.52 code review, deep, charts, finding 7).\n description={description}\n ref={rootRef}\n config={TREEMAP_CONFIG}\n className={cn(box.className, rest.className)}\n style={{ ...style, ...box.style }}\n >\n {/* No chart-level `stroke`: recharts spreads it onto the <svg> root, from where\n it inherits into every label (see TreemapCell). Each cell strokes its own rect. */}\n <RechartsTreemap data={nodes} dataKey=\"size\" fill=\"var(--chart-1)\" isAnimationActive={false} content={cell}>\n {tooltip}\n </RechartsTreemap>\n </ChartContainer>\n </TreemapTiles.Provider>\n );\n}\n"],"mappings":"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","names":["height"]}
1
+ {"version":3,"sources":["../../src/components/treemap.tsx"],"sourcesContent":["// A tile chart (\"treemap\"): one rectangle per node, its AREA the node's share of what\n// is drawn. Lifted out of keksdose's reports, where five cards drew it (spending by\n// group and by category, net worth by account and by type, allocation, payees, food\n// groups) — every domain word is gone and every string it could print is a prop.\n//\n// It uses the kit's own chart shell (`ChartContainer`, `ChartTooltipContent`) and the\n// kit's own ink rule (`textOn`), so it re-skins with the palette like every other\n// chart. The one thing it cannot take from a CSS var is the tile colour: the label ink\n// is MEASURED against the fill, and a `var(--chart-3)` string measures as nothing — see\n// `useChartTokenHex` below for how the concrete hex is found instead.\n//\n// Since 0.52 a node can hold `children`: a group, drawn as a labelled frame round its\n// tiles (keksdose dev #642, shops by chain). Where every node is drawn is the map's own\n// layout (treemap-layout.ts), not recharts': see \"Groups: a frame round their tiles\".\n// Since 0.55 it draws them itself too, and recharts' `<Treemap>` is gone from it: see\n// \"The picture\".\nimport {\n cloneElement,\n memo,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type { ComponentProps, ComponentPropsWithoutRef, KeyboardEvent, ReactElement, ReactNode } from \"react\";\nimport { ChartContainer, ChartTooltipContent, type ChartConfig } from \"./chart\";\nimport { CHART_ROLES, PALETTE_HEX, PALETTE_SIZE, lerpHex, textOn } from \"../theme/chart-palette\";\nimport { resolveChartColor, useChartEdge, useEdgesInForce, type ChartEdges } from \"./chart-edge\";\nimport { parseHex } from \"../theme/color\";\nimport { dirOf, rovingTarget, type Direction } from \"../lib/direction\";\nimport { returnFocus } from \"../lib/focus-left\";\nimport { shareFormat } from \"../lib/share-format\";\nimport { cn } from \"../lib/cn\";\nimport { useTextSize } from \"../theme/text-size\";\nimport { chartHeightProps, type ChartHeight } from \"./chart-height\";\nimport { formatWithMinus } from \"../lib/minus\";\nimport { prefersReducedMotion } from \"../lib/reduced-motion\";\nimport { useKitLabels, useKitLocale, type LabelOverride } from \"../i18n/kit-labels\";\nimport { DEFAULT_TREEMAP_LABELS, type TreemapLabels } from \"./treemap-labels\";\nimport { groupBody, layoutTreemap, type Placed } from \"./treemap-layout\";\n\n/**\n * One tile.\n *\n * `value`, not keksdose's `total`: a treemap's areas are not always money (keksdose\n * already sizes one by transaction COUNT), and `value` is the word every other chart\n * in recharts uses for the number a mark is sized by. Migrating is a rename at the\n * mapping step — `{ id, name, value: row.total }`.\n */\nexport interface TreemapNode {\n /** Stable key, handed back by {@link TreemapProps.onNodeClick} and\n * {@link TreemapProps.nodeNote}. Names can collide; ids must not among a group's own\n * nodes (or the top's). A group and a tile inside it may share one — chain 12 and shop\n * 12 from two tables — and `onNodeClick`'s `groups` tells them apart (0.52). */\n id: string;\n name: string;\n /** The tile's area. A node whose value is not a positive finite number is NOT\n * drawn — an area cannot be negative, and a squarified layout handed a negative size\n * draws nonsense rather than refusing it. Saying so is the caller's job: a group that\n * vanishes silently between two views of the same data is a bug report.\n *\n * A GROUP's own `value` is not read: its area is its drawn children's together. Pass\n * that sum — the field stays required so that every reader of a `TreemapNode[]` keeps\n * a `number`. */\n value: number;\n /** Overrides the by-index palette colour for this tile — e.g. to paint every child\n * in its parent group's colour, so the groups stay legible in one picture. A\n * concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured — so\n * does `CHART_ROLES.other` (`var(--chart-muted)`), the colour for a \"rest\" bucket,\n * which should not wrap round to a hue the reader matches to a real node;\n * anything else (another var, a `color-mix()`) gets `currentColor` ink, because\n * there is nothing to measure — set `labelColor` then.\n *\n * On a group, the colour of every tile inside it that sets none (0.52): keksdose's\n * \"hue = chain\" is a `fill` per chain. A group left without one takes the ramp by its\n * index, as a tile does, and hands that down; its frame is drawn in the page's own\n * colours whatever it is. */\n fill?: string;\n /** The label's ink, for a `fill` the kit cannot measure (a `var()` or `color-mix()`):\n * keksdose's recency plot fills with `color-mix(in srgb, var(--chart-1) N%,\n * transparent)`, where `currentColor` put dark page text on full-strength indigo\n * (~1.5:1). Wins over the measured ink when given. A tile that takes its group's\n * `fill` takes the group's `labelColor` with it; one with a `fill` of its own, or one\n * that `shade` turns into another colour, is measured again. */\n labelColor?: string;\n /**\n * The tile's colour strength, 0–1 (0.52): its `fill` mixed into the card's colour\n * (`--bg-surface`) as a concrete hex, `1` the full hue. For a second, ordered quantity\n * on top of the hue — keksdose's recency steps (1, 0.78, 0.55) over the chain's hue —\n * without a `color-mix()`: the result is measured like any hex, so the label ink stays\n * automatic and no `labelColor` is needed. keksdose found a tile under ~0.55 stops\n * reading as its hue and passes for the muted \"other\" (#642).\n *\n * Only a fill the kit can measure is shaded (a hex, a bare `var(--chart-N)` or another\n * bare token), and only on a page that declares `--bg-surface` as a hex; anything else\n * keeps its full hue. On a group, the default for its tiles, as `fill` is.\n */\n shade?: number;\n /**\n * Makes this node a GROUP (0.52, keksdose dev #642: \"Is there already a distinction\n * between shop and chain?\"): a labelled frame, its name in a band along the top, its\n * tiles laid out inside it by the map's own layout, each at its share of the whole\n * picture (see \"Groups: a frame round their tiles\"). Groups nest. A group with nothing\n * drawable inside is not drawn; an empty array is a tile.\n *\n * The frame is in the page's colours (the well, `--bg-surface-2`, under\n * `--text-primary`, at least 7.4:1 in every preset and contrast), and under More\n * contrast it is outlined in `--chart-ink` as a pale tile is. Its band is drawn only\n * where the group has the room for it — see `TreemapCell`.\n */\n children?: readonly TreemapNode[];\n}\n\n/**\n * Everything a `<div>` takes reaches the chart's root (`ChartContainer`), so a test id\n * or a `data-tour` anchor can find it. `children` is the chart's own.\n *\n * The root is a `role=\"figure\"` (0.52, refactor audit 2026-10-30, DSP-5): name it with\n * `aria-label` (\"Spending by category\"), and say what it shows with {@link\n * TreemapProps.description}.\n */\nexport interface TreemapProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n data: readonly TreemapNode[];\n /**\n * Formats a node's value in the tooltip. Handed the node's SHARE of what is drawn\n * (0–1) as a second argument, because a tile is read for how big it is relative to\n * the rest, and \"€412.30\" alone makes the reader estimate that ratio off the areas.\n * The share is `undefined` when the drawn total is not positive.\n *\n * A tile's and a group's accessible name (`labels.tile`, `labels.group`, 0.52) says the\n * value too, asked of the formatter as the tooltip asks it, share and all, and its text\n * is used; markup is said as the bare number. The name says the share itself, so a\n * formatter that writes it into its text has it said twice — say so with\n * {@link TreemapProps.shareInValue}.\n *\n * Default: the bare number through `Intl.NumberFormat` in the provider's locale (0.52,\n * DOC-10: before, the runtime's). A host with a currency passes this — the kit knows\n * none.\n */\n valueFormatter?: (value: number, share: number | undefined) => ReactNode;\n /**\n * The `valueFormatter`'s text already says the share (\"CHF 412.30 · 12%\"), so a tile's\n * and a group's accessible name leave their own \" (12%)\" out (0.53, keksdose after 0.52):\n * the map hands `labels.tile` and `labels.group` the share as `\"\"`, and every catalogue's\n * template drops it there, in its own typography. Before, the only way out was to replace\n * both templates, and with them the kit's seven catalogues. The tooltip is the same either\n * way: it prints what the formatter prints. A formatter whose value is markup is said as\n * the bare number, so its share is then not said at all.\n *\n * Default `false`: the name says the share.\n */\n shareInValue?: boolean;\n /**\n * What the map SHOWS, in a sentence — \"Rent is three fifths of the month.\" — the\n * figure's accessible description (0.52, DSP-5). Not drawn; a caption the eye should\n * read too belongs in the page.\n */\n description?: string;\n /** Per-map strings over `<UiKitProvider labels={{ treemap }}>`. */\n labels?: LabelOverride<TreemapLabels>;\n /** Chart height: a CSS length (`\"20rem\"`, `\"min(20rem, 50dvh)\"`), a Tailwind class,\n * or px. Default `\"20rem\"` — 320 px at Normal, growing with the text size like the\n * labels in the tiles (0.32, docs/text-size-harmonization.md §10.10). */\n height?: ChartHeight;\n /**\n * A tile was activated — by click, or by Enter/Space on a focused tile. Setting this\n * makes every tile a `role=\"button\"`.\n *\n * Either way every tile is reachable (0.52, DSP-5): ONE tab stop for the map, the\n * arrows (←/→ along the reading direction, ↓/↑), Home and End between tiles in the\n * order given, each named \"name: value (share)\" (`labels.tile`) — a `role=\"img\"` to read\n * when nothing listens, as PieChart's slices. Before, a map nobody clicked was nothing\n * to a screen reader, and a clickable tile said its name alone, one tab stop each.\n *\n * In a map with groups (`TreemapNode.children`, 0.52) the walk is the data's own order,\n * depth first: a group's frame, then its tiles, then the next group. A frame is named\n * by `labels.group` (\"Interspar, group: €1,240 (40%)\"), a tile inside one by\n * `labels.inGroup` round its own name (\"Rankweil: €412 (12%), in Interspar\"). Only\n * tiles reach this callback (a frame reaches `onGroupClick`); a tile inside a group is\n * handed a third argument, the ids of its groups, outermost first. Optional in the type,\n * so a forwarder calling this with two arguments still type-checks — and absent for a\n * tile at the top, so a flat map calls it exactly as before.\n */\n onNodeClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /**\n * A group's frame was activated — by click on its band or margin, or by Enter/Space on\n * the focused frame (0.52). Setting this makes every frame a `role=\"button\"`; without it\n * a frame is a `role=\"img\"`, still a stop of the map's keyboard so its total is heard.\n * `groups` as for {@link TreemapProps.onNodeClick}: the groups round this one, passed\n * only for a group inside another.\n */\n onGroupClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /**\n * The node names are the user's own data (payees, not categories): the tile labels\n * and the whole tooltip bubble get `data-private`, the same attribute the kit's\n * `Tooltip redact` and `ChartTooltipContent`'s value cell carry, so the host's one\n * privacy rule reaches them. Off by default — a category name is a label of the app.\n *\n * Every tile is tagged as a whole too, clickable or not (0.52): its accessible name\n * (`labels.tile`) says the name and the value, in an attribute a host's redaction\n * reaches only through the tag.\n *\n * - `true`: every node — the tiles, and a group's frame (its `<g>` and its band's name)\n * and tooltip bubble too.\n * - `\"tiles\"` (0.53, keksdose after 0.52): the tiles as `true` tags them, and no group —\n * for groups that are public names over private ones, as chains (Migros, Coop) over\n * the shops a user paid. Its frame's band, its frame and its bubble's name are\n * untagged, and so is the frame's accessible name (`labels.group`), which says the\n * group's TOTAL: use it only where the groups' names and totals are not the private\n * part. The bubble's value cell is tagged whatever this says, as in every chart.\n * A tile's name still says its group's (`labels.inGroup`), under the tile's tag.\n */\n redactNames?: boolean | \"tiles\";\n /**\n * A second, smaller line under a tile's label — one extra figure per node that the\n * name cannot carry (a year-on-year `+12%`, `new`). Return `undefined` for a node\n * with nothing to add. Never tagged `data-private`: a ratio reveals no amount.\n *\n * ON THE TILE rather than in the tooltip, for two reasons. The tooltip identifies a\n * node by name or by raw value, so a per-node note would be keyed on an amount (two\n * nodes of equal size would swap notes). And a tooltip is a pointer affordance: a\n * phone never hovers, so a figure that only exists on hover does not exist there.\n *\n * Asked for tiles only: a group's band holds its name and nothing else.\n */\n nodeNote?: (id: string) => string | undefined;\n /**\n * Draw at most this many tiles (the first N drawable nodes, in the order given —\n * sort first). Unbounded by default. A tile under ~44x20px has no room for a label,\n * and a map made of unlabelled rectangles answers nothing; around 24 is where the\n * smallest tile of a skewed series reaches that floor on a phone-width card. The\n * caller owns the \"N more not shown\" line, since it knows how many it passed.\n *\n * With groups, the tiles are counted across them in the order given, depth first; a\n * group left with none is not drawn, and a group's frame is not a tile.\n */\n maxTiles?: number;\n /**\n * The categorical ramp tiles take by index, as concrete hex. Default: the page's\n * live `--chart-1…8` tokens (see `useChartTokenHex`), cycled after eight like\n * `paletteFor` (0.49). Pass one when the host\n * already holds the active palette — e.g. `createPaletteStore`'s `useChartHex()` —\n * so a caller colouring a legend beside the map is guaranteed the same list.\n */\n colors?: readonly string[];\n /**\n * How tiles are parted (0.51.6, keksdose dev #642, Marcel: \"The borders do not look\n * nice when changing from tile to tile. Can this be uniform black?\" — and then, on the\n * contrast rule that would go with it: follow the contrast setting).\n *\n * - `\"auto\"` (default): `\"uniform\"` at Standard contrast, `\"contrast\"` while More\n * contrast is in force (`<html data-contrast=\"more\">`: the reader chose it, or chose\n * System on a device that asks for more). Followed live.\n * - `\"uniform\"`: one 2px gutter in the page's ground colour (`--bg-page`, near-black\n * in the dark theme) round every tile, and nothing else. Tiles always abut, so the\n * gutter parts them; each tile is named by its label.\n * - `\"contrast\"`: the 0.49 rule. The gutter is the card's colour (`--bg-surface`),\n * and a tile whose hue is under 3:1 against the card gets a 1px ink edge inside it\n * (WCAG 1.4.11, `useChartEdge`). In the dark theme that ink is light, so some tiles\n * carry a white outline and their neighbours none — the unevenness #642 saw, and\n * the price of the edge for a reader who asked for it.\n */\n edges?: TreemapEdges;\n}\n\n/** {@link TreemapProps.edges}. The switch itself is chart-edge's `useEdgesInForce`,\n * which SeriesChart's bars and areas follow too since 0.52 (DSP-18). */\nexport type TreemapEdges = ChartEdges;\n\n// ── The categorical ramp, as concrete hex ──────────────────────────────────\n//\n// The label ink is measured against the tile's fill, so the fill has to be a colour\n// and not a reference to one. keksdose read it from its palette STORE, which is an\n// app object the kit cannot import. What the kit does own is the token layer: whether\n// a palette arrives through `applyTokenSet` (inline properties on <html>) or through\n// tokens.css (`:root` / `.dark`), the resolved `--chart-N` on <html> is the colour\n// every other chart on the page is being painted with. So that is what is read, and\n// re-read whenever <html>'s class or style changes — which is exactly when a theme\n// toggle or a palette switch lands.\n\nconst CHART_TOKENS = PALETTE_SIZE;\n\n/** The resolved ramp, and after a `|` the card's colour, joined into ONE string so\n * `useSyncExternalStore` compares a primitive: a fresh array per read would never be\n * `Object.is`-equal to the last and would re-render forever. */\nfunction readChartTokens(): string {\n const root = document.documentElement;\n const fallback = root.classList.contains(\"dark\") ? PALETTE_HEX.dark : PALETTE_HEX.light;\n const style = getComputedStyle(root);\n const ramp = Array.from({ length: CHART_TOKENS }, (_, i) => {\n const v = style.getPropertyValue(`--chart-${i + 1}`).trim();\n // An unresolved token (a host that never loaded tokens.css, a test DOM) falls\n // back to the built-in ramp for the current theme rather than to no colour.\n return parseHex(v) ? v : fallback[i];\n }).join(\",\");\n // The card, which `TreemapNode.shade` mixes a hue into (0.52). No fallback: a page that\n // declares no `--bg-surface` hex leaves nothing to mix into, and the tile keeps its hue.\n const card = style.getPropertyValue(\"--bg-surface\").trim();\n return `${ramp}|${parseHex(card) ? card : \"\"}`;\n}\n\nfunction subscribeChartTokens(onChange: () => void): () => void {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"data-theme\"],\n });\n return () => observer.disconnect();\n}\n\nconst SERVER_TOKENS = `${PALETTE_HEX.light.join(\",\")}|`;\n\ninterface ChartTokens {\n ramp: readonly string[];\n /** `--bg-surface` as hex, or null when the page declares none. */\n card: string | null;\n}\n\nfunction useChartTokenHex(): ChartTokens {\n const joined = useSyncExternalStore(subscribeChartTokens, readChartTokens, () => SERVER_TOKENS);\n // One object per reading, not per render: the map's nodes are memoised on it.\n return useMemo(() => {\n const [ramp = \"\", card = \"\"] = joined.split(\"|\");\n return { ramp: ramp.split(\",\"), card: card || null };\n }, [joined]);\n}\n\nconst CHART_TOKEN_REF = /^var\\(\\s*--chart-(\\d+)\\s*\\)$/;\n\n/**\n * The hex a tile fill measures as: the fill itself when it is hex, the live token when\n * it is a bare `var(--chart-N)` (what `paletteFor` returns), otherwise null. Without\n * the token case a hand-built recharts map coloured with the kit's own `paletteFor`\n * got `currentColor` ink — dark page text on dark indigo in the light theme.\n */\nfunction measurableFill(fill: string, tokens: readonly string[]): string | null {\n if (parseHex(fill)) return fill;\n const m = CHART_TOKEN_REF.exec(fill.trim());\n const hex = m ? tokens[Number(m[1]) - 1] : undefined;\n if (hex && parseHex(hex)) return hex;\n // Any other bare token the page declares — `var(--chart-muted)` for a rest bucket,\n // `var(--chart-9)`, a money colour — read off <html> as it resolves.\n if (typeof document === \"undefined\") return null;\n return resolveChartColor(fill, getComputedStyle(document.documentElement));\n}\n\n// ── What a tile reads from its map ───────────────────────────────────────\n//\n// 0.52 (refactor audit 2026-10-30, DSP-22): every tile resolved the token ramp, the ink\n// edge and the edge rule itself — three stores, each with a MutationObserver on <html>, so\n// 72 on keksdose's 24-tile maps, and up to 29 custom properties re-read on every render of\n// every tile. `Treemap` resolves them once and hands them to every tile it draws; a\n// TreemapCell drawn in a caller's own recharts `<Treemap>` has no map above it and\n// resolves its own.\n\ninterface TileContext {\n tokens: readonly string[];\n edgeFor: (fill: string | undefined) => string | undefined;\n edges: \"uniform\" | \"contrast\";\n /** The map's keyboard (DSP-5): absent for a standalone cell. */\n keys?: TileKeys;\n /** The pointer is on the node at `path`: what the map's tooltip says, standing where the\n * node is drawn. Absent for a standalone cell, whose tooltip is its recharts map's. */\n hover?: (path: string) => void;\n}\n\n/**\n * Keyed by a node's PATH (`MapNode.path`), not its id: a group and a tile may share an id\n * — chain 12 and shop 12 come from two tables — and keyed by id the map had two tab stops,\n * the frame said the tile's name and the arrows stuck (0.52 code review of treemap.tsx,\n * finding 3).\n */\ninterface TileKeys {\n /** A tile's or a group's accessible name (`labels.tile`, `labels.group`). */\n name: (path: string) => string | undefined;\n onFocus: (path: string) => void;\n /** A node's element as it mounts (and `null` as it goes): the map sets its `tabindex` and\n * gives it back the focus its previous element held (see the map's keyboard). */\n register: (path: string, element: SVGGElement | null) => void;\n /** Moves focus for an arrow, Home or End pressed on node `path`; `false` for any other key. */\n move: (path: string, event: KeyboardEvent<SVGGElement>) => boolean;\n}\n\n// ── Groups: a frame round their tiles ─────────────────────────────────────\n//\n// 0.52 (keksdose dev #642). A group is a frame with its name in a band along the top, its\n// tiles inside it. recharts lays nested data out edge to edge, with no room for a band, so\n// the map lays it out ITSELF (treemap-layout.ts): squarified as recharts does it, each\n// group given its frame's area on top of its tiles' — so a tile's area stays its share of\n// the picture in any group (0.52 code review of treemap.tsx, finding 1) — and computed up\n// front from the drawn nodes and the chart's size. Before, a group's cell made the room\n// during render and left it in a module cache for its children's cells, which held only\n// while recharts rendered a group before its children (finding 8).\n//\n// ── The picture ───────────────────────────────────────────────────────────\n//\n// 0.55 (held back from 0.53's code review): the map draws its nodes itself, an `<svg>` of\n// its own tiles in the order its keyboard walks them (`TreemapPlot`), with its own bubble\n// beside it (`TreemapTooltip`). Up to 0.54 recharts' `<Treemap>` still rendered each node's\n// cell and the tooltip, and the map worked round it at every turn: recharts laid the nodes\n// out again for nothing, keyed each by where IT had put it (so new data remounted the tile\n// holding the focus), anchored its bubble at the rectangle IT had laid out (so the map\n// stood it again), and brought its chart store along — about 240 kB of recharts, minified,\n// on a page whose one chart is a map. What stays is the shell every chart is drawn in,\n// `ChartContainer`.\n\n// ── Tile ──────────────────────────────────────────────────────────────────\n\n/** The name's size in px at Normal; a tile draws it × the text size's scale (§10.10), and\n * every distance below that is measured in lines grows with it. */\nconst LABEL_FONT_SIZE = 12;\n/** The optional second line, a step down from the name so the tile reads name-first. */\nconst NOTE_FONT_SIZE = 11;\n\n/**\n * Fit a label to its tile: the text itself when it fits, an ellipsised head when it\n * does not, `null` when not even three characters would. Without this a long name ran\n * on across its neighbours and collided with THEIR labels. 0.58em is the average\n * advance of a UI sans at this weight; it only needs to be close, since the tile keeps\n * 6px of padding either side. The full name stays in the tooltip.\n */\nexport function fitLabel(name: string, boxWidth: number, fontSize: number): string | null {\n const max = Math.floor((boxWidth - 12) / (fontSize * 0.58));\n if (max < 3) return null;\n return name.length <= max ? name : `${name.slice(0, max - 1).trimEnd()}…`;\n}\n\n/** A tile's text, painted over a halo in its tile's colour (see `TreemapCell`). */\nconst haloOf = (halo: string) => ({\n stroke: halo,\n strokeWidth: 2.5,\n strokeLinejoin: \"round\" as const,\n paintOrder: \"stroke\",\n});\n\n/** A band's name: the page's text on the well, 8.2:1 at the least in every preset, 7.4:1\n * under More contrast (measured 0.52) — no halo, nothing to stand out from; `none`\n * against the inherited chart stroke. */\nconst BAND_INK = { fill: \"var(--text-primary)\", stroke: \"none\" };\n\n/** What recharts hands a `content` element for each node, plus the three options the\n * chart forwards. Everything optional because recharts clones the element it was\n * given and fills the geometry in — the element as written carries only the options. */\nexport interface TreemapCellProps {\n depth?: number;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n name?: string;\n fill?: string;\n /** See {@link TreemapNode.labelColor}; recharts hands each node's fields to its cell. */\n labelColor?: string;\n id?: string;\n /** Called with `groups` as its third argument when the cell has any; see\n * {@link TreemapProps.onNodeClick}. */\n onNodeClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /** A group's cell's click; see {@link TreemapProps.onGroupClick}. */\n onGroupClick?: (id: string, name: string, groups?: readonly string[]) => void;\n /** The ids of the groups round the node, outermost first. `Treemap` gives it every tile\n * it draws; a cell drawn in a caller's own map has it if the data does. */\n groups?: readonly string[];\n /** The node's place in the tree, which `Treemap` gives every tile it draws: its key in\n * the map's keyboard, where an id may repeat between a group and a tile. */\n path?: string;\n /** Filled in by recharts for a GROUP: the nodes laid out inside it. A cell with\n * children draws a frame, not a tile (0.52). */\n children?: readonly object[] | null;\n /** See {@link TreemapProps.redactNames}: `\"tiles\"` tags a cell with no `children`, and\n * leaves a group's frame and band untagged (0.53). */\n redactNames?: boolean | \"tiles\";\n nodeNote?: (id: string) => string | undefined;\n /** The page's reading direction, which `Treemap` reads off its own root. In `rtl`\n * the label stands in the tile's top RIGHT corner and is shaped right-to-left, so a\n * clipped name ends in its ellipsis on the reading end. Default `ltr`. */\n dir?: Direction;\n /** See {@link TreemapProps.edges}. Default `auto`. Read by a cell drawn on its own; a\n * `Treemap` hands its tiles its own `edges`. */\n edges?: TreemapEdges;\n}\n\n/**\n * One tile, for a caller drawing its own recharts `<Treemap>`: the `content` that gives it\n * the map's label rules. `Treemap` itself draws the same tile (0.55, \"The picture\").\n *\n * recharts' default cell inherits the chart-level `stroke`, an SVG presentation\n * attribute that lands on the `<svg>` root and cascades into the label — so the glyphs\n * were painted with a 1px outline ON TOP of themselves, which reads as blur. This cell\n * draws its own label with a halo in the TILE colour painted BEHIND the fill\n * (`paint-order: stroke`), which defeats any inherited stroke and keeps the text solid\n * where it overlaps a lighter or darker part of its own tile.\n *\n * Drawn on its own it is a stop only as a button (`onNodeClick`), named by `name`; a\n * `Treemap`'s tile is one stop of the map's keyboard, named with its value and share\n * (0.52).\n *\n * A node with `children` is a GROUP (0.52): the cell draws a frame in the well's colour\n * (`--bg-surface-2`), with the group's name in a band along its top where the group has\n * the room (61px at Normal, treemap-layout's `BODY_SHARE`). In a caller's own nested\n * recharts `<Treemap>` the group's tiles are drawn where recharts lays them, over the\n * frame — only `Treemap` makes the room, laying them out in the frame's body.\n */\nexport function TreemapCell(props: TreemapCellProps) {\n // A cell with no `Treemap` above it resolves what the map would have handed it.\n const tokens = useChartTokenHex().ramp;\n const edgeFor = useChartEdge();\n const edges = useEdgesInForce(props.edges ?? \"auto\");\n const map = useMemo(() => ({ tokens, edgeFor, edges }), [tokens, edgeFor, edges]);\n return <Tile {...props} map={map} />;\n}\n\nfunction Tile({\n depth,\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n name,\n fill,\n labelColor,\n id,\n onNodeClick,\n onGroupClick,\n groups,\n path,\n children,\n redactNames,\n nodeNote,\n dir = \"ltr\",\n map,\n place,\n}: TreemapCellProps & { map: TileContext; place?: Placed }) {\n // Before the early return: a hook has to run on every render of the cell.\n const { scale } = useTextSize();\n const { tokens: tokenHex, edgeFor, edges, keys } = map;\n // Depth 0 is recharts' synthetic root, which spans the whole chart; the map draws none.\n if (!depth) return null;\n // In a Treemap, where the map's layout puts the node; on its own, where recharts laid it.\n const box = place?.box ?? { x, y, width, height };\n // Whole pixels: a rect on a half pixel renders its edge across two half-lit\n // columns, and the text baseline inherits that blur.\n const px = Math.round(box.x);\n const py = Math.round(box.y);\n const pw = Math.round(box.width);\n const ph = Math.round(box.height);\n const isGroup = children != null && children.length > 0;\n const group = !isGroup\n ? undefined\n : place?.body\n ? { banded: place.banded ?? false, body: place.body }\n : groupBody({ x: px, y: py, width: pw, height: ph }, scale);\n const tileFill = fill ?? \"var(--chart-1)\";\n const measured = measurableFill(tileFill, tokenHex);\n const ink = labelColor ?? (measured ? textOn(measured) : \"currentColor\");\n // `contrast`: a tile in a hue under 3:1 against the card gets a 1px ink edge inside\n // the gutter (WCAG 1.4.11, anyplot's rule); a hue that clears it stays bare.\n // `uniform`: no per-tile edge; every tile is parted by the same gutter.\n // A group's frame is the well, 1.09–1.24:1 from the card in every preset and contrast\n // (measured 0.52): under `contrast` it always takes the edge.\n const edge =\n edges !== \"contrast\" ? undefined : group ? CHART_ROLES.outline : measured ? edgeFor(measured) : undefined;\n // The halo only on an OPAQUE fill we could measure. A translucent fill (a\n // `color-mix(… transparent)`) composited twice — once as the tile, once as the halo\n // over it — draws a darker ring round every glyph, which read as doubled text on the\n // faded tiles. `none` still overrides the inherited chart `stroke`.\n const halo = measured ? tileFill : \"none\";\n // Below ~44x20 there is no room for a readable word, so no label at all rather than\n // a one-letter stub bleeding over the tile edge. A group's name only in its band.\n const labelSize = LABEL_FONT_SIZE * scale;\n const noteSize = NOTE_FONT_SIZE * scale;\n const roomForName = group ? group.banded : pw >= 44 * scale && ph >= 20 * scale;\n const label = roomForName ? fitLabel(name ?? \"\", pw, labelSize) : null;\n // Only under a label that is itself drawn — a stray \"+12%\" on an unnamed rectangle\n // names nothing — and only where a second baseline fits: the note's baseline sits\n // 29px down and an 11px glyph hangs ~2px below it, so 36 leaves the same ~5px under\n // the note that the label keeps under itself at the 20px floor. Never in a band.\n const rawNote = !group && id != null && nodeNote ? nodeNote(id) : undefined;\n const note = label != null && rawNote != null && ph >= 36 * scale ? fitLabel(rawNote, pw, noteSize) : null;\n // The label's START edge, 6px in from the tile's. The tiles themselves are laid out\n // physically (the plot is not mirrored, see `ChartContainer`) and the chart's SVG is\n // pinned `ltr`, so the direction is set on each <text> explicitly: `start` is then\n // the right end in RTL, and the label grows leftwards from the tile's right edge.\n const textX = dir === \"rtl\" ? px + pw - 6 : px + 6;\n // A tile's click is `onNodeClick`, a frame's `onGroupClick`. The groups go along only\n // when there are any, so a flat map calls back exactly as it did before 0.52.\n const click = group ? onGroupClick : onNodeClick;\n const activate =\n id != null && click\n ? () => (groups && groups.length > 0 ? click(id, name ?? \"\", groups) : click(id, name ?? \"\"))\n : undefined;\n // Inside a Treemap every tile and frame is a stop of the map's one roving tab stop,\n // named with its value and share; a standalone cell is a stop of its own only when it\n // is a button.\n const roving = path != null && keys ? keys : undefined;\n const focusable = roving !== undefined || activate !== undefined;\n // `\"tiles\"` (0.53, keksdose after 0.52) leaves a group's frame and band untagged: its\n // groups are public chain names, and its blur on `[data-private]` blurred them with the\n // payees. A truthy test on the union would have tagged them still.\n const redact = redactNames === true || (redactNames === \"tiles\" && !group);\n const onKeyDown = focusable\n ? (e: KeyboardEvent<SVGGElement>) => {\n if (roving?.move(path!, e)) {\n e.preventDefault();\n return;\n }\n if (!activate || (e.key !== \"Enter\" && e.key !== \" \")) return;\n e.preventDefault();\n activate();\n }\n : undefined;\n return (\n <g\n ref={roving ? refFor(roving.register, path!) : undefined}\n onClick={activate}\n onKeyDown={onKeyDown}\n onFocus={roving ? () => roving.onFocus(path!) : undefined}\n onMouseEnter={map.hover && path != null ? () => map.hover!(path) : undefined}\n // A clickable tile is a button to a keyboard and a screen reader too; the\n // drilldown it opens was mouse-only in the app this came from. Under `redactNames`\n // the group carries `data-private` whenever it carries a name, button or not: the\n // name says the node's name and value, and an untagged `aria-label` is read by a\n // session replay or a redaction keyed on `[data-private]` (0.52, code review of\n // feat/0.52.0 — DSP-5 named every tile, and this tagged only the buttons).\n role={activate ? \"button\" : roving ? \"img\" : undefined}\n // In a map, -1 as drawn: the map moves its one stop on the elements in place.\n tabIndex={roving ? -1 : activate ? 0 : undefined}\n aria-label={focusable ? (roving?.name(path!) ?? name) : undefined}\n data-private={focusable && redact ? \"\" : undefined}\n data-group={group ? \"\" : undefined}\n cursor={activate ? \"pointer\" : undefined}\n // CSS beats a presentation attribute, so this recolours the rect's own stroke\n // for as long as the tile has keyboard focus.\n className={focusable ? \"outline-none [&:focus-visible>rect]:stroke-[var(--brand)]\" : undefined}\n >\n {/* The 2px gutter: the page's ground (`uniform`, the same dark line round every\n tile in the dark theme) or the card showing through (`contrast`). Never a white\n grid: on a dark surface white lines glare harder than the tiles. A group's frame\n is the well, the inset surface: the page's own colour in light, a step up from\n it in dark, and never one of the hues its tiles are told apart by. */}\n <rect\n x={px}\n y={py}\n width={pw}\n height={ph}\n fill={group ? \"var(--bg-surface-2)\" : tileFill}\n stroke={edges === \"contrast\" ? \"var(--bg-surface)\" : \"var(--bg-page)\"}\n strokeWidth={2}\n rx={3}\n />\n {edge && pw > 3 && ph > 3 && (\n <rect\n data-edge=\"\"\n x={px + 1.5}\n y={py + 1.5}\n width={pw - 3}\n height={ph - 3}\n fill=\"none\"\n stroke={edge}\n strokeWidth={1}\n rx={2}\n style={{ pointerEvents: \"none\" }}\n />\n )}\n {label != null && (\n <text\n // A plain attribute, so the host's `[data-private]` rule reaches an SVG\n // <text> exactly as it reaches a table cell.\n data-private={redact ? \"\" : undefined}\n x={textX}\n // A band's name centred in its 20px; a tile's where its floor leaves room below.\n y={py + (group ? 14 : 15) * scale}\n direction={dir}\n {...(group ? BAND_INK : { fill: ink, ...haloOf(halo) })}\n fontSize={labelSize}\n fontWeight={600}\n style={{ pointerEvents: \"none\" }}\n >\n {label}\n </text>\n )}\n {note != null && (\n <text\n x={textX}\n y={py + 29 * scale}\n direction={dir}\n fill={ink}\n fillOpacity={0.85}\n {...haloOf(halo)}\n fontSize={noteSize}\n fontWeight={500}\n style={{ pointerEvents: \"none\" }}\n >\n {note}\n </text>\n )}\n </g>\n );\n}\n\n// ── Chart ─────────────────────────────────────────────────────────────────\n\n// Nodes carry their own `fill`, and `ChartTooltipContent` falls back to it for the\n// swatch, so no series config is needed.\nconst TREEMAP_CONFIG: ChartConfig = {};\n\n/**\n * The shared tooltip with the whole bubble tagged `data-private`, for names that are\n * the user's own data.\n *\n * The bubble, not just the name: `ChartTooltipContent` prints the name either from\n * `item.name` or from a config `label`, and only the config route could be tagged — but\n * a config entry keyed by a payee name is not a CSS identifier, and `ChartStyle` would\n * (rightly) refuse it. The bubble holds the name and the value, and the value cell is\n * already `data-private`, so tagging the wrapper hides nothing extra.\n *\n * The map's tooltip CLONES the bubble element to hand it `active`/`payload`, as recharts'\n * did, so this has to BE that element — a `<div>` wrapped around `<ChartTooltipContent />`\n * in the JSX would take those props itself and hand the tooltip nothing.\n *\n * `groups: false` is `redactNames=\"tiles\"` (0.53): a group's bubble is left untagged, as its\n * frame is. The payload says which node is hovered — the node itself, with its `children`\n * for a group and none for a tile — so anything else, or no payload, is tagged.\n */\nfunction RedactedTooltipContent({\n groups = true,\n ...props\n}: ComponentProps<typeof ChartTooltipContent> & { groups?: boolean }) {\n const children = props.payload?.[0]?.payload?.children;\n const group = Array.isArray(children) && children.length > 0;\n return (\n <div data-private={groups || !group ? true : undefined}>\n <ChartTooltipContent {...props} />\n </div>\n );\n}\n\n/** Which node the pointer is on, by path: a store of one string, so that only the tooltip\n * re-renders when it moves — not the map, nor every tile. `clear` when it leaves the\n * chart, or on Escape. Its box is read off the layout as drawn now, so new data or a\n * resize under a still pointer moves the bubble with its node, or takes it away with it. */\ninterface Anchor {\n read: () => string | null;\n subscribe: (onChange: () => void) => () => void;\n set: (path: string | null) => void;\n clear: () => void;\n}\n\nfunction createAnchor(): Anchor {\n let at: string | null = null;\n const listeners = new Set<() => void>();\n const set = (path: string | null) => {\n if (path === at) return;\n at = path;\n listeners.forEach((onChange) => onChange());\n };\n return {\n read: () => at,\n subscribe: (onChange) => {\n listeners.add(onChange);\n return () => listeners.delete(onChange);\n },\n set,\n clear: () => set(null),\n };\n}\n\n/** The slide recharts gave its bubble between two positions, kept for the kit's own. */\nconst TOOLTIP_SLIDE = \"transform 400ms ease\";\n\n/**\n * The map's tooltip (0.55): the bubble for the node the pointer is on, stood at the node AS\n * DRAWN, 10px off its centre towards the chart's middle so it does not run off the side\n * the node is on. `content` is the bubble, cloned with `active` and the node as its\n * `payload`, in the shape recharts handed it: `ChartTooltipContent` reads the name, the\n * value and the node's own `fill` for its swatch.\n *\n * It behaves as recharts' bubble did, through which the map drew it up to 0.54: entering a\n * node shows it, leaving the chart takes it away, Escape dismisses it until the pointer\n * enters another node (WCAG 1.4.13, content on hover), and it slides from node to node\n * unless the reader asked for less motion. Its box keeps recharts' class,\n * `recharts-tooltip-wrapper`: `ChartTooltipContent` re-measures its nudge into the viewport\n * when that box's slide ends, and a host's CSS for a chart's bubble reaches it there too.\n */\nfunction TreemapTooltip({\n content,\n anchor,\n nodes,\n placed,\n width,\n height,\n}: {\n content: ReactElement;\n anchor: Anchor;\n nodes: ReadonlyMap<string, MapNode>;\n placed: ReadonlyMap<string, Placed>;\n width: number;\n height: number;\n}) {\n const at = useSyncExternalStore(anchor.subscribe, anchor.read, anchor.read);\n const shown = at !== null;\n useEffect(() => {\n if (!shown) return;\n const onKeyDown = (event: globalThis.KeyboardEvent) => {\n if (event.key === \"Escape\") anchor.clear();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [shown, anchor]);\n // A node that is gone takes the bubble with it for good: the node coming back with new\n // data is not the pointer coming back to it.\n useEffect(() => {\n if (at !== null && !nodes.has(at)) anchor.clear();\n }, [at, nodes, anchor]);\n const node = at === null ? undefined : nodes.get(at);\n // The box the tile draws: the layout's, on whole pixels already.\n const box = at === null ? undefined : placed.get(at)?.box;\n if (!node || !box) return null;\n const cx = box.x + box.width / 2;\n const cy = box.y + box.height / 2;\n const left = cx > width / 2;\n const up = cy > height / 2;\n return (\n <div\n className=\"recharts-tooltip-wrapper\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n pointerEvents: \"none\",\n transform: `translate(${cx + (left ? -10 : 10)}px, ${cy + (up ? -10 : 10)}px)`,\n // The bubble's corner at that point: towards the middle, whatever its size.\n translate: `${left ? \"-100%\" : \"0\"} ${up ? \"-100%\" : \"0\"}`,\n transition: prefersReducedMotion() ? undefined : TOOLTIP_SLIDE,\n }}\n >\n {cloneElement(content, {\n active: true,\n payload: [{ dataKey: \"size\", name: node.name, value: node.size, color: node.fill, payload: node }],\n } as Partial<ComponentProps<typeof ChartTooltipContent>>)}\n </div>\n );\n}\n\n/** What `Treemap` draws per node: a tile, or a group with `children`. Every field a tile\n * reads is here; a field left out of this never reaches the tile (0.8.0 shipped\n * `labelColor` without it — keksdose). A type, not an interface: the tooltip hands it on\n * as a `payload`, a `Record<string, unknown>`, whose index signature only an alias meets. */\ntype MapNode = {\n id: string;\n name: string;\n /** A tile's value; a group's, its drawn tiles' together. The layout and the tooltip read\n * this field. */\n size: number;\n fill: string;\n labelColor?: string;\n /** The ids of the groups round the node, outermost first. */\n groups: readonly string[];\n /** The node's key in the map's keyboard and names: its id and its groups' (see TileKeys). */\n path: string;\n children?: MapNode[];\n};\n\n/** What a group hands down to the tiles inside it that set none of their own. */\ninterface Paint {\n fill: string;\n labelColor?: string;\n shade?: number;\n}\n\n/**\n * `data` as drawn: tiles with a positive value, at most `maxTiles` of them in the order\n * given (depth first), and the groups that still hold any. `shaded` turns a fill and a\n * `shade` into the fill drawn.\n *\n * Coloured by index into `data` AS GIVEN, before anything is dropped: a caller painting a\n * legend or a second map from the same list must land on the same hue for the same row,\n * and filtering first would shift every colour after a gap. Below the top a node takes\n * its group's colour, so a group's tiles share one hue unless they set their own.\n */\nfunction mapNodes(\n data: readonly TreemapNode[],\n ramp: readonly string[],\n maxTiles: number | null | undefined,\n shaded: (fill: string, shade: number | undefined) => string,\n): MapNode[] {\n // Whole tiles, as `slice(0, maxTiles ?? undefined)` counted them up to 0.51: 2.5 draws 2,\n // NaN none and null every one (0.52 code review of treemap.tsx, finding 4 — a bare\n // counter drew 3, and every tile; deep review, charts, finding 2 — Math.floor(null) is 0,\n // and a JS caller's or a `number | null` setting's null drew an empty card).\n let left = maxTiles == null ? Infinity : Number.isNaN(maxTiles) ? 0 : Math.floor(maxTiles);\n const walk = (list: readonly TreemapNode[], groups: readonly string[], paint: Paint | undefined): MapNode[] =>\n list.flatMap((d, idx): MapNode[] => {\n const fill = d.fill ?? paint?.fill ?? ramp[idx % ramp.length]!;\n // An inherited fill brings its ink along (a `color-mix()` group needs its\n // `labelColor`); a fill of the tile's own is measured again.\n const labelColor = d.labelColor ?? (d.fill === undefined ? paint?.labelColor : undefined);\n const shade = d.shade ?? paint?.shade;\n if (d.children && d.children.length > 0) {\n const children = walk(d.children, [...groups, d.id], { fill, labelColor, shade });\n if (children.length === 0) return [];\n const size = children.reduce((acc, c) => acc + c.size, 0);\n return [{ id: d.id, name: d.name, size, fill, groups, path: pathOf(groups, d.id), children }];\n }\n if (!(Number.isFinite(d.value) && d.value > 0) || left <= 0) return [];\n left -= 1;\n const drawn = shaded(fill, shade);\n // An ink the GROUP chose was chosen for the group's colour: once `shade` makes the\n // tile another hex, that hex is measured instead (0.52 code review of treemap.tsx,\n // finding 5 — white ink on a tile shaded almost to the card, ~1.3:1). The tile's\n // own `labelColor` still wins: the caller set it for this tile.\n const ink = d.labelColor ?? (drawn === fill ? labelColor : undefined);\n return [\n { id: d.id, name: d.name, size: d.value, fill: drawn, labelColor: ink, groups, path: pathOf(groups, d.id) },\n ];\n });\n return walk(data, [], undefined);\n}\n\n/** What every tile of a map is handed alike: the map's options, which a cell drawn in a\n * caller's own recharts map takes as props. */\ntype TileOptions = Pick<TreemapCellProps, \"onNodeClick\" | \"onGroupClick\" | \"redactNames\" | \"nodeNote\" | \"dir\">;\n\n/** A tile of the map's own picture: memoised, so a render of the map that changes nothing a\n * tile reads draws no tile again — its layout, fitted labels and note (0.52 code review of\n * feat/0.52.0; recharts' PureComponent kept that up to 0.54). */\nconst PlotTile = memo(Tile);\n\ninterface PlotProps {\n /** The chart's box in px: see {@link TreemapPlot}. Nothing is drawn at 0. */\n width?: number;\n height?: number;\n nodes: readonly MapNode[];\n /** `nodes` depth first: the order the tiles are drawn and walked in. */\n order: readonly MapNode[];\n options: TileOptions;\n map: TileContext;\n /** The tooltip's bubble, cloned per node by `TreemapTooltip`. */\n bubble: ReactElement;\n anchor: Anchor;\n}\n\n/**\n * The map's picture (0.55, see \"The picture\"): an `<svg>` of the map's own tiles and frames,\n * laid out for the box it is handed, and the bubble over it.\n *\n * The box is the chart's own, which `Treemap` measures on its root (`useBoxSize`) — or the\n * one a container hands its one child, which wins: recharts' `ResponsiveContainer` did so\n * up to recharts 2, and the kit's suites stand one in at 800x300.\n *\n * Drawn in `order`, the keyboard's: a group's frame before the tiles inside it, which paint\n * over it, so the document's order is the walk the arrows take. A node is keyed by its PATH:\n * recharts keyed it by where it had laid it out (`x-y-name`), so new data or a resize\n * remounted the node holding the focus, and its focus fell to <body> unless the map gave it\n * back (0.52 code review of treemap.tsx, finding 7). Now its element stays.\n *\n * The SVG takes no chart-level `stroke`: recharts spread one onto its root, from where it\n * inherited into every label (see TreemapCell). Each tile strokes its own rect. And it is\n * pinned `ltr`, as `ChartContainer` pins recharts' surface: the tiles are laid out\n * physically, and each label sets its own direction.\n */\nconst TreemapPlot = memo(function TreemapPlot({\n width = 0,\n height = 0,\n nodes,\n order,\n options,\n map,\n bubble,\n anchor,\n}: PlotProps) {\n // The band a group's name stands in grows with the text size, and its frame with it.\n const { scale } = useTextSize();\n const placed = useMemo(\n () => (width > 0 && height > 0 ? layoutTreemap(nodes, width, height, scale) : null),\n [nodes, width, height, scale],\n );\n const byPath = useMemo(() => new Map(order.map((node) => [node.path, node])), [order]);\n if (!placed) return null;\n return (\n <div className=\"relative\" style={{ width, height }}>\n <svg\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n className=\"block outline-none\"\n style={{ direction: \"ltr\" }}\n // The pointer leaving the chart takes the bubble away, as leaving recharts' wrapper\n // did. The SVG is the whole chart, and the bubble over it takes no pointer.\n onMouseLeave={anchor.clear}\n >\n {order.map((node) => (\n <PlotTile\n key={node.path}\n depth={node.groups.length + 1}\n id={node.id}\n name={node.name}\n fill={node.fill}\n labelColor={node.labelColor}\n groups={node.groups}\n path={node.path}\n // A group's cell is told it is one by its children, as recharts told it.\n children={node.children}\n {...options}\n map={map}\n place={placed.get(node.path)}\n />\n ))}\n </svg>\n <TreemapTooltip content={bubble} anchor={anchor} nodes={byPath} placed={placed} width={width} height={height} />\n </div>\n );\n});\n\n/**\n * An element's box in whole px, measured as it mounts and followed (`ResizeObserver`):\n * `null` until then, and where there is no layout to measure (SSR, jsdom) or nothing in it.\n * The size the map is laid out for (see {@link TreemapPlot}).\n */\nfunction useBoxSize(element: HTMLElement | null): { width: number; height: number } | null {\n const [size, setSize] = useState<{ width: number; height: number } | null>(null);\n useLayoutEffect(() => {\n if (!element) return;\n const take = (width: number, height: number) => {\n const w = Math.round(width);\n const h = Math.round(height);\n setSize((last) =>\n w > 0 && h > 0 ? (last?.width === w && last.height === h ? last : { width: w, height: h }) : null,\n );\n };\n // The layout box, not the bounding one: a chart in a scaled preview is drawn in its own\n // px, which a transform does not change.\n take(element.clientWidth, element.clientHeight);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n if (entry) take(entry.contentRect.width, entry.contentRect.height);\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [element]);\n return size;\n}\n\n/** One ref callback per map and path, the same on every render: a new function each time\n * makes React detach and attach the element on every render of the tile. A path no longer\n * drawn is let go as the map lays out its new data (see the map's keyboard). */\nconst REFS = new WeakMap<TileKeys[\"register\"], Map<string, (element: SVGGElement | null) => void>>();\n\nfunction refFor(register: TileKeys[\"register\"], path: string) {\n let byPath = REFS.get(register);\n if (!byPath) {\n byPath = new Map();\n REFS.set(register, byPath);\n }\n let ref = byPath.get(path);\n if (!ref) {\n ref = (element) => register(path, element);\n byPath.set(path, ref);\n }\n return ref;\n}\n\n/** A node's path: its groups' ids and its own, as JSON so that no id can fake a separator. */\nconst pathOf = (groups: readonly string[], id: string) => JSON.stringify([...groups, id]);\n\n/** `nodes` in the order the keyboard walks them: each group, then what is inside it. */\nfunction depthFirst(nodes: readonly MapNode[]): MapNode[] {\n return nodes.flatMap((n) => [n, ...(n.children ? depthFirst(n.children) : [])]);\n}\n\n/**\n * Tile chart: one rectangle per {@link TreemapNode}, sized by `value`, coloured by\n * index from the chart ramp (or by the node's own `fill`), labelled in whichever of the\n * kit's two inks measures better against it. Hover shows the name and the formatted\n * value; every tile is a keyboard stop named with its value and share (see\n * `onNodeClick`), and `onNodeClick` turns the tiles into buttons.\n *\n * A node with `children` is a group (0.52): a labelled frame with its tiles inside, in\n * its colour unless they set their own — see {@link TreemapNode.children}.\n *\n * Renders nothing when no node has a positive value — an empty box is a hole in the\n * card, and the caller's own sentence is the better answer there.\n *\n * Drawn by the kit (0.55): the layout, the SVG and the tooltip are the map's own, inside the\n * chart shell every chart shares, `ChartContainer` — whose `ResponsiveContainer` is all of\n * `recharts` it still needs.\n */\nexport function Treemap({\n data,\n valueFormatter,\n height = \"20rem\",\n onNodeClick,\n onGroupClick,\n redactNames = false,\n shareInValue = false,\n nodeNote,\n maxTiles,\n colors,\n edges = \"auto\",\n description,\n labels: labelsProp,\n style,\n ...rest\n}: TreemapProps) {\n // Resolved once for every tile, which is handed them (DSP-22).\n const { ramp: tokenHex, card } = useChartTokenHex();\n const edgeFor = useChartEdge();\n const edgesInForce = useEdgesInForce(edges);\n const labels = useKitLabels(\"treemap\", DEFAULT_TREEMAP_LABELS, labelsProp);\n // The provider's locale (0.52, refactor audit 2026-10-30, DOC-10): this was the one\n // formatter default in the kit that took the runtime's.\n const locale = useKitLocale();\n const numberFormat = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n // One decimal under 10 %, the rule PieChart says its shares by too (lib/share-format):\n // a tile of 0.4 % named \"0%\" says it is not there.\n const shareText = useMemo(() => shareFormat(locale), [locale]);\n // Read off the DOM after mount: the `dir` a treemap sits under is an ancestor's\n // attribute, and SVG text anchoring cannot follow it by CSS (see TreemapCell). Again\n // on new data, and when the root mounts: a map that had nothing to draw rendered none.\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n const takeRoot = useCallback((element: HTMLDivElement | null) => {\n rootRef.current = element;\n setRoot(element);\n }, []);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n useLayoutEffect(() => {\n setDir(dirOf(rootRef.current));\n }, [root, data]);\n // The box the map is laid out for (0.55): the root's own, as recharts measured it.\n const size = useBoxSize(root);\n const ramp = colors && colors.length > 0 ? colors : tokenHex;\n // Memoised: moving the tab stop re-renders the map, and fresh nodes would re-lay it.\n const nodes = useMemo(\n () =>\n mapNodes(data, ramp, maxTiles, (fill, shade) => {\n // `shade` (0.52): the hue mixed into the card as a hex, so it is measured like\n // any other. A fill or a card it cannot read keeps the full hue.\n if (shade === undefined || !Number.isFinite(shade) || shade >= 1 || !card) return fill;\n const hex = measurableFill(fill, tokenHex);\n return hex ? lerpHex(card, hex, shade) : fill;\n }),\n [data, ramp, maxTiles, tokenHex, card],\n );\n // The keyboard walks groups and tiles alike, in the data's order (see `onNodeClick`); the\n // picture is drawn in it too (`TreemapPlot`).\n const order = useMemo(() => depthFirst(nodes), [nodes]);\n // Where the pointer is, for the tooltip to stand at (`TreemapTooltip`).\n const [anchor] = useState(createAnchor);\n\n // The keyboard (DSP-5): one tab stop for the map, moved between tiles in `order` as\n // PieChart's is between slices. Each tile registers its element here as it mounts,\n // rather than being looked up.\n //\n // The stop is moved on the elements IN PLACE, as PieChart moves its own, and never\n // reaches the tiles' props: kept there, every arrow-key step was a new context and every\n // tile drew again — layout, fitted labels, notes (0.52 code review of treemap.tsx,\n // finding 6). A tile keeps its element across new data and a resize (it is keyed by its\n // path, see TreemapPlot; recharts remounted it, finding 7), but one can still leave\n // holding the focus: its node is gone, or the whole picture is (a box measured 0). The\n // path it held is kept for the commit, and a new element for it takes the focus back as\n // it mounts — or, once the node is gone, the stop does.\n const tiles = useRef(new Map<string, SVGGElement>());\n const orderRef = useRef(order);\n const stopRef = useRef<string | undefined>(undefined);\n const restoreRef = useRef<string | null>(null);\n /** The path holding the stop: the last one focused while it is drawn, else the first. */\n const stopOf = useCallback(() => {\n const drawn = orderRef.current;\n return drawn.some((n) => n.path === stopRef.current) ? stopRef.current : drawn[0]?.path;\n }, []);\n const applyStop = useCallback(() => {\n const at = stopOf();\n tiles.current.forEach((el, path) => el.setAttribute(\"tabindex\", path === at ? \"0\" : \"-1\"));\n return at;\n }, [stopOf]);\n const register = useCallback(\n (path: string, element: SVGGElement | null) => {\n if (element) {\n tiles.current.set(path, element);\n element.setAttribute(\"tabindex\", path === stopOf() ? \"0\" : \"-1\");\n // The kit's one rule (lib/focus-left `returnFocus`; 0.52 code review 2026-10-31,\n // held to 0.55): the path's new element, in the commit that removed its old one.\n if (restoreRef.current === path) {\n restoreRef.current = null;\n returnFocus({ opener: element });\n }\n return;\n }\n // Detached before it is removed: still the focused element if it held the focus.\n // Kept for this commit only — the node's new element mounts in it, or the effect below\n // hands the focus on. A node that left with no new data (`<Activity mode=\"hidden\">`, a\n // panel 0 wide) took the focus back whenever it was shown again, from wherever the\n // reader was by then (0.52 code review, deep, charts, finding 3).\n if (tiles.current.get(path) === document.activeElement) {\n restoreRef.current = path;\n queueMicrotask(() => {\n if (restoreRef.current === path) restoreRef.current = null;\n });\n }\n tiles.current.delete(path);\n },\n [stopOf],\n );\n const onFocus = useCallback(\n (path: string) => {\n stopRef.current = path;\n applyStop();\n },\n [applyStop],\n );\n // New data: the order the stop is read against, the stop set again on what is drawn now,\n // the focus of a node that is gone handed to the stop — and the ref callbacks of the paths\n // no longer drawn let go: a map that switched data (a month picker over payees) kept one\n // for every node it had ever drawn (0.52 code review, deep, charts, finding 11).\n useLayoutEffect(() => {\n orderRef.current = order;\n const refs = REFS.get(register);\n if (refs) {\n const drawn = new Set(order.map((n) => n.path));\n for (const path of refs.keys()) if (!drawn.has(path)) refs.delete(path);\n }\n const at = applyStop();\n if (restoreRef.current === null) return;\n restoreRef.current = null;\n // The node is gone: the stated fallback, the tile that holds the tab stop now.\n returnFocus({ fallback: [at ? tiles.current.get(at) : null] });\n }, [order, applyStop, register]);\n const move = useCallback((path: string, event: KeyboardEvent<SVGGElement>) => {\n const drawn = orderRef.current;\n const at = drawn.findIndex((n) => n.path === path);\n // The ends hold: a map is a plane, not PieChart's ring. → is the next tile in any page:\n // the tiles are laid out left to right whatever the reading direction (the plot is not\n // mirrored, see `ChartContainer`), and → read off the page walked an RTL page's map\n // backwards, against the picture (0.52 code review, deep, charts, finding 1).\n const to = at < 0 ? undefined : rovingTarget(event.key, \"ltr\", at, drawn.length, false);\n if (to === undefined) return false;\n const target = drawn[to];\n if (target) tiles.current.get(target.path)?.focus();\n return true;\n }, []);\n\n // Everything below is memoised over what it reads (0.52, code review of feat/0.52.0). The\n // picture and each tile are memoised components (`TreemapPlot`, `PlotTile`) that draw\n // again when a prop is new: built afresh per render, as these were, a page re-rendering\n // the card or one arrow-key step redrew every tile and ran `labels.tile` and two `Intl`\n // formats per node again — the cost DSP-22 had just taken out of the tiles.\n //\n // The share is of what is DRAWN, so it always reads as \"of this picture\" — a group's\n // and a tile's inside it alike.\n const total = useMemo(() => nodes.reduce((acc, n) => acc + n.size, 0), [nodes]);\n const format = useCallback(\n (v: number): ReactNode =>\n valueFormatter ? valueFormatter(v, total > 0 ? v / total : undefined) : formatWithMinus(numberFormat, v),\n [valueFormatter, total, numberFormat],\n );\n const names = useMemo(() => {\n // The value as the tooltip says it: the caller's formatter, handed the node's share as\n // it is there, when it gives text; a value it formats as markup is said as a number.\n //\n // It was asked with the share `undefined` — which, with anything drawn, it had never\n // been before 0.52 — so a formatter leaning on the share printed \"NaN%\" into every name\n // or threw and took the chart down (0.52 code review of treemap.tsx, finding 2). A guard\n // caught both; every named node has its share, so it is handed over instead (deep\n // review, charts, finding 13).\n const valueText = (v: number) => {\n const out = format(v);\n return typeof out === \"string\" || typeof out === \"number\" ? String(out) : formatWithMinus(numberFormat, v);\n };\n // A group's name by its path; a node's groups by theirs, its own path's prefixes.\n const groupName = new Map(order.filter((n) => n.children).map((n) => [n.path, n.name]));\n // `\"\"` when the formatter's text says the share already, which every template drops\n // (0.53, `shareInValue`).\n const shareOf = (v: number) => (shareInValue ? \"\" : shareText(total > 0 ? v / total : 0));\n return new Map(\n order.map((n) => {\n const say = n.children ? labels.group : labels.tile;\n const own = say(n.name, valueText(n.size), shareOf(n.size));\n // The innermost group first: \"Rent: …, in Housing, in Fixed costs\".\n const named = n.groups\n .map((g, i) => groupName.get(pathOf(n.groups.slice(0, i), g)) ?? g)\n .reduceRight((acc, group) => labels.inGroup(acc, group), own);\n return [n.path, named] as const;\n }),\n );\n }, [order, labels, format, numberFormat, shareText, shareInValue, total]);\n const keys = useMemo<TileKeys>(\n () => ({ name: (path) => names.get(path), onFocus, register, move }),\n [names, onFocus, register, move],\n );\n const map = useMemo<TileContext>(\n () => ({ tokens: tokenHex, edgeFor, edges: edgesInForce, keys, hover: anchor.set }),\n [tokenHex, edgeFor, edgesInForce, keys, anchor],\n );\n const options = useMemo<TileOptions>(\n () => ({ onNodeClick, onGroupClick, redactNames, nodeNote, dir }),\n [onNodeClick, onGroupClick, redactNames, nodeNote, dir],\n );\n const bubble = useMemo(\n () =>\n redactNames ? (\n <RedactedTooltipContent hideLabel valueFormatter={format} groups={redactNames === true} />\n ) : (\n <ChartTooltipContent hideLabel valueFormatter={format} />\n ),\n [redactNames, format],\n );\n\n if (nodes.length === 0) return null;\n const box = chartHeightProps(height);\n return (\n <ChartContainer\n role=\"figure\"\n {...rest}\n // Read through `aria-describedby`, after a caller's own ids; `hidden`, inside the\n // figure (0.52 code review, deep, charts, finding 7).\n description={description}\n ref={takeRoot}\n config={TREEMAP_CONFIG}\n className={cn(box.className, rest.className)}\n style={{ ...style, ...box.style }}\n >\n <TreemapPlot\n width={size?.width}\n height={size?.height}\n nodes={nodes}\n order={order}\n options={options}\n map={map}\n bubble={bubble}\n anchor={anchor}\n />\n </ChartContainer>\n );\n}\n"],"mappings":"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","names":["TreemapPlot"]}
@@ -4,6 +4,19 @@ export interface CurrencyOption {
4
4
  symbol: string;
5
5
  /** ISO 3166-1 alpha-2 country code (lowercase) used by flag-icons. */
6
6
  country: string;
7
+ /**
8
+ * The day ("YYYY-MM-DD") the currency was replaced as its country's money — set only on
9
+ * one that was. The pickers leave it out of their default list. A field that already
10
+ * holds it still shows it (the trigger's flag and code, and its row in the list,
11
+ * checked), and an explicit `options` list that names it still offers it: that list is
12
+ * the caller's.
13
+ *
14
+ * Marked rather than deleted, because an app may hold amounts in it from before. Gone
15
+ * from the array, `getCurrency` would answer `undefined` for a stored value, the field
16
+ * would show its placeholder over a real amount, and an app's flag subset
17
+ * (`CURRENCY_COUNTRIES`) would ship that value's flag as an empty box.
18
+ */
19
+ retired?: string;
7
20
  }
8
21
  export declare const CURRENCIES: CurrencyOption[];
9
22
  /**
@@ -11,8 +24,9 @@ export declare const CURRENCIES: CurrencyOption[];
11
24
  * code flag-icons names it by (`fi-ch`, `flags/4x3/ch.svg`). `CHF` → `"ch"`, `EUR` →
12
25
  * `"eu"` (flag-icons has the EU's flag under that code).
13
26
  *
14
- * Derived from {@link CURRENCIES}, so a currency the pickers gain is a flag here too. An
15
- * app that ships only the flags it renders builds its subset from these values:
27
+ * Derived from {@link CURRENCIES}, so a currency the pickers gain is a flag here too — and
28
+ * a `retired` one keeps its flag, for the values an app still holds in it. An app that
29
+ * ships only the flags it renders builds its subset from these values:
16
30
  *
17
31
  * ```ts
18
32
  * // vite.config.ts — plain Node, no DOM
@@ -15,7 +15,8 @@ const CURRENCIES = [
15
15
  { code: "CZK", name: "Czech Koruna", symbol: "K\u010D", country: "cz" },
16
16
  { code: "HUF", name: "Hungarian Forint", symbol: "Ft", country: "hu" },
17
17
  { code: "RON", name: "Romanian Leu", symbol: "lei", country: "ro" },
18
- { code: "BGN", name: "Bulgarian Lev", symbol: "\u043B\u0432", country: "bg" },
18
+ // Bulgaria adopted the euro on 2026-01-01 (keksdose audit follow-up, 2026-11-01).
19
+ { code: "BGN", name: "Bulgarian Lev", symbol: "\u043B\u0432", country: "bg", retired: "2026-01-01" },
19
20
  { code: "TRY", name: "Turkish Lira", symbol: "\u20BA", country: "tr" },
20
21
  { code: "RUB", name: "Russian Ruble", symbol: "\u20BD", country: "ru" },
21
22
  { code: "CNY", name: "Chinese Yuan", symbol: "\xA5", country: "cn" },
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/currencies.ts"],"sourcesContent":["// `@eifi1/ui-kit/currencies` — the currencies the kit's pickers offer, as plain data\n// (0.35, keksdose's 0.34 report).\n//\n// This module imports NOTHING: no React, no stylesheet, no other kit module. That is the\n// entry's whole promise, because an app's BUILD reads it in Node — keksdose's Vite plugin\n// builds its flag-icons subset from these country codes while Vite loads its config. Until\n// 0.35 that plugin parsed the `country` literals out of dist/components/currency-select.js;\n// 0.34 moved the list into the picker's option list and 91 of its test files failed at\n// load. A file inside dist/ is not something the kit promises; an entry is.\n// packaging-contract.test.ts pins the purity, scripts/verify-package.mjs the load in Node.\n//\n// CurrencySelect, AmountInput's currency chip and NumberInput's code detection read this\n// same array (components/currency-option-list re-exports it, and currency-select as it\n// always has), so the list the pickers show and the flags an app ships cannot drift.\n\nexport interface CurrencyOption {\n code: string;\n name: string;\n symbol: string;\n /** ISO 3166-1 alpha-2 country code (lowercase) used by flag-icons. */\n country: string;\n}\n\nexport const CURRENCIES: CurrencyOption[] = [\n { code: \"EUR\", name: \"Euro\", symbol: \"€\", country: \"eu\" },\n { code: \"USD\", name: \"US Dollar\", symbol: \"$\", country: \"us\" },\n { code: \"GBP\", name: \"British Pound\", symbol: \"£\", country: \"gb\" },\n { code: \"CHF\", name: \"Swiss Franc\", symbol: \"CHF\", country: \"ch\" },\n { code: \"JPY\", name: \"Japanese Yen\", symbol: \"¥\", country: \"jp\" },\n { code: \"CAD\", name: \"Canadian Dollar\", symbol: \"CA$\", country: \"ca\" },\n { code: \"AUD\", name: \"Australian Dollar\", symbol: \"A$\", country: \"au\" },\n { code: \"NZD\", name: \"New Zealand Dollar\", symbol: \"NZ$\", country: \"nz\" },\n { code: \"SEK\", name: \"Swedish Krona\", symbol: \"kr\", country: \"se\" },\n { code: \"NOK\", name: \"Norwegian Krone\", symbol: \"kr\", country: \"no\" },\n { code: \"DKK\", name: \"Danish Krone\", symbol: \"kr\", country: \"dk\" },\n { code: \"PLN\", name: \"Polish Zloty\", symbol: \"zł\", country: \"pl\" },\n { code: \"CZK\", name: \"Czech Koruna\", symbol: \"Kč\", country: \"cz\" },\n { code: \"HUF\", name: \"Hungarian Forint\", symbol: \"Ft\", country: \"hu\" },\n { code: \"RON\", name: \"Romanian Leu\", symbol: \"lei\", country: \"ro\" },\n { code: \"BGN\", name: \"Bulgarian Lev\", symbol: \"лв\", country: \"bg\" },\n { code: \"TRY\", name: \"Turkish Lira\", symbol: \"₺\", country: \"tr\" },\n { code: \"RUB\", name: \"Russian Ruble\", symbol: \"₽\", country: \"ru\" },\n { code: \"CNY\", name: \"Chinese Yuan\", symbol: \"¥\", country: \"cn\" },\n { code: \"HKD\", name: \"Hong Kong Dollar\", symbol: \"HK$\", country: \"hk\" },\n { code: \"SGD\", name: \"Singapore Dollar\", symbol: \"S$\", country: \"sg\" },\n { code: \"INR\", name: \"Indian Rupee\", symbol: \"₹\", country: \"in\" },\n { code: \"BRL\", name: \"Brazilian Real\", symbol: \"R$\", country: \"br\" },\n { code: \"MXN\", name: \"Mexican Peso\", symbol: \"MX$\", country: \"mx\" },\n { code: \"ZAR\", name: \"South African Rand\", symbol: \"R\", country: \"za\" },\n { code: \"ILS\", name: \"Israeli Shekel\", symbol: \"₪\", country: \"il\" },\n { code: \"AED\", name: \"UAE Dirham\", symbol: \"د.إ\", country: \"ae\" },\n];\n\n/**\n * Each currency code → the flag the kit paints for it: the lowercase ISO 3166-1 alpha-2\n * code flag-icons names it by (`fi-ch`, `flags/4x3/ch.svg`). `CHF` → `\"ch\"`, `EUR` →\n * `\"eu\"` (flag-icons has the EU's flag under that code).\n *\n * Derived from {@link CURRENCIES}, so a currency the pickers gain is a flag here too. An\n * app that ships only the flags it renders builds its subset from these values:\n *\n * ```ts\n * // vite.config.ts — plain Node, no DOM\n * import { CURRENCY_COUNTRIES } from \"@eifi1/ui-kit/currencies\";\n * const flags = new Set(Object.values(CURRENCY_COUNTRIES));\n * ```\n */\nexport const CURRENCY_COUNTRIES: Record<string, string> = Object.fromEntries(\n CURRENCIES.map((c) => [c.code, c.country]),\n);\n"],"mappings":";AAuBO,MAAM,aAA+B;AAAA,EAC1C,EAAE,MAAM,OAAO,MAAM,QAAQ,QAAQ,UAAK,SAAS,KAAK;AAAA,EACxD,EAAE,MAAM,OAAO,MAAM,aAAa,QAAQ,KAAK,SAAS,KAAK;AAAA,EAC7D,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,QAAK,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,eAAe,QAAQ,OAAO,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,QAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,mBAAmB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,qBAAqB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,sBAAsB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACxE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,MAAM,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,mBAAmB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACpE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,WAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,WAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,OAAO,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,gBAAM,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,UAAK,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,QAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,kBAAkB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACnE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,OAAO,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,sBAAsB,QAAQ,KAAK,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,kBAAkB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,cAAc,QAAQ,iBAAO,SAAS,KAAK;AAClE;AAgBO,MAAM,qBAA6C,OAAO;AAAA,EAC/D,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC;AAC3C;","names":[]}
1
+ {"version":3,"sources":["../src/currencies.ts"],"sourcesContent":["// `@eifi1/ui-kit/currencies` — the currencies the kit's pickers offer, as plain data\n// (0.35, keksdose's 0.34 report).\n//\n// This module imports NOTHING: no React, no stylesheet, no other kit module. That is the\n// entry's whole promise, because an app's BUILD reads it in Node — keksdose's Vite plugin\n// builds its flag-icons subset from these country codes while Vite loads its config. Until\n// 0.35 that plugin parsed the `country` literals out of dist/components/currency-select.js;\n// 0.34 moved the list into the picker's option list and 91 of its test files failed at\n// load. A file inside dist/ is not something the kit promises; an entry is.\n// packaging-contract.test.ts pins the purity, scripts/verify-package.mjs the load in Node.\n//\n// CurrencySelect, AmountInput's currency chip and NumberInput's code detection read this\n// same array (components/currency-option-list re-exports it, and currency-select as it\n// always has), so the list the pickers show and the flags an app ships cannot drift.\n//\n// A currency that was REPLACED stays in the array, marked `retired`: the pickers stop\n// offering it, and whatever reads a value an app already holds — the trigger's flag and\n// code, `getCurrency`, the flag subset, NumberInput's code detection — keeps knowing it\n// (keksdose audit follow-up, 2026-11-01: the lev, after Bulgaria's euro).\n\nexport interface CurrencyOption {\n code: string;\n name: string;\n symbol: string;\n /** ISO 3166-1 alpha-2 country code (lowercase) used by flag-icons. */\n country: string;\n /**\n * The day (\"YYYY-MM-DD\") the currency was replaced as its country's money — set only on\n * one that was. The pickers leave it out of their default list. A field that already\n * holds it still shows it (the trigger's flag and code, and its row in the list,\n * checked), and an explicit `options` list that names it still offers it: that list is\n * the caller's.\n *\n * Marked rather than deleted, because an app may hold amounts in it from before. Gone\n * from the array, `getCurrency` would answer `undefined` for a stored value, the field\n * would show its placeholder over a real amount, and an app's flag subset\n * (`CURRENCY_COUNTRIES`) would ship that value's flag as an empty box.\n */\n retired?: string;\n}\n\nexport const CURRENCIES: CurrencyOption[] = [\n { code: \"EUR\", name: \"Euro\", symbol: \"€\", country: \"eu\" },\n { code: \"USD\", name: \"US Dollar\", symbol: \"$\", country: \"us\" },\n { code: \"GBP\", name: \"British Pound\", symbol: \"£\", country: \"gb\" },\n { code: \"CHF\", name: \"Swiss Franc\", symbol: \"CHF\", country: \"ch\" },\n { code: \"JPY\", name: \"Japanese Yen\", symbol: \"¥\", country: \"jp\" },\n { code: \"CAD\", name: \"Canadian Dollar\", symbol: \"CA$\", country: \"ca\" },\n { code: \"AUD\", name: \"Australian Dollar\", symbol: \"A$\", country: \"au\" },\n { code: \"NZD\", name: \"New Zealand Dollar\", symbol: \"NZ$\", country: \"nz\" },\n { code: \"SEK\", name: \"Swedish Krona\", symbol: \"kr\", country: \"se\" },\n { code: \"NOK\", name: \"Norwegian Krone\", symbol: \"kr\", country: \"no\" },\n { code: \"DKK\", name: \"Danish Krone\", symbol: \"kr\", country: \"dk\" },\n { code: \"PLN\", name: \"Polish Zloty\", symbol: \"zł\", country: \"pl\" },\n { code: \"CZK\", name: \"Czech Koruna\", symbol: \"Kč\", country: \"cz\" },\n { code: \"HUF\", name: \"Hungarian Forint\", symbol: \"Ft\", country: \"hu\" },\n { code: \"RON\", name: \"Romanian Leu\", symbol: \"lei\", country: \"ro\" },\n // Bulgaria adopted the euro on 2026-01-01 (keksdose audit follow-up, 2026-11-01).\n { code: \"BGN\", name: \"Bulgarian Lev\", symbol: \"лв\", country: \"bg\", retired: \"2026-01-01\" },\n { code: \"TRY\", name: \"Turkish Lira\", symbol: \"₺\", country: \"tr\" },\n { code: \"RUB\", name: \"Russian Ruble\", symbol: \"₽\", country: \"ru\" },\n { code: \"CNY\", name: \"Chinese Yuan\", symbol: \"¥\", country: \"cn\" },\n { code: \"HKD\", name: \"Hong Kong Dollar\", symbol: \"HK$\", country: \"hk\" },\n { code: \"SGD\", name: \"Singapore Dollar\", symbol: \"S$\", country: \"sg\" },\n { code: \"INR\", name: \"Indian Rupee\", symbol: \"₹\", country: \"in\" },\n { code: \"BRL\", name: \"Brazilian Real\", symbol: \"R$\", country: \"br\" },\n { code: \"MXN\", name: \"Mexican Peso\", symbol: \"MX$\", country: \"mx\" },\n { code: \"ZAR\", name: \"South African Rand\", symbol: \"R\", country: \"za\" },\n { code: \"ILS\", name: \"Israeli Shekel\", symbol: \"₪\", country: \"il\" },\n { code: \"AED\", name: \"UAE Dirham\", symbol: \"د.إ\", country: \"ae\" },\n];\n\n/**\n * Each currency code → the flag the kit paints for it: the lowercase ISO 3166-1 alpha-2\n * code flag-icons names it by (`fi-ch`, `flags/4x3/ch.svg`). `CHF` → `\"ch\"`, `EUR` →\n * `\"eu\"` (flag-icons has the EU's flag under that code).\n *\n * Derived from {@link CURRENCIES}, so a currency the pickers gain is a flag here too — and\n * a `retired` one keeps its flag, for the values an app still holds in it. An app that\n * ships only the flags it renders builds its subset from these values:\n *\n * ```ts\n * // vite.config.ts — plain Node, no DOM\n * import { CURRENCY_COUNTRIES } from \"@eifi1/ui-kit/currencies\";\n * const flags = new Set(Object.values(CURRENCY_COUNTRIES));\n * ```\n */\nexport const CURRENCY_COUNTRIES: Record<string, string> = Object.fromEntries(\n CURRENCIES.map((c) => [c.code, c.country]),\n);\n"],"mappings":";AAyCO,MAAM,aAA+B;AAAA,EAC1C,EAAE,MAAM,OAAO,MAAM,QAAQ,QAAQ,UAAK,SAAS,KAAK;AAAA,EACxD,EAAE,MAAM,OAAO,MAAM,aAAa,QAAQ,KAAK,SAAS,KAAK;AAAA,EAC7D,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,QAAK,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,eAAe,QAAQ,OAAO,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,QAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,mBAAmB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,qBAAqB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,sBAAsB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACxE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,MAAM,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,mBAAmB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACpE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,WAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,WAAM,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,OAAO,SAAS,KAAK;AAAA;AAAA,EAElE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,gBAAM,SAAS,MAAM,SAAS,aAAa;AAAA,EACzF,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,iBAAiB,QAAQ,UAAK,SAAS,KAAK;AAAA,EACjE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,QAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,OAAO,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,oBAAoB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACrE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAChE,EAAE,MAAM,OAAO,MAAM,kBAAkB,QAAQ,MAAM,SAAS,KAAK;AAAA,EACnE,EAAE,MAAM,OAAO,MAAM,gBAAgB,QAAQ,OAAO,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,sBAAsB,QAAQ,KAAK,SAAS,KAAK;AAAA,EACtE,EAAE,MAAM,OAAO,MAAM,kBAAkB,QAAQ,UAAK,SAAS,KAAK;AAAA,EAClE,EAAE,MAAM,OAAO,MAAM,cAAc,QAAQ,iBAAO,SAAS,KAAK;AAClE;AAiBO,MAAM,qBAA6C,OAAO;AAAA,EAC/D,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC;AAC3C;","names":[]}
@@ -47,7 +47,7 @@ const roleUtilities = {
47
47
  };
48
48
  }
49
49
  };
50
- const VERSION = true ? "0.53.0" : "0.0.0-source";
50
+ const VERSION = true ? "0.55.0" : "0.0.0-source";
51
51
  const recommended = {
52
52
  name: "@eifi1/ui-kit/recommended",
53
53
  // Not the tests, as check:tokens skips the kit's: a test names the old class to assert it
@@ -103,7 +103,7 @@ export interface RoleClassHit {
103
103
  /**
104
104
  * Every `[var(--token)]` class in `text` (or one of v4's spellings of it, above) whose
105
105
  * token has a role utility on that property.
106
- * A token with no utility there (`text-[var(--chart-1)]`, `--status-*`,
107
- * `bg-[var(--border)]` as a divider's fill) is not a hit: `[var(--…)]` is right for it.
106
+ * A token with no utility there (`text-[var(--chart-1)]`, `bg-[var(--border)]` as a
107
+ * divider's fill) is not a hit: `[var(--…)]` is right for it.
108
108
  */
109
109
  export declare function findRoleClasses(text: string, table: RoleUtilityTable): RoleClassHit[];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/eslint/role-utilities.ts"],"sourcesContent":["// Which `[var(--token)]` classes have a role utility, and what it is called — the one\n// definition `@eifi1/ui-kit/eslint`'s `role-utilities` rule and `npm run check:tokens`\n// (scripts/check-token-discipline.mjs) both read (round 0.35 R5, Kurvenschmiede's Q6).\n//\n// The table itself is role-utilities.generated.ts, re-rendered from tokens.css's\n// `@theme inline` block by scripts/gen-role-utilities.mjs with the parser below; the\n// check fails while it is stale. So tokens.css stays the source: a role that gains a\n// utility there is flagged in the kit and in the apps from the next release on. The\n// README's role-utility table (scripts/gen-styling-inventory.mjs) reads the block with\n// the same parser since 0.37, so the README, the check and the plugin cannot disagree.\n//\n// This module imports NOTHING, and its TypeScript is erasable only (no enum, no\n// namespace, no parameter property): the check imports it from source under Node's type\n// stripping, before any build, and the plugin ships it to an app's ESLint, which is\n// plain Node with no React and no stylesheet.\n\n/**\n * The properties a role utility exists for, each with the Tailwind theme namespace it is\n * declared in. `ring-width` and `outline-width` are a width typed `length:` —\n * `ring-[length:var(--focus-ring-width)]` is `ring-focus`. `divide-*` and the side borders\n * (`border-t-*`, `border-x-*` …) read `border`'s, as Tailwind does.\n */\nexport const ROLE_NAMESPACES = {\n text: \"text-color\",\n bg: \"background-color\",\n border: \"border-color\",\n ring: \"ring-color\",\n \"ring-offset\": \"ring-offset-color\",\n outline: \"outline-color\",\n fill: \"fill\",\n stroke: \"stroke\",\n \"ring-width\": \"ring-width\",\n \"outline-width\": \"outline-width\",\n} as const;\n\nexport type RoleProperty = keyof typeof ROLE_NAMESPACES;\n\n/**\n * Per property, each token that has a role utility → the utility's name after the\n * property's prefix: `border: { \"--border\": [\"subtle\"] }` is `border-subtle`. A list,\n * because nothing stops two names from reading one token; one name is a fix, several a\n * choice.\n */\nexport type RoleUtilityTable = {\n readonly [P in RoleProperty]: Readonly<Record<string, readonly string[]>>;\n};\n\n/** One `--<namespace>-<name>: <value>;` of the `@theme inline` block. */\nexport interface ThemeInlineDeclaration {\n /** The theme variable without its dashes: `text-color-muted`, `ring-width-focus`. */\n variable: string;\n /** The property whose namespace it is in; `null` for a namespace ROLE_NAMESPACES lacks. */\n property: RoleProperty | null;\n /** The name after the namespace, `muted`; `null` with `property`. */\n name: string | null;\n /** The utility it makes: `text-muted`, `ring-focus` (a width is `ring-*` like a colour). */\n utility: string | null;\n /** The token the value reads, `--text-muted`; `null` unless the value is a bare `var()`. */\n token: string | null;\n /** The value as written, trimmed. */\n value: string;\n}\n\n/** The `@theme inline` block: where it stands, and every declaration in it in block order. */\nexport interface ThemeInlineBlock {\n /** Its first and one-past-last offset in `stripCssComments(css)`, not in `css`. With\n * several `@theme inline` blocks, the first one's; `ranges` holds each. */\n start: number;\n end: number;\n ranges: { start: number; end: number }[];\n /** Every block's, in stylesheet order. */\n declarations: ThemeInlineDeclaration[];\n}\n\n/** `css` without its comments: a declaration commented out is not one. */\nexport function stripCssComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n}\n\n/** One `@theme … { … }` block: its first and one-past-last offset, and what its braces hold. */\nexport interface ThemeBlock {\n start: number;\n end: number;\n body: string;\n}\n\n/**\n * Every `@theme` block of `css` (comments already stripped) whose prelude names `inline`\n * (`@theme inline`, `@theme inline reference`) — or, with `inline: false`, every other\n * one (`@theme {`, Tailwind's `@theme default {`). Counted brace by brace, so a nested\n * block does not end it early.\n *\n * The one block finder (refactor audit 2026-10-30, DUP-13): this parser read only the\n * first `@theme inline {…}` up to its first `}`, the testing guard's\n * `declaredCustomProperties` every inline block brace-aware, and the README's inventory a\n * plain `@theme {` by a regex of its own — three readers that disagreed on what one block\n * is. It lives here because this module imports nothing, so the scripts load it from\n * source under Node.\n */\nexport function themeBlocks(css: string, { inline }: { inline: boolean }): ThemeBlock[] {\n const blocks: ThemeBlock[] = [];\n for (const m of css.matchAll(/@theme\\b([^{;]*)\\{/g)) {\n if (/\\binline\\b/.test(m[1]) !== inline) continue;\n const open = m.index + m[0].length - 1;\n let depth = 0;\n let close = open;\n for (; close < css.length; close++) {\n if (css[close] === \"{\") depth++;\n else if (css[close] === \"}\" && --depth === 0) break;\n }\n blocks.push({ start: m.index, end: close + 1, body: css.slice(open + 1, close) });\n }\n return blocks;\n}\n\nconst PROPERTIES = Object.keys(ROLE_NAMESPACES) as RoleProperty[];\n\n/**\n * tokens.css's `@theme inline` block (every one, should there be several), read once for\n * every reader: the table below, and the README's (scripts/gen-styling-inventory.mjs),\n * which fails on a `property: null` where the table leaves it out. `null` when there is\n * no such block — the role utilities are gone, or this parser is. No namespace is\n * another's prefix, so the first that fits is the one.\n */\nexport function parseThemeInline(css: string): ThemeInlineBlock | null {\n const blocks = themeBlocks(stripCssComments(css), { inline: true });\n if (blocks.length === 0) return null;\n const declarations = [\n ...blocks\n .map((b) => b.body)\n .join(\"\\n\")\n .matchAll(/--([a-z0-9-]+)\\s*:\\s*([^;]+);/g),\n ].map(([, variable, raw]) => {\n const value = raw.trim();\n const property = PROPERTIES.find((p) => variable.startsWith(`${ROLE_NAMESPACES[p]}-`)) ?? null;\n const name = property && variable.slice(ROLE_NAMESPACES[property].length + 1);\n return {\n variable,\n property,\n name,\n utility: property && `${property.replace(/-width$/, \"\")}-${name}`,\n token: /^var\\((--[a-z0-9-]+)\\)$/.exec(value)?.[1] ?? null,\n value,\n };\n });\n const ranges = blocks.map(({ start, end }) => ({ start, end }));\n return { start: ranges[0].start, end: ranges[0].end, ranges, declarations };\n}\n\n/**\n * The table `tokens.css` declares: every `--<namespace>-<name>: var(--<token>)` in its\n * `@theme inline` block, in block order, each property present (in ROLE_NAMESPACES' order)\n * even when it has none. `null` when there is no such block.\n */\nexport function roleUtilitiesFromCss(css: string): RoleUtilityTable | null {\n const block = parseThemeInline(css);\n if (!block) return null;\n const table = Object.fromEntries(PROPERTIES.map((p) => [p, {}])) as Record<RoleProperty, Record<string, string[]>>;\n for (const { property, name, token } of block.declarations) {\n if (property && name && token) (table[property][token] ??= []).push(name);\n }\n return table;\n}\n\n/**\n * `text-[var(--text-muted)]`, `hover:bg-[var(--bg-hover)]`, `border-t-[var(--border)]`,\n * `focus-visible:ring-[var(--brand)]/40`, `ring-offset-[var(--bg-surface)]`,\n * `ring-[length:var(--focus-ring-width)]` — and, since 0.52, Tailwind v4's own spellings\n * of the same: the shorthand `text-(--text-muted)`, a type hint `text-[color:var(--x)]` /\n * `text-(color:--x)` / `ring-(length:--x)`, and the arbitrary property\n * `[color:var(--text-muted)]`, which is `text-muted` (refactor audit 2026-10-30, DOC-1:\n * all of them passed the check and every app's lint). Preceded by a start, a space, a\n * quote or a variant's colon, which the match does not include — nor the variants before\n * it or an opacity or `!` after it — so replacing the match keeps all of them.\n *\n * Groups: 1 the property; then 2 the hint and 3 the token of `-[…var()]`, or 4 and 5 of\n * `-(…)`; or 6 the CSS property and 7 the token of `[property:var()]`.\n */\nconst TOKEN = \"(--[a-zA-Z0-9-]+)\";\nconst ROLE_CLASS = new RegExp(\n \"(?<=^|[\\\\s\\\"'`:])(?:\" +\n \"(text|bg|border(?:-[xytrblse])?|divide|ring-offset|ring|outline|fill|stroke)-\" +\n `(?:\\\\[(?:(length|color):)?var\\\\(${TOKEN}\\\\)\\\\]|\\\\((?:(length|color):)?${TOKEN}\\\\))` +\n `|\\\\[(color|background-color|border-color|outline-color|fill|stroke):var\\\\(${TOKEN}\\\\)\\\\]` +\n \")\",\n \"g\",\n);\n\n/** The utility prefix an arbitrary property `[property:var(--x)]` is written as. */\nconst ARBITRARY_PROPERTY: Record<string, string> = {\n color: \"text\",\n \"background-color\": \"bg\",\n \"border-color\": \"border\",\n \"outline-color\": \"outline\",\n fill: \"fill\",\n stroke: \"stroke\",\n};\n\nexport interface RoleClassHit {\n /** Where the class starts in the scanned text, after its variants. */\n index: number;\n /** The class as written, without variants or opacity: `border-t-[var(--border)]`. */\n found: string;\n /** The role utilities that paint the same: `[\"border-t-subtle\"]`. */\n utilities: string[];\n}\n\n/**\n * Every `[var(--token)]` class in `text` (or one of v4's spellings of it, above) whose\n * token has a role utility on that property.\n * A token with no utility there (`text-[var(--chart-1)]`, `--status-*`,\n * `bg-[var(--border)]` as a divider's fill) is not a hit: `[var(--…)]` is right for it.\n */\nexport function findRoleClasses(text: string, table: RoleUtilityTable): RoleClassHit[] {\n // Every form names a token: `var(--x)`, or `(--x)` in v4's shorthand.\n if (!text.includes(\"--\")) return [];\n const hits: RoleClassHit[] = [];\n for (const m of text.matchAll(ROLE_CLASS)) {\n const [found, utility, bracketHint, bracketToken, parenHint, parenToken, cssProperty, propertyToken] = m;\n const prop = utility ?? ARBITRARY_PROPERTY[cssProperty];\n const token = bracketToken ?? parenToken ?? propertyToken;\n // `border-t` and `divide` read `border`'s names; a `length:` hint reads the widths.\n const key =\n (bracketHint ?? parenHint) === \"length\"\n ? `${prop}-width`\n : prop.startsWith(\"border-\") || prop === \"divide\"\n ? \"border\"\n : prop;\n const names = (table as Partial<Record<string, Readonly<Record<string, readonly string[]>>>>)[key]?.[token];\n if (names?.length) hits.push({ index: m.index, found, utilities: names.map((name) => `${prop}-${name}`) });\n }\n return hits;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/eslint/role-utilities.ts"],"sourcesContent":["// Which `[var(--token)]` classes have a role utility, and what it is called — the one\n// definition `@eifi1/ui-kit/eslint`'s `role-utilities` rule and `npm run check:tokens`\n// (scripts/check-token-discipline.mjs) both read (round 0.35 R5, Kurvenschmiede's Q6).\n//\n// The table itself is role-utilities.generated.ts, re-rendered from tokens.css's\n// `@theme inline` block by scripts/gen-role-utilities.mjs with the parser below; the\n// check fails while it is stale. So tokens.css stays the source: a role that gains a\n// utility there is flagged in the kit and in the apps from the next release on. The\n// README's role-utility table (scripts/gen-styling-inventory.mjs) reads the block with\n// the same parser since 0.37, so the README, the check and the plugin cannot disagree.\n//\n// This module imports NOTHING, and its TypeScript is erasable only (no enum, no\n// namespace, no parameter property): the check imports it from source under Node's type\n// stripping, before any build, and the plugin ships it to an app's ESLint, which is\n// plain Node with no React and no stylesheet.\n\n/**\n * The properties a role utility exists for, each with the Tailwind theme namespace it is\n * declared in. `ring-width` and `outline-width` are a width typed `length:` —\n * `ring-[length:var(--focus-ring-width)]` is `ring-focus`. `divide-*` and the side borders\n * (`border-t-*`, `border-x-*` …) read `border`'s, as Tailwind does.\n */\nexport const ROLE_NAMESPACES = {\n text: \"text-color\",\n bg: \"background-color\",\n border: \"border-color\",\n ring: \"ring-color\",\n \"ring-offset\": \"ring-offset-color\",\n outline: \"outline-color\",\n fill: \"fill\",\n stroke: \"stroke\",\n \"ring-width\": \"ring-width\",\n \"outline-width\": \"outline-width\",\n} as const;\n\nexport type RoleProperty = keyof typeof ROLE_NAMESPACES;\n\n/**\n * Per property, each token that has a role utility → the utility's name after the\n * property's prefix: `border: { \"--border\": [\"subtle\"] }` is `border-subtle`. A list,\n * because nothing stops two names from reading one token; one name is a fix, several a\n * choice.\n */\nexport type RoleUtilityTable = {\n readonly [P in RoleProperty]: Readonly<Record<string, readonly string[]>>;\n};\n\n/** One `--<namespace>-<name>: <value>;` of the `@theme inline` block. */\nexport interface ThemeInlineDeclaration {\n /** The theme variable without its dashes: `text-color-muted`, `ring-width-focus`. */\n variable: string;\n /** The property whose namespace it is in; `null` for a namespace ROLE_NAMESPACES lacks. */\n property: RoleProperty | null;\n /** The name after the namespace, `muted`; `null` with `property`. */\n name: string | null;\n /** The utility it makes: `text-muted`, `ring-focus` (a width is `ring-*` like a colour). */\n utility: string | null;\n /** The token the value reads, `--text-muted`; `null` unless the value is a bare `var()`. */\n token: string | null;\n /** The value as written, trimmed. */\n value: string;\n}\n\n/** The `@theme inline` block: where it stands, and every declaration in it in block order. */\nexport interface ThemeInlineBlock {\n /** Its first and one-past-last offset in `stripCssComments(css)`, not in `css`. With\n * several `@theme inline` blocks, the first one's; `ranges` holds each. */\n start: number;\n end: number;\n ranges: { start: number; end: number }[];\n /** Every block's, in stylesheet order. */\n declarations: ThemeInlineDeclaration[];\n}\n\n/** `css` without its comments: a declaration commented out is not one. */\nexport function stripCssComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n}\n\n/** One `@theme … { … }` block: its first and one-past-last offset, and what its braces hold. */\nexport interface ThemeBlock {\n start: number;\n end: number;\n body: string;\n}\n\n/**\n * Every `@theme` block of `css` (comments already stripped) whose prelude names `inline`\n * (`@theme inline`, `@theme inline reference`) — or, with `inline: false`, every other\n * one (`@theme {`, Tailwind's `@theme default {`). Counted brace by brace, so a nested\n * block does not end it early.\n *\n * The one block finder (refactor audit 2026-10-30, DUP-13): this parser read only the\n * first `@theme inline {…}` up to its first `}`, the testing guard's\n * `declaredCustomProperties` every inline block brace-aware, and the README's inventory a\n * plain `@theme {` by a regex of its own — three readers that disagreed on what one block\n * is. It lives here because this module imports nothing, so the scripts load it from\n * source under Node.\n */\nexport function themeBlocks(css: string, { inline }: { inline: boolean }): ThemeBlock[] {\n const blocks: ThemeBlock[] = [];\n for (const m of css.matchAll(/@theme\\b([^{;]*)\\{/g)) {\n if (/\\binline\\b/.test(m[1]) !== inline) continue;\n const open = m.index + m[0].length - 1;\n let depth = 0;\n let close = open;\n for (; close < css.length; close++) {\n if (css[close] === \"{\") depth++;\n else if (css[close] === \"}\" && --depth === 0) break;\n }\n blocks.push({ start: m.index, end: close + 1, body: css.slice(open + 1, close) });\n }\n return blocks;\n}\n\nconst PROPERTIES = Object.keys(ROLE_NAMESPACES) as RoleProperty[];\n\n/**\n * tokens.css's `@theme inline` block (every one, should there be several), read once for\n * every reader: the table below, and the README's (scripts/gen-styling-inventory.mjs),\n * which fails on a `property: null` where the table leaves it out. `null` when there is\n * no such block — the role utilities are gone, or this parser is. No namespace is\n * another's prefix, so the first that fits is the one.\n */\nexport function parseThemeInline(css: string): ThemeInlineBlock | null {\n const blocks = themeBlocks(stripCssComments(css), { inline: true });\n if (blocks.length === 0) return null;\n const declarations = [\n ...blocks\n .map((b) => b.body)\n .join(\"\\n\")\n .matchAll(/--([a-z0-9-]+)\\s*:\\s*([^;]+);/g),\n ].map(([, variable, raw]) => {\n const value = raw.trim();\n const property = PROPERTIES.find((p) => variable.startsWith(`${ROLE_NAMESPACES[p]}-`)) ?? null;\n const name = property && variable.slice(ROLE_NAMESPACES[property].length + 1);\n return {\n variable,\n property,\n name,\n utility: property && `${property.replace(/-width$/, \"\")}-${name}`,\n token: /^var\\((--[a-z0-9-]+)\\)$/.exec(value)?.[1] ?? null,\n value,\n };\n });\n const ranges = blocks.map(({ start, end }) => ({ start, end }));\n return { start: ranges[0].start, end: ranges[0].end, ranges, declarations };\n}\n\n/**\n * The table `tokens.css` declares: every `--<namespace>-<name>: var(--<token>)` in its\n * `@theme inline` block, in block order, each property present (in ROLE_NAMESPACES' order)\n * even when it has none. `null` when there is no such block.\n */\nexport function roleUtilitiesFromCss(css: string): RoleUtilityTable | null {\n const block = parseThemeInline(css);\n if (!block) return null;\n const table = Object.fromEntries(PROPERTIES.map((p) => [p, {}])) as Record<RoleProperty, Record<string, string[]>>;\n for (const { property, name, token } of block.declarations) {\n if (property && name && token) (table[property][token] ??= []).push(name);\n }\n return table;\n}\n\n/**\n * `text-[var(--text-muted)]`, `hover:bg-[var(--bg-hover)]`, `border-t-[var(--border)]`,\n * `focus-visible:ring-[var(--brand)]/40`, `ring-offset-[var(--bg-surface)]`,\n * `ring-[length:var(--focus-ring-width)]` — and, since 0.52, Tailwind v4's own spellings\n * of the same: the shorthand `text-(--text-muted)`, a type hint `text-[color:var(--x)]` /\n * `text-(color:--x)` / `ring-(length:--x)`, and the arbitrary property\n * `[color:var(--text-muted)]`, which is `text-muted` (refactor audit 2026-10-30, DOC-1:\n * all of them passed the check and every app's lint). Preceded by a start, a space, a\n * quote or a variant's colon, which the match does not include — nor the variants before\n * it or an opacity or `!` after it — so replacing the match keeps all of them.\n *\n * Groups: 1 the property; then 2 the hint and 3 the token of `-[…var()]`, or 4 and 5 of\n * `-(…)`; or 6 the CSS property and 7 the token of `[property:var()]`.\n */\nconst TOKEN = \"(--[a-zA-Z0-9-]+)\";\nconst ROLE_CLASS = new RegExp(\n \"(?<=^|[\\\\s\\\"'`:])(?:\" +\n \"(text|bg|border(?:-[xytrblse])?|divide|ring-offset|ring|outline|fill|stroke)-\" +\n `(?:\\\\[(?:(length|color):)?var\\\\(${TOKEN}\\\\)\\\\]|\\\\((?:(length|color):)?${TOKEN}\\\\))` +\n `|\\\\[(color|background-color|border-color|outline-color|fill|stroke):var\\\\(${TOKEN}\\\\)\\\\]` +\n \")\",\n \"g\",\n);\n\n/** The utility prefix an arbitrary property `[property:var(--x)]` is written as. */\nconst ARBITRARY_PROPERTY: Record<string, string> = {\n color: \"text\",\n \"background-color\": \"bg\",\n \"border-color\": \"border\",\n \"outline-color\": \"outline\",\n fill: \"fill\",\n stroke: \"stroke\",\n};\n\nexport interface RoleClassHit {\n /** Where the class starts in the scanned text, after its variants. */\n index: number;\n /** The class as written, without variants or opacity: `border-t-[var(--border)]`. */\n found: string;\n /** The role utilities that paint the same: `[\"border-t-subtle\"]`. */\n utilities: string[];\n}\n\n/**\n * Every `[var(--token)]` class in `text` (or one of v4's spellings of it, above) whose\n * token has a role utility on that property.\n * A token with no utility there (`text-[var(--chart-1)]`, `bg-[var(--border)]` as a\n * divider's fill) is not a hit: `[var(--…)]` is right for it.\n */\nexport function findRoleClasses(text: string, table: RoleUtilityTable): RoleClassHit[] {\n // Every form names a token: `var(--x)`, or `(--x)` in v4's shorthand.\n if (!text.includes(\"--\")) return [];\n const hits: RoleClassHit[] = [];\n for (const m of text.matchAll(ROLE_CLASS)) {\n const [found, utility, bracketHint, bracketToken, parenHint, parenToken, cssProperty, propertyToken] = m;\n const prop = utility ?? ARBITRARY_PROPERTY[cssProperty];\n const token = bracketToken ?? parenToken ?? propertyToken;\n // `border-t` and `divide` read `border`'s names; a `length:` hint reads the widths.\n const key =\n (bracketHint ?? parenHint) === \"length\"\n ? `${prop}-width`\n : prop.startsWith(\"border-\") || prop === \"divide\"\n ? \"border\"\n : prop;\n const names = (table as Partial<Record<string, Readonly<Record<string, readonly string[]>>>>)[key]?.[token];\n if (names?.length) hits.push({ index: m.index, found, utilities: names.map((name) => `${prop}-${name}`) });\n }\n return hits;\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  const DEFAULT_FEEDBACK_CONTEXT_LABELS = {
3
3
  user: "User",
4
- attachUrl: "Attach current page URL"
4
+ attachUrl: "Attach current page URL",
5
+ diagnostics: "Diagnostics sent with the report"
5
6
  };
6
7
  export {
7
8
  DEFAULT_FEEDBACK_CONTEXT_LABELS
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/feedback/feedback-context-labels.ts"],"sourcesContent":["// The English words of ./feedback-context, in a module with no component code: the English\n// catalogue (src/i18n/defaults.ts) imports them without the component, so `en` loads as one\n// chunk like the other languages (0.34, KS U8). ./feedback-context re-exports them.\n\nimport type { FeedbackContextLabels } from \"./feedback-context\";\n\nexport const DEFAULT_FEEDBACK_CONTEXT_LABELS: FeedbackContextLabels = {\n user: \"User\",\n attachUrl: \"Attach current page URL\",\n};\n"],"mappings":";AAMO,MAAM,kCAAyD;AAAA,EACpE,MAAM;AAAA,EACN,WAAW;AACb;","names":[]}
1
+ {"version":3,"sources":["../../src/feedback/feedback-context-labels.ts"],"sourcesContent":["// The English words of ./feedback-context, in a module with no component code: the English\n// catalogue (src/i18n/defaults.ts) imports them without the component, so `en` loads as one\n// chunk like the other languages (0.34, KS U8). ./feedback-context re-exports them.\n\nimport type { FeedbackContextLabels } from \"./feedback-context\";\n\nexport const DEFAULT_FEEDBACK_CONTEXT_LABELS: FeedbackContextLabels = {\n user: \"User\",\n attachUrl: \"Attach current page URL\",\n diagnostics: \"Diagnostics sent with the report\",\n};\n"],"mappings":";AAMO,MAAM,kCAAyD;AAAA,EACpE,MAAM;AAAA,EACN,WAAW;AAAA,EACX,aAAa;AACf;","names":[]}