@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
@@ -0,0 +1,137 @@
1
+ "use client";
2
+ const COMMIT_LINK = /\s*\(\[[0-9a-f]{4,40}\]\([^)]*\)\)/gi;
3
+ const TRAILING_REFS = /(?:(?:,\s*\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|references?)(?:\s*\[[^\]]*#\d+\]\([^)]*\))+)+|(?:\s*\[[^\]]*#\d+\]\([^)]*\))+)\s*$/i;
4
+ const SCOPE = /^\*\*([^*:]+?)(?::\*\*|\*\*:)\s*/;
5
+ const INLINE_LINK = /\[([^\]]+)\]\([^)]*\)/g;
6
+ function parseChangelogItem(line) {
7
+ let text = line.replace(COMMIT_LINK, "").replace(TRAILING_REFS, "");
8
+ let scope = null;
9
+ const scoped = SCOPE.exec(text);
10
+ if (scoped) {
11
+ scope = plain(scoped[1]).trim();
12
+ text = text.slice(scoped[0].length);
13
+ }
14
+ text = plain(text).replace(/\s+([,.;:])(?=\s|$)/g, "$1").replace(/\s{2,}/g, " ").replace(/[,;:]+\s*$/, "").trim();
15
+ return { scope, text };
16
+ }
17
+ function plain(markdown) {
18
+ return markdown.replace(INLINE_LINK, "$1").replace(/\*\*(.+?)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1");
19
+ }
20
+ function groupByScope(items) {
21
+ const groups = /* @__PURE__ */ new Map();
22
+ const seen = /* @__PURE__ */ new Set();
23
+ for (const raw of items) {
24
+ const { scope, text } = typeof raw === "string" ? parseChangelogItem(raw) : raw;
25
+ if (!text) continue;
26
+ const repeat = `${scope ?? ""}\0${text.toLowerCase()}`;
27
+ if (seen.has(repeat)) continue;
28
+ seen.add(repeat);
29
+ const group = groups.get(scope ?? "") ?? { scope, texts: [] };
30
+ group.texts.push(text);
31
+ groups.set(scope ?? "", group);
32
+ }
33
+ return [...groups.values()];
34
+ }
35
+ const SECTION_NAMES = {
36
+ breaking: ["breaking changes", "breaking"],
37
+ added: ["added", "features", "feat"],
38
+ changed: ["changed", "performance improvements", "performance", "code refactoring"],
39
+ fixed: ["fixed", "bug fixes", "fixes"],
40
+ deprecated: ["deprecated"],
41
+ removed: ["removed"],
42
+ security: ["security"],
43
+ reverted: ["reverts", "reverted"]
44
+ };
45
+ const SECTION_KEYS = new Map(
46
+ Object.entries(SECTION_NAMES).flatMap(([key, names]) => names.map((name) => [name, key]))
47
+ );
48
+ function changelogSectionKey(title) {
49
+ const name = title.toLowerCase().replace(/^[^\p{L}]+/u, "").split(/\s+[—–-]\s+|\s*\(/)[0].trim();
50
+ return SECTION_KEYS.get(name) ?? null;
51
+ }
52
+ const VERSION = String.raw`\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]*)?`;
53
+ const RELEASE_HEADING = new RegExp(String.raw`^#{2,3}\s+(?:\[(${VERSION})\](?:\([^)]*\))?|(${VERSION}))(.*)$`);
54
+ const DATE = /(?:^|[^\d-])(\d{4}-\d{2}-\d{2})(?![\d-])/;
55
+ const HEADING = /^(#{1,6})\s+(.*?)\s*#*\s*$/;
56
+ const BULLET = /^[*-]\s+(.*)$/;
57
+ const NESTED_BULLET = /^\s+[*-]\s+/;
58
+ const FENCE = /^\s*(`{3,}|~{3,})/;
59
+ function parseChangelog(markdown) {
60
+ const releases = [];
61
+ let release = null;
62
+ let sections = [];
63
+ let section = null;
64
+ let body = [];
65
+ let open = null;
66
+ let fence = null;
67
+ const close = () => {
68
+ if (!release) return;
69
+ release.body = body.join("\n").trim() || null;
70
+ release.sections = sections.map((s) => ({
71
+ title: s.title,
72
+ key: changelogSectionKey(s.title),
73
+ items: s.bullets.map(parseChangelogItem).filter((item) => item.text !== "")
74
+ })).filter((s) => s.items.length > 0);
75
+ releases.push(release);
76
+ release = null;
77
+ };
78
+ for (const raw of markdown.split(/\r?\n/)) {
79
+ const line = raw.trimEnd();
80
+ if (fence !== null) {
81
+ if (line.trimStart().startsWith(fence)) fence = null;
82
+ continue;
83
+ }
84
+ const fenced = FENCE.exec(line);
85
+ if (fenced) {
86
+ fence = fenced[1];
87
+ open = null;
88
+ continue;
89
+ }
90
+ const heading = HEADING.exec(line);
91
+ if (heading) {
92
+ open = null;
93
+ const released = RELEASE_HEADING.exec(line);
94
+ if (released) {
95
+ close();
96
+ const [, bracketed, bare, rest] = released;
97
+ release = { version: bracketed ?? bare, date: DATE.exec(rest)?.[1] ?? null, sections: [], body: null };
98
+ sections = [];
99
+ section = null;
100
+ body = [];
101
+ } else if (heading[1].length <= 2) {
102
+ close();
103
+ } else if (heading[1].length === 3 && release) {
104
+ section = { title: heading[2], bullets: [] };
105
+ sections.push(section);
106
+ }
107
+ continue;
108
+ }
109
+ if (!release) continue;
110
+ if (line.trim() === "") {
111
+ open = null;
112
+ if (!section) body.push("");
113
+ continue;
114
+ }
115
+ if (!section) {
116
+ body.push(line);
117
+ continue;
118
+ }
119
+ const bullet = BULLET.exec(line);
120
+ if (bullet) {
121
+ open = section.bullets.push(bullet[1]) - 1;
122
+ } else if (NESTED_BULLET.test(line)) {
123
+ open = null;
124
+ } else if (open !== null) {
125
+ section.bullets[open] += ` ${line.trim()}`;
126
+ }
127
+ }
128
+ close();
129
+ return releases;
130
+ }
131
+ export {
132
+ changelogSectionKey,
133
+ groupByScope,
134
+ parseChangelog,
135
+ parseChangelogItem
136
+ };
137
+ //# sourceMappingURL=changelog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/changelog.ts"],"sourcesContent":["/**\n * Reading a generated changelog for a person (kastlan reuse audit 2026-11-02, proposal 1).\n *\n * All four repositories write CHANGELOG.md with `commit-and-tag-version`, and two apps\n * show it: keksdose on its changelog page and in the what's-new dialog (its backend\n * splits the file into releases, `changelog-format.ts` reads each bullet), kastlan on its\n * changelog page (`parse-changelog.ts`). kastlan's parser read the compare link's \"(\" as\n * the release date, so every release showed a GitHub URL, and left `**scope:**` and\n * `, closes [#12](…)` in the text. It fixed that with keksdose's rules (kastlan 153248c),\n * and those rules are here once, with what the four files still asked of them:\n *\n * ```\n * ## [0.13.9](https://github.com/…/compare/v0.13.8...v0.13.9) (2026-10-20)\n *\n * ### Added\n *\n * * **admin:** a company rename logs both names again ([8f698eb](https://…/commit/8f698eb…)), closes [#12](…)\n * ```\n *\n * reads as `{ version: \"0.13.9\", date: \"2026-10-20\", sections: [{ title: \"Added\", key:\n * \"added\", items: [{ scope: \"admin\", text: \"a company rename logs both names again\" }] }] }`.\n *\n * - **Every heading shape the tool writes**: `## [x.y.z](compare) (date)`, the `###` it\n * writes for a patch under the default template, and `## x.y.z (date)` without a link\n * (the first release, which has nothing to compare with). The compare link is skipped\n * before the date is read, and a date is `YYYY-MM-DD` or nothing. The hand-written\n * shapes before a repository adopted the tool read too: `## [0.2.0] - 2026-04-10`,\n * `## [0.6.1] — 2026-09-24`.\n * - **One key per kind of section**, whichever names the repository's `.versionrc`\n * gives it: \"Added\", \"Fixed\", \"Changed\" (the kit's, kastlan's, Kurvenschmiede's) and\n * \"Features\", \"Bug Fixes\", \"Performance Improvements\" (the default preset's; keksdose\n * writes \"Performance\") are the same three ({@link changelogSectionKey}).\n * - **An item is its sentence**: the scope comes out as a value, the commit link and the\n * trailing references go, and the rest of the markdown is resolved\n * ({@link parseChangelogItem}).\n * - **An item is all its lines**: a breaking change's note that runs on over several\n * lines is one item, where both apps' parsers kept its first line (\"…the beta\n * allow-list and profile routes are\").\n *\n * Pure string work, no React: the page that shows it is the app's.\n */\n\n/**\n * What a section's heading means, whichever preset named it. `null` from\n * {@link changelogSectionKey} for a heading no preset writes (\"Known\", say): show its own\n * title then, rather than hide it behind a missing translation.\n */\nexport type ChangelogSectionKey =\n \"breaking\" | \"added\" | \"changed\" | \"fixed\" | \"deprecated\" | \"removed\" | \"security\" | \"reverted\";\n\n/** One bullet: its conventional-commit scope and its sentence. */\nexport interface ChangelogItem {\n /** `**admin:**` → `\"admin\"`; `null` for a bullet with no scope. */\n scope: string | null;\n /** The sentence, without the commit link, the trailing references or any markdown. */\n text: string;\n}\n\n/** One `###` section of a release. */\nexport interface ChangelogSection {\n /** The heading as written: \"Added\", \"Bug Fixes\", \"⚠ BREAKING CHANGES\". */\n title: string;\n /** {@link changelogSectionKey} of the title. */\n key: ChangelogSectionKey | null;\n /** Never empty: a section whose bullets all read as nothing is left out. */\n items: ChangelogItem[];\n}\n\n/** One release, as the file lists them: newest first. */\nexport interface ChangelogRelease {\n /** `\"0.13.9\"`, without brackets. */\n version: string;\n /** The release day, `YYYY-MM-DD`, or `null` when the heading carries none. */\n date: string | null;\n sections: ChangelogSection[];\n /** The prose between the heading and the first section, as written; `null` when there\n * is none. A hand-written release can carry its notes here alone. */\n body: string | null;\n}\n\n/** The items of one scope, from {@link groupByScope}. */\nexport interface ChangelogScopeGroup {\n scope: string | null;\n texts: string[];\n}\n\n// ── Items ─────────────────────────────────────────────────────────────────────\n\n/** `([abc1234](https://…/commit/…))`, the generator's commit link. Four hex digits at\n * least, git's shortest abbreviation. */\nconst COMMIT_LINK = /\\s*\\(\\[[0-9a-f]{4,40}\\]\\([^)]*\\)\\)/gi;\n\n/**\n * The trailing references, to the end of the line: every `, closes …` clause with EVERY\n * link to an issue after its word, or a run of such links with no word. Anchored at the\n * end, because a link inside the sentence is prose the author wrote; the run at the end\n * is machinery. `[^\\]]*#\\d+` reads `#455`, `dev#435` and `Eifi1/ui-kit#12` alike.\n *\n * The words are conventional-changelog's `referenceActions` (close, fix, resolve and their\n * inflections) and `references`, which conventional-changelog 8 writes where 7 wrote\n * `closes`: both spellings stay in a CHANGELOG.md for good, and keksdose's what's-new\n * dialog showed \"…, references\" after a bump that only knew `closes` (live #211). Version\n * 8 writes both clauses on one line — `, closes [#639](…), references [#405](…)` in\n * keksdose's 0.4.72 — so there can be more than one. A clause starts with the comma the\n * generator writes and a whole word, so \"a quick fix\" and \"disclose\" keep theirs.\n */\nconst TRAILING_REFS =\n /(?:(?:,\\s*\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|references?)(?:\\s*\\[[^\\]]*#\\d+\\]\\([^)]*\\))+)+|(?:\\s*\\[[^\\]]*#\\d+\\]\\([^)]*\\))+)\\s*$/i;\n\n/** A leading scope: `**admin:**`, or `**admin**:`. The colon makes it one: a hand-written\n * bullet that opens with a bold phrase (`**The path was wrong.** README …`) has none. */\nconst SCOPE = /^\\*\\*([^*:]+?)(?::\\*\\*|\\*\\*:)\\s*/;\n\n/** `[label](url)` inside the sentence: the label is what it meant. */\nconst INLINE_LINK = /\\[([^\\]]+)\\]\\([^)]*\\)/g;\n\n/**\n * One bullet, without its `* `, as its scope and its sentence:\n * `**admin:** a rename ([8f698eb](…)), closes [#12](…)` → `{ scope: \"admin\", text: \"a\n * rename\" }`. keksdose's rules (`changelog-format.ts`), which kastlan copied, with three\n * corrections the four files asked for: every reference clause goes, not the last alone;\n * a scope needs its colon; and a code span that opens with a dot keeps the space before\n * it.\n *\n * The commit link and the trailing references go BEFORE the general link rule, which\n * would unwrap them into bare hashes and issue numbers. Then emphasis and code spans are\n * resolved, and what the removals left is repaired: a space before punctuation that ends a\n * clause (not the one before a code span's `.mutate`), doubled spaces, and the comma that\n * introduced \", closes …\".\n */\nexport function parseChangelogItem(line: string): ChangelogItem {\n let text = line.replace(COMMIT_LINK, \"\").replace(TRAILING_REFS, \"\");\n let scope: string | null = null;\n const scoped = SCOPE.exec(text);\n if (scoped) {\n // A hand-written label can carry code (`**`Autocomplete`:**`): plain, like the text.\n scope = plain(scoped[1]).trim();\n text = text.slice(scoped[0].length);\n }\n text = plain(text)\n .replace(/\\s+([,.;:])(?=\\s|$)/g, \"$1\")\n .replace(/\\s{2,}/g, \" \")\n .replace(/[,;:]+\\s*$/, \"\")\n .trim();\n return { scope, text };\n}\n\n/** Links as their labels, emphasis and code spans as their text. */\nfunction plain(markdown: string): string {\n return markdown\n .replace(INLINE_LINK, \"$1\")\n .replace(/\\*\\*(.+?)\\*\\*/g, \"$1\")\n .replace(/`([^`]+)`/g, \"$1\");\n}\n\n/**\n * Items grouped by scope, in the order each scope first appears, with exact repeats\n * dropped (keksdose's `groupByScope`). A feature that lands over several commits gets a\n * bullet per commit; the same sentence twice goes, and two different sentences both stay —\n * whether they mean the same is not a regex's call. Takes raw bullets (keksdose's\n * backend sends them) or parsed items; an item that reads as nothing is left out.\n */\nexport function groupByScope(items: readonly (string | ChangelogItem)[]): ChangelogScopeGroup[] {\n const groups = new Map<string, ChangelogScopeGroup>();\n const seen = new Set<string>();\n for (const raw of items) {\n const { scope, text } = typeof raw === \"string\" ? parseChangelogItem(raw) : raw;\n if (!text) continue;\n const repeat = `${scope ?? \"\"}\\u0000${text.toLowerCase()}`;\n if (seen.has(repeat)) continue;\n seen.add(repeat);\n const group = groups.get(scope ?? \"\") ?? { scope, texts: [] };\n group.texts.push(text);\n groups.set(scope ?? \"\", group);\n }\n return [...groups.values()];\n}\n\n// ── Sections ──────────────────────────────────────────────────────────────────\n\n/** Each key's headings, lower-cased: the kit's `.versionrc.cjs` (and kastlan's and\n * Kurvenschmiede's), the default preset, keksdose's, and Keep a Changelog's. */\nconst SECTION_NAMES: Record<ChangelogSectionKey, readonly string[]> = {\n breaking: [\"breaking changes\", \"breaking\"],\n added: [\"added\", \"features\", \"feat\"],\n changed: [\"changed\", \"performance improvements\", \"performance\", \"code refactoring\"],\n fixed: [\"fixed\", \"bug fixes\", \"fixes\"],\n deprecated: [\"deprecated\"],\n removed: [\"removed\"],\n security: [\"security\"],\n reverted: [\"reverts\", \"reverted\"],\n};\n\nconst SECTION_KEYS = new Map(\n Object.entries(SECTION_NAMES).flatMap(([key, names]) => names.map((name) => [name, key as ChangelogSectionKey])),\n);\n\n/**\n * The key of a section's heading, for the app's own words: \"Added\" and \"Features\" are\n * `\"added\"`, \"Fixed\" and \"Bug Fixes\" `\"fixed\"`, \"Changed\", \"Performance Improvements\" and\n * keksdose's \"Performance\" `\"changed\"`, \"⚠ BREAKING CHANGES\" `\"breaking\"`, \"Reverts\"\n * `\"reverted\"`; Keep a Changelog's \"Deprecated\", \"Removed\" and \"Security\" are their own.\n * Case and a leading \"⚠\" don't count, nor a qualifier after a dash or in parentheses\n * (\"Fixed — layout\", \"Deprecated (removed in 0.35)\"). `null` for any other heading.\n */\nexport function changelogSectionKey(title: string): ChangelogSectionKey | null {\n const name = title\n .toLowerCase()\n .replace(/^[^\\p{L}]+/u, \"\")\n .split(/\\s+[—–-]\\s+|\\s*\\(/)[0]\n .trim();\n return SECTION_KEYS.get(name) ?? null;\n}\n\n// ── Releases ──────────────────────────────────────────────────────────────────\n\nconst VERSION = String.raw`\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.+-]*)?`;\n\n/** A release heading: `##` (or `###`, a patch under the default template), the version\n * bracketed with its compare link or bare, then the rest of the line. */\nconst RELEASE_HEADING = new RegExp(String.raw`^#{2,3}\\s+(?:\\[(${VERSION})\\](?:\\([^)]*\\))?|(${VERSION}))(.*)$`);\n\n/** The date in what follows the version, once the compare link is behind it. */\nconst DATE = /(?:^|[^\\d-])(\\d{4}-\\d{2}-\\d{2})(?![\\d-])/;\n\nconst HEADING = /^(#{1,6})\\s+(.*?)\\s*#*\\s*$/;\nconst BULLET = /^[*-]\\s+(.*)$/;\nconst NESTED_BULLET = /^\\s+[*-]\\s+/;\nconst FENCE = /^\\s*(`{3,}|~{3,})/;\n\n/** A section while its release is read: the bullets as written, continuations joined. */\ninterface OpenSection {\n title: string;\n bullets: string[];\n}\n\n/**\n * A CHANGELOG.md as its releases, in the file's order (newest first).\n *\n * What counts as what, line by line:\n *\n * - a `##` or `###` heading with a version starts a release (see the module note for the\n * shapes); any other `#` or `##` heading ends it, so an \"Unreleased\" block or the\n * file's title is no release's;\n * - inside a release, any other `###` heading starts a section, keyed by\n * {@link changelogSectionKey};\n * - a bullet at the start of the line is an item of its section. The lines straight\n * after it continue it, indented or not, as a breaking change's note runs on; a blank\n * line, a heading, a nested bullet or a code fence ends it. Nested bullets, prose\n * inside a section and fenced code (where a `- ` line is a diff) are no item;\n * - before the first section, the prose is the release's {@link ChangelogRelease.body}.\n *\n * A section whose items all read as nothing is left out, as kastlan's parser did.\n */\nexport function parseChangelog(markdown: string): ChangelogRelease[] {\n const releases: ChangelogRelease[] = [];\n let release: ChangelogRelease | null = null;\n let sections: OpenSection[] = [];\n let section: OpenSection | null = null;\n let body: string[] = [];\n /** The bullet that later lines continue, as an index into `section.bullets`. */\n let open: number | null = null;\n /** The opening fence while inside a code block. */\n let fence: string | null = null;\n\n const close = () => {\n if (!release) return;\n release.body = body.join(\"\\n\").trim() || null;\n release.sections = sections\n .map((s) => ({\n title: s.title,\n key: changelogSectionKey(s.title),\n items: s.bullets.map(parseChangelogItem).filter((item) => item.text !== \"\"),\n }))\n .filter((s) => s.items.length > 0);\n releases.push(release);\n release = null;\n };\n\n for (const raw of markdown.split(/\\r?\\n/)) {\n const line = raw.trimEnd();\n\n if (fence !== null) {\n if (line.trimStart().startsWith(fence)) fence = null;\n continue;\n }\n const fenced = FENCE.exec(line);\n if (fenced) {\n fence = fenced[1];\n open = null;\n continue;\n }\n\n const heading = HEADING.exec(line);\n if (heading) {\n open = null;\n const released = RELEASE_HEADING.exec(line);\n if (released) {\n close();\n const [, bracketed, bare, rest] = released;\n release = { version: bracketed ?? bare, date: DATE.exec(rest)?.[1] ?? null, sections: [], body: null };\n sections = [];\n section = null;\n body = [];\n } else if (heading[1].length <= 2) {\n close();\n } else if (heading[1].length === 3 && release) {\n section = { title: heading[2], bullets: [] };\n sections.push(section);\n }\n continue;\n }\n if (!release) continue;\n\n if (line.trim() === \"\") {\n open = null;\n if (!section) body.push(\"\");\n continue;\n }\n if (!section) {\n body.push(line);\n continue;\n }\n const bullet = BULLET.exec(line);\n if (bullet) {\n open = section.bullets.push(bullet[1]) - 1;\n } else if (NESTED_BULLET.test(line)) {\n open = null;\n } else if (open !== null) {\n section.bullets[open] += ` ${line.trim()}`;\n }\n }\n close();\n return releases;\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { isEditableContent } from "./editable-content.js";
2
3
  const MINUS_BEFORE_NUMBER = /\u2212(?=[\p{L}\p{Sc}.]{0,5}[\s\u00a0\u202f]*\d)/gu;
3
4
  function asciiMinusInNumbers(text) {
4
5
  return text.replace(MINUS_BEFORE_NUMBER, "-");
@@ -12,8 +13,7 @@ function hasMinusBeforeNumber(text) {
12
13
  function isEditable(node) {
13
14
  const el = node instanceof Element ? node : node?.parentElement;
14
15
  if (!el) return false;
15
- if (el.closest("input, textarea, select")) return true;
16
- return el.isContentEditable === true || el.closest('[contenteditable=""], [contenteditable="true"]') !== null;
16
+ return el.closest("input, textarea, select") !== null || isEditableContent(el);
17
17
  }
18
18
  function wrapTableParts(container) {
19
19
  const first = container.firstElementChild?.tagName;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/clipboard-minus.ts"],"sourcesContent":["/**\n * The copy handler for the one minus sign (0.49 KIT-1b, Marcel's follow-up to KIT-1).\n *\n * The kit prints a negative with U+2212 (lib/minus.ts). A spreadsheet does not read that\n * as a number: a figure copied out of a kit table and pasted into Excel landed as text.\n * So while a selection inside the page is copied, every U+2212 standing directly before a\n * number — optionally after a currency code or symbol and spaces: \"−CHF 1,250.50\",\n * \"−€2,000\", \"−US$12\", \"−12%\" — becomes the ASCII \"-\" in the clipboard, in `text/plain` AND\n * in `text/html` (Excel pastes the HTML flavour, as cells). The page keeps \"−\".\n *\n * A copy with no such minus is left to the browser untouched, and so is any copy out of an\n * input, a textarea or an editable element: what the person typed is theirs.\n *\n * Installed once per document by `UiKitProvider` (`copyAsciiMinus`, default `true`) —\n * the one part every app mounts, the signed-out pages included.\n */\n\n/**\n * U+2212, then up to five letters, currency symbols or dots, then spaces, then a digit.\n *\n * Five of them, not \"a code or one symbol\": English and Chinese write a currency with\n * letters before the symbol — \"−US$\", \"−CA$\", \"−A$\", \"−NZ$\", \"−HK$\", \"−R$\", \"−MX$\",\n * \"−JP¥\", \"−CN¥\" — and `narrowSymbol` with letters alone (\"−kr\", \"−zł\", \"−Kč\"). Those kept\n * U+2212 in the clipboard, and Excel pasted them as text: 35 of 351 locale × currency\n * pairs at the default display, every one English or Chinese, and 136 of 1428 with\n * `narrowSymbol` (refactor audit 2026-10-30, DSP-11). A minus with no digit after it\n * (\"x−y\", \"a − b\") is still left alone.\n */\nconst MINUS_BEFORE_NUMBER = /\\u2212(?=[\\p{L}\\p{Sc}.]{0,5}[\\s\\u00a0\\u202f]*\\d)/gu;\n\n/**\n * `text` with each U+2212 that stands before a number (in the sense above) as the ASCII\n * hyphen-minus, every other character as it was — for text an app hands a machine that\n * did not come from a formatter's `minus: \"ascii\"`.\n */\nexport function asciiMinusInNumbers(text: string): string {\n return text.replace(MINUS_BEFORE_NUMBER, \"-\");\n}\n\nfunction hasMinusBeforeNumber(text: string): boolean {\n MINUS_BEFORE_NUMBER.lastIndex = 0;\n const found = MINUS_BEFORE_NUMBER.test(text);\n MINUS_BEFORE_NUMBER.lastIndex = 0;\n return found;\n}\n\n/** An input, a textarea or an editable element: a copy there is the person's own text. */\nfunction isEditable(node: Node | null | undefined): boolean {\n const el = node instanceof Element ? node : node?.parentElement;\n if (!el) return false;\n if (el.closest(\"input, textarea, select\")) return true;\n return (\n (el as HTMLElement).isContentEditable === true ||\n el.closest('[contenteditable=\"\"], [contenteditable=\"true\"]') !== null\n );\n}\n\n/** Table parts copied alone are wrapped back into a table, which Excel reads as cells. */\nfunction wrapTableParts(container: HTMLElement): string {\n const first = container.firstElementChild?.tagName;\n const html = container.innerHTML;\n if (first === \"TD\" || first === \"TH\") return `<table><tbody><tr>${html}</tr></tbody></table>`;\n if (first === \"TR\") return `<table><tbody>${html}</tbody></table>`;\n if (first === \"TBODY\" || first === \"THEAD\" || first === \"TFOOT\") return `<table>${html}</table>`;\n return html;\n}\n\nfunction onCopy(event: ClipboardEvent): void {\n if (event.defaultPrevented || !event.clipboardData) return;\n const doc = (event.target as Node | null)?.ownerDocument ?? document;\n if (isEditable(event.target as Node | null) || isEditable(doc.activeElement)) return;\n const selection = doc.getSelection();\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;\n if (isEditable(selection.anchorNode) || isEditable(selection.focusNode)) return;\n const text = selection.toString();\n if (!hasMinusBeforeNumber(text)) return;\n\n const container = doc.createElement(\"div\");\n for (let i = 0; i < selection.rangeCount; i++) container.appendChild(selection.getRangeAt(i).cloneContents());\n const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT);\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n node.nodeValue = asciiMinusInNumbers(node.nodeValue ?? \"\");\n }\n event.clipboardData.setData(\"text/plain\", asciiMinusInNumbers(text));\n event.clipboardData.setData(\"text/html\", wrapTableParts(container));\n event.preventDefault();\n}\n\nconst installs = new WeakMap<Document, number>();\n\n/**\n * Listen for copies on `doc` until the returned function is called. Counted: nested\n * providers install it once, and the last one to go removes it.\n */\nexport function installClipboardMinus(doc: Document = document): () => void {\n const count = installs.get(doc) ?? 0;\n if (count === 0) doc.addEventListener(\"copy\", onCopy);\n installs.set(doc, count + 1);\n let done = false;\n return () => {\n if (done) return;\n done = true;\n const left = (installs.get(doc) ?? 1) - 1;\n installs.set(doc, left);\n if (left === 0) doc.removeEventListener(\"copy\", onCopy);\n };\n}\n"],"mappings":";AA4BA,MAAM,sBAAsB;AAOrB,SAAS,oBAAoB,MAAsB;AACxD,SAAO,KAAK,QAAQ,qBAAqB,GAAG;AAC9C;AAEA,SAAS,qBAAqB,MAAuB;AACnD,sBAAoB,YAAY;AAChC,QAAM,QAAQ,oBAAoB,KAAK,IAAI;AAC3C,sBAAoB,YAAY;AAChC,SAAO;AACT;AAGA,SAAS,WAAW,MAAwC;AAC1D,QAAM,KAAK,gBAAgB,UAAU,OAAO,MAAM;AAClD,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI,GAAG,QAAQ,yBAAyB,EAAG,QAAO;AAClD,SACG,GAAmB,sBAAsB,QAC1C,GAAG,QAAQ,gDAAgD,MAAM;AAErE;AAGA,SAAS,eAAe,WAAgC;AACtD,QAAM,QAAQ,UAAU,mBAAmB;AAC3C,QAAM,OAAO,UAAU;AACvB,MAAI,UAAU,QAAQ,UAAU,KAAM,QAAO,qBAAqB,IAAI;AACtE,MAAI,UAAU,KAAM,QAAO,iBAAiB,IAAI;AAChD,MAAI,UAAU,WAAW,UAAU,WAAW,UAAU,QAAS,QAAO,UAAU,IAAI;AACtF,SAAO;AACT;AAEA,SAAS,OAAO,OAA6B;AAC3C,MAAI,MAAM,oBAAoB,CAAC,MAAM,cAAe;AACpD,QAAM,MAAO,MAAM,QAAwB,iBAAiB;AAC5D,MAAI,WAAW,MAAM,MAAqB,KAAK,WAAW,IAAI,aAAa,EAAG;AAC9E,QAAM,YAAY,IAAI,aAAa;AACnC,MAAI,CAAC,aAAa,UAAU,eAAe,KAAK,UAAU,YAAa;AACvE,MAAI,WAAW,UAAU,UAAU,KAAK,WAAW,UAAU,SAAS,EAAG;AACzE,QAAM,OAAO,UAAU,SAAS;AAChC,MAAI,CAAC,qBAAqB,IAAI,EAAG;AAEjC,QAAM,YAAY,IAAI,cAAc,KAAK;AACzC,WAAS,IAAI,GAAG,IAAI,UAAU,YAAY,IAAK,WAAU,YAAY,UAAU,WAAW,CAAC,EAAE,cAAc,CAAC;AAC5G,QAAM,SAAS,IAAI,iBAAiB,WAAW,WAAW,SAAS;AACnE,WAAS,OAAO,OAAO,SAAS,GAAG,MAAM,OAAO,OAAO,SAAS,GAAG;AACjE,SAAK,YAAY,oBAAoB,KAAK,aAAa,EAAE;AAAA,EAC3D;AACA,QAAM,cAAc,QAAQ,cAAc,oBAAoB,IAAI,CAAC;AACnE,QAAM,cAAc,QAAQ,aAAa,eAAe,SAAS,CAAC;AAClE,QAAM,eAAe;AACvB;AAEA,MAAM,WAAW,oBAAI,QAA0B;AAMxC,SAAS,sBAAsB,MAAgB,UAAsB;AAC1E,QAAM,QAAQ,SAAS,IAAI,GAAG,KAAK;AACnC,MAAI,UAAU,EAAG,KAAI,iBAAiB,QAAQ,MAAM;AACpD,WAAS,IAAI,KAAK,QAAQ,CAAC;AAC3B,MAAI,OAAO;AACX,SAAO,MAAM;AACX,QAAI,KAAM;AACV,WAAO;AACP,UAAM,QAAQ,SAAS,IAAI,GAAG,KAAK,KAAK;AACxC,aAAS,IAAI,KAAK,IAAI;AACtB,QAAI,SAAS,EAAG,KAAI,oBAAoB,QAAQ,MAAM;AAAA,EACxD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/clipboard-minus.ts"],"sourcesContent":["/**\n * The copy handler for the one minus sign (0.49 KIT-1b, Marcel's follow-up to KIT-1).\n *\n * The kit prints a negative with U+2212 (lib/minus.ts). A spreadsheet does not read that\n * as a number: a figure copied out of a kit table and pasted into Excel landed as text.\n * So while a selection inside the page is copied, every U+2212 standing directly before a\n * number — optionally after a currency code or symbol and spaces: \"−CHF 1,250.50\",\n * \"−€2,000\", \"−US$12\", \"−12%\" — becomes the ASCII \"-\" in the clipboard, in `text/plain` AND\n * in `text/html` (Excel pastes the HTML flavour, as cells). The page keeps \"−\".\n *\n * A copy with no such minus is left to the browser untouched, and so is any copy out of an\n * input, a textarea or an editable element: what the person typed is theirs.\n *\n * Installed once per document by `UiKitProvider` (`copyAsciiMinus`, default `true`) —\n * the one part every app mounts, the signed-out pages included.\n */\n\nimport { isEditableContent } from \"./editable-content\";\n\n/**\n * U+2212, then up to five letters, currency symbols or dots, then spaces, then a digit.\n *\n * Five of them, not \"a code or one symbol\": English and Chinese write a currency with\n * letters before the symbol — \"−US$\", \"−CA$\", \"−A$\", \"−NZ$\", \"−HK$\", \"−R$\", \"−MX$\",\n * \"−JP¥\", \"−CN¥\" — and `narrowSymbol` with letters alone (\"−kr\", \"−zł\", \"−Kč\"). Those kept\n * U+2212 in the clipboard, and Excel pasted them as text: 35 of 351 locale × currency\n * pairs at the default display, every one English or Chinese, and 136 of 1428 with\n * `narrowSymbol` (refactor audit 2026-10-30, DSP-11). A minus with no digit after it\n * (\"x−y\", \"a − b\") is still left alone.\n */\nconst MINUS_BEFORE_NUMBER = /\\u2212(?=[\\p{L}\\p{Sc}.]{0,5}[\\s\\u00a0\\u202f]*\\d)/gu;\n\n/**\n * `text` with each U+2212 that stands before a number (in the sense above) as the ASCII\n * hyphen-minus, every other character as it was — for text an app hands a machine that\n * did not come from a formatter's `minus: \"ascii\"`.\n */\nexport function asciiMinusInNumbers(text: string): string {\n return text.replace(MINUS_BEFORE_NUMBER, \"-\");\n}\n\nfunction hasMinusBeforeNumber(text: string): boolean {\n MINUS_BEFORE_NUMBER.lastIndex = 0;\n const found = MINUS_BEFORE_NUMBER.test(text);\n MINUS_BEFORE_NUMBER.lastIndex = 0;\n return found;\n}\n\n/**\n * An input, a textarea or editable content: a copy there is the person's own text.\n * Editable content by the kit's one rule (lib/editable-content, 0.54 follow-up): beside\n * `isContentEditable` this asked a `[contenteditable=\"\"], [contenteditable=\"true\"]`\n * selector, which found the editor round a `contenteditable=\"false\"` island (a chip no\n * one typed) and left its figure's U+2212 in the clipboard — in every engine — and,\n * where the engine has no `isContentEditable` (jsdom), missed `plaintext-only` and took\n * the minus out of what someone typed there.\n */\nfunction isEditable(node: Node | null | undefined): boolean {\n const el = node instanceof Element ? node : node?.parentElement;\n if (!el) return false;\n return el.closest(\"input, textarea, select\") !== null || isEditableContent(el);\n}\n\n/** Table parts copied alone are wrapped back into a table, which Excel reads as cells. */\nfunction wrapTableParts(container: HTMLElement): string {\n const first = container.firstElementChild?.tagName;\n const html = container.innerHTML;\n if (first === \"TD\" || first === \"TH\") return `<table><tbody><tr>${html}</tr></tbody></table>`;\n if (first === \"TR\") return `<table><tbody>${html}</tbody></table>`;\n if (first === \"TBODY\" || first === \"THEAD\" || first === \"TFOOT\") return `<table>${html}</table>`;\n return html;\n}\n\nfunction onCopy(event: ClipboardEvent): void {\n if (event.defaultPrevented || !event.clipboardData) return;\n const doc = (event.target as Node | null)?.ownerDocument ?? document;\n if (isEditable(event.target as Node | null) || isEditable(doc.activeElement)) return;\n const selection = doc.getSelection();\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;\n if (isEditable(selection.anchorNode) || isEditable(selection.focusNode)) return;\n const text = selection.toString();\n if (!hasMinusBeforeNumber(text)) return;\n\n const container = doc.createElement(\"div\");\n for (let i = 0; i < selection.rangeCount; i++) container.appendChild(selection.getRangeAt(i).cloneContents());\n const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT);\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n node.nodeValue = asciiMinusInNumbers(node.nodeValue ?? \"\");\n }\n event.clipboardData.setData(\"text/plain\", asciiMinusInNumbers(text));\n event.clipboardData.setData(\"text/html\", wrapTableParts(container));\n event.preventDefault();\n}\n\nconst installs = new WeakMap<Document, number>();\n\n/**\n * Listen for copies on `doc` until the returned function is called. Counted: nested\n * providers install it once, and the last one to go removes it.\n */\nexport function installClipboardMinus(doc: Document = document): () => void {\n const count = installs.get(doc) ?? 0;\n if (count === 0) doc.addEventListener(\"copy\", onCopy);\n installs.set(doc, count + 1);\n let done = false;\n return () => {\n if (done) return;\n done = true;\n const left = (installs.get(doc) ?? 1) - 1;\n installs.set(doc, left);\n if (left === 0) doc.removeEventListener(\"copy\", onCopy);\n };\n}\n"],"mappings":";AAiBA,SAAS,yBAAyB;AAalC,MAAM,sBAAsB;AAOrB,SAAS,oBAAoB,MAAsB;AACxD,SAAO,KAAK,QAAQ,qBAAqB,GAAG;AAC9C;AAEA,SAAS,qBAAqB,MAAuB;AACnD,sBAAoB,YAAY;AAChC,QAAM,QAAQ,oBAAoB,KAAK,IAAI;AAC3C,sBAAoB,YAAY;AAChC,SAAO;AACT;AAWA,SAAS,WAAW,MAAwC;AAC1D,QAAM,KAAK,gBAAgB,UAAU,OAAO,MAAM;AAClD,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO,GAAG,QAAQ,yBAAyB,MAAM,QAAQ,kBAAkB,EAAE;AAC/E;AAGA,SAAS,eAAe,WAAgC;AACtD,QAAM,QAAQ,UAAU,mBAAmB;AAC3C,QAAM,OAAO,UAAU;AACvB,MAAI,UAAU,QAAQ,UAAU,KAAM,QAAO,qBAAqB,IAAI;AACtE,MAAI,UAAU,KAAM,QAAO,iBAAiB,IAAI;AAChD,MAAI,UAAU,WAAW,UAAU,WAAW,UAAU,QAAS,QAAO,UAAU,IAAI;AACtF,SAAO;AACT;AAEA,SAAS,OAAO,OAA6B;AAC3C,MAAI,MAAM,oBAAoB,CAAC,MAAM,cAAe;AACpD,QAAM,MAAO,MAAM,QAAwB,iBAAiB;AAC5D,MAAI,WAAW,MAAM,MAAqB,KAAK,WAAW,IAAI,aAAa,EAAG;AAC9E,QAAM,YAAY,IAAI,aAAa;AACnC,MAAI,CAAC,aAAa,UAAU,eAAe,KAAK,UAAU,YAAa;AACvE,MAAI,WAAW,UAAU,UAAU,KAAK,WAAW,UAAU,SAAS,EAAG;AACzE,QAAM,OAAO,UAAU,SAAS;AAChC,MAAI,CAAC,qBAAqB,IAAI,EAAG;AAEjC,QAAM,YAAY,IAAI,cAAc,KAAK;AACzC,WAAS,IAAI,GAAG,IAAI,UAAU,YAAY,IAAK,WAAU,YAAY,UAAU,WAAW,CAAC,EAAE,cAAc,CAAC;AAC5G,QAAM,SAAS,IAAI,iBAAiB,WAAW,WAAW,SAAS;AACnE,WAAS,OAAO,OAAO,SAAS,GAAG,MAAM,OAAO,OAAO,SAAS,GAAG;AACjE,SAAK,YAAY,oBAAoB,KAAK,aAAa,EAAE;AAAA,EAC3D;AACA,QAAM,cAAc,QAAQ,cAAc,oBAAoB,IAAI,CAAC;AACnE,QAAM,cAAc,QAAQ,aAAa,eAAe,SAAS,CAAC;AAClE,QAAM,eAAe;AACvB;AAEA,MAAM,WAAW,oBAAI,QAA0B;AAMxC,SAAS,sBAAsB,MAAgB,UAAsB;AAC1E,QAAM,QAAQ,SAAS,IAAI,GAAG,KAAK;AACnC,MAAI,UAAU,EAAG,KAAI,iBAAiB,QAAQ,MAAM;AACpD,WAAS,IAAI,KAAK,QAAQ,CAAC;AAC3B,MAAI,OAAO;AACX,SAAO,MAAM;AACX,QAAI,KAAM;AACV,WAAO;AACP,UAAM,QAAQ,SAAS,IAAI,GAAG,KAAK,KAAK;AACxC,aAAS,IAAI,KAAK,IAAI;AACtB,QAAI,SAAS,EAAG,KAAI,oBAAoB,QAAQ,MAAM;AAAA,EACxD;AACF;","names":[]}
@@ -25,7 +25,32 @@
25
25
  */
26
26
  /** Zero-pad a number to two digits ("3" -> "03"). */
27
27
  export declare function pad(n: number): string;
28
- /** Today as "YYYY-MM-DD" on the LOCAL calendar (see the module note). */
28
+ /**
29
+ * Move "today" for every helper here, and for the kit's calendars and pickers, to `day`
30
+ * ("YYYY-MM-DD") — the dev clock's day, which the app reads at boot and from its dev menu's
31
+ * Time rows (`setToday(answer.today)`) — or back to the device's own with `null`. A string
32
+ * that is not a real day is the device's own too: a dev tool must not stop the app.
33
+ *
34
+ * Only the DATE moves, as on the server: {@link currentDate} keeps the device's time of
35
+ * day, and a timestamp the kit takes (`new Date()` for a sync, a crash report, a relative
36
+ * time) stays on the device's real clock. Not for production code: the app gates the call
37
+ * (`import.meta.env.DEV`), as it gates the menu. The kit's `DevMenu` puts its "Overrides
38
+ * on" dot on the flask while a day is set.
39
+ */
40
+ export declare function setToday(day: string | null): void;
41
+ /** The day {@link setToday} moved "today" to, or `null` while it is the device's own. */
42
+ export declare function todayOverride(): string | null;
43
+ /** Hear {@link setToday} move the day; returns the unsubscribe. With {@link todayOverride},
44
+ * `useSyncExternalStore`'s pair. */
45
+ export declare function subscribeToday(listener: () => void): () => void;
46
+ /**
47
+ * Now, on the day the kit takes for today: the device's clock, or — while {@link setToday}
48
+ * has moved it — that day at the device's time of day. A new Date on every call, free to
49
+ * mutate. Every "what day is it" here reads it; read it for an app's own.
50
+ */
51
+ export declare function currentDate(): Date;
52
+ /** Today as "YYYY-MM-DD" on the LOCAL calendar (see the module note), on the day
53
+ * {@link currentDate} takes for today. */
29
54
  export declare function todayIso(): string;
30
55
  /** A Date as a local-time "YYYY-MM-DD" string. */
31
56
  export declare function toLocalIso(d: Date): string;
@@ -60,7 +85,13 @@ export declare function parseIsoDate(s: string): Date | null;
60
85
  * unparseable). Defaults to the locale's short numeric date; pass `options` to
61
86
  * override (e.g. `{ dateStyle: "medium" }`). `locale` may be `undefined` since 0.34 —
62
87
  * the runtime's default, as `Intl` reads it and as every other kit formatter takes it
63
- * (DUP-11: DatePicker re-wrapped this function only to pass none). */
88
+ * (DUP-11: DatePicker re-wrapped this function only to pass none).
89
+ *
90
+ * Through the kit's formatter cache since 0.54 (keksdose audit follow-up, 2026-11-01):
91
+ * `toLocaleDateString` builds a formatter per call, and this one runs for the date
92
+ * pickers' trigger on every render and for an app's date column once per row. The output
93
+ * is `toLocaleDateString`'s: the same options, with the date it adds when they name none
94
+ * (`asDateOptions`). */
64
95
  export declare function formatIsoDate(iso: string, locale: string | undefined, options?: Intl.DateTimeFormatOptions): string;
65
96
  /** A "YYYY-MM-DD" string shifted by `days` (negative shifts back), or the input
66
97
  * unchanged when it isn't a parseable date.
@@ -100,7 +131,8 @@ export interface DateRangePreset {
100
131
  export declare function dateRangePresets(): DateRangePreset[];
101
132
  /** The last `n` WHOLE calendar months before the current one, as an inclusive
102
133
  * `{ from, to }` — on 15 May, `n = 3` is 1 February … 30 April. Local-calendar, like
103
- * everything here; `now` is for tests and for a host with its own clock. */
134
+ * everything here; `now` is for tests and for a host with its own clock (default
135
+ * {@link currentDate}). */
104
136
  export declare function lastFullMonthsRange(n: number, now?: Date): {
105
137
  from: string;
106
138
  to: string;
@@ -116,7 +148,7 @@ export interface CalendarMonthPresetOptions {
116
148
  months?: readonly number[];
117
149
  /** How many whole years each year preset spans. Default `[2]`. */
118
150
  years?: readonly number[];
119
- /** The day to count back from. Default: now, on the local calendar. */
151
+ /** The day to count back from. Default: now, on the local calendar ({@link currentDate}). */
120
152
  now?: Date;
121
153
  }
122
154
  /**
package/dist/lib/dates.js CHANGED
@@ -1,9 +1,35 @@
1
1
  "use client";
2
+ import { dateFormatter } from "./intl-cache.js";
2
3
  function pad(n) {
3
4
  return String(n).padStart(2, "0");
4
5
  }
6
+ let movedToday = null;
7
+ const todayListeners = /* @__PURE__ */ new Set();
8
+ function setToday(day) {
9
+ const next = day !== null && parseIsoDate(day) !== null ? day : null;
10
+ if (next === movedToday) return;
11
+ movedToday = next;
12
+ for (const listener of [...todayListeners]) listener();
13
+ }
14
+ function todayOverride() {
15
+ return movedToday;
16
+ }
17
+ function subscribeToday(listener) {
18
+ todayListeners.add(listener);
19
+ return () => {
20
+ todayListeners.delete(listener);
21
+ };
22
+ }
23
+ function currentDate() {
24
+ const now = /* @__PURE__ */ new Date();
25
+ if (movedToday !== null) {
26
+ const [y, m, d] = movedToday.split("-").map(Number);
27
+ now.setFullYear(y, m - 1, d);
28
+ }
29
+ return now;
30
+ }
5
31
  function todayIso() {
6
- return toLocalIso(/* @__PURE__ */ new Date());
32
+ return toLocalIso(currentDate());
7
33
  }
8
34
  function toLocalIso(d) {
9
35
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
@@ -12,39 +38,39 @@ function monthKey(d) {
12
38
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;
13
39
  }
14
40
  function shiftIso(days) {
15
- const d = /* @__PURE__ */ new Date();
41
+ const d = currentDate();
16
42
  d.setDate(d.getDate() + days);
17
43
  return toLocalIso(d);
18
44
  }
19
45
  function startOfWeekIso() {
20
- const d = /* @__PURE__ */ new Date();
46
+ const d = currentDate();
21
47
  const day = d.getDay();
22
48
  const diff = (day + 6) % 7;
23
49
  d.setDate(d.getDate() - diff);
24
50
  return toLocalIso(d);
25
51
  }
26
52
  function startOfMonthIso(offsetMonths = 0) {
27
- const d = /* @__PURE__ */ new Date();
53
+ const d = currentDate();
28
54
  d.setDate(1);
29
55
  d.setMonth(d.getMonth() + offsetMonths);
30
56
  return toLocalIso(d);
31
57
  }
32
58
  function endOfMonthIso(offsetMonths = 0) {
33
- const d = /* @__PURE__ */ new Date();
59
+ const d = currentDate();
34
60
  d.setDate(1);
35
61
  d.setMonth(d.getMonth() + offsetMonths + 1);
36
62
  d.setDate(0);
37
63
  return toLocalIso(d);
38
64
  }
39
65
  function startOfYearIso(offsetYears = 0) {
40
- const d = /* @__PURE__ */ new Date();
66
+ const d = currentDate();
41
67
  d.setMonth(0);
42
68
  d.setDate(1);
43
69
  d.setFullYear(d.getFullYear() + offsetYears);
44
70
  return toLocalIso(d);
45
71
  }
46
72
  function endOfYearIso(offsetYears = 0) {
47
- const d = /* @__PURE__ */ new Date();
73
+ const d = currentDate();
48
74
  d.setMonth(11);
49
75
  d.setDate(31);
50
76
  d.setFullYear(d.getFullYear() + offsetYears);
@@ -60,9 +86,15 @@ function parseIsoDate(s) {
60
86
  if (dt.getFullYear() !== y || dt.getMonth() !== m - 1 || dt.getDate() !== d) return null;
61
87
  return dt;
62
88
  }
89
+ const DATE_FIELDS = ["weekday", "year", "month", "day", "dateStyle"];
90
+ const NUMERIC_DATE = { year: "numeric", month: "numeric", day: "numeric" };
91
+ function asDateOptions(options) {
92
+ if (!options) return NUMERIC_DATE;
93
+ return DATE_FIELDS.some((field) => options[field] !== void 0) ? options : { ...options, ...NUMERIC_DATE };
94
+ }
63
95
  function formatIsoDate(iso, locale, options) {
64
96
  const d = parseIsoDate(iso);
65
- return d ? d.toLocaleDateString(locale, options) : "";
97
+ return d ? dateFormatter(locale, asDateOptions(options)).format(d) : "";
66
98
  }
67
99
  function addDaysIso(iso, days) {
68
100
  const d = parseIsoDate(iso);
@@ -91,7 +123,7 @@ function sameYmd(a, b) {
91
123
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
92
124
  }
93
125
  function dateRangePresets() {
94
- const mondayOffset = ((/* @__PURE__ */ new Date()).getDay() + 6) % 7;
126
+ const mondayOffset = (currentDate().getDay() + 6) % 7;
95
127
  return [
96
128
  { key: "today", from: todayIso(), to: todayIso() },
97
129
  { key: "yesterday", from: shiftIso(-1), to: shiftIso(-1) },
@@ -106,17 +138,17 @@ function dateRangePresets() {
106
138
  { key: "last_year", from: startOfYearIso(-1), to: endOfYearIso(-1) }
107
139
  ];
108
140
  }
109
- function lastFullMonthsRange(n, now = /* @__PURE__ */ new Date()) {
141
+ function lastFullMonthsRange(n, now = currentDate()) {
110
142
  const from = new Date(now.getFullYear(), now.getMonth() - n, 1);
111
143
  const to = new Date(now.getFullYear(), now.getMonth(), 0);
112
144
  return { from: toLocalIso(from), to: toLocalIso(to) };
113
145
  }
114
- function lastFullYearsRange(n, now = /* @__PURE__ */ new Date()) {
146
+ function lastFullYearsRange(n, now = currentDate()) {
115
147
  const year = now.getFullYear();
116
148
  return { from: `${year - n}-01-01`, to: `${year - 1}-12-31` };
117
149
  }
118
150
  function calendarMonthPresets(options = {}) {
119
- const { months = [3, 6], years = [2], now = /* @__PURE__ */ new Date() } = options;
151
+ const { months = [3, 6], years = [2], now = currentDate() } = options;
120
152
  return [
121
153
  ...months.map((n) => ({ key: `last_${n}_full_months`, ...lastFullMonthsRange(n, now) })),
122
154
  ...years.map((n) => ({ key: `last_${n}_full_years`, ...lastFullYearsRange(n, now) }))
@@ -126,6 +158,7 @@ export {
126
158
  addDaysIso,
127
159
  addMonthsClamped,
128
160
  calendarMonthPresets,
161
+ currentDate,
129
162
  dateRangePresets,
130
163
  endOfMonthIso,
131
164
  endOfYearIso,
@@ -137,11 +170,14 @@ export {
137
170
  pad,
138
171
  parseIsoDate,
139
172
  sameYmd,
173
+ setToday,
140
174
  shiftIso,
141
175
  startOfMonthIso,
142
176
  startOfWeekIso,
143
177
  startOfYearIso,
178
+ subscribeToday,
144
179
  toLocalIso,
145
- todayIso
180
+ todayIso,
181
+ todayOverride
146
182
  };
147
183
  //# sourceMappingURL=dates.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/dates.ts"],"sourcesContent":["/**\n * ISO date helpers, collected from copies that were scattered across the pages.\n *\n * **Every \"what day is it\" helper here reads the LOCAL calendar** — one family, not\n * two. It used to be two: these all built a local Date and then read it back through\n * `toISOString()`, which is UTC, so east of Greenwich they all returned YESTERDAY\n * between local midnight and the UTC rollover. The old module note called that\n * deliberate and warned against changing it. It was not defensible, and dev#471 is\n * what it cost:\n *\n * > *\"Just entered a tx now from scheduled. But it still shows as upcoming. Is there\n * > a problem with timezones? It is currently 1 o clock middle european summer time.\"*\n *\n * There was: at 01:00 CEST `todayIso()` answered with the 14th, the row the scheduler\n * had just booked was dated the 15th, and `isUpcoming` compares the two strings — so a\n * transaction entered a second ago read as one still to come. The same slip moved\n * `startOfMonthIso()` onto the last day of the PREVIOUS month for an hour every first\n * of the month, which is a report range nobody would have questioned.\n *\n * A calendar day only means anything to the human reading it, and every caller here —\n * the register's today, the date-range presets, the budget cursor, a new\n * transaction's default date — is asking that question. There is no caller for whom\n * \"the UTC day\" is the right answer; the server compares plain dates with no zone at\n * all, so sending it the user's local day is also what it wants.\n */\n\n/** Zero-pad a number to two digits (\"3\" -> \"03\"). */\nexport function pad(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\n/** Today as \"YYYY-MM-DD\" on the LOCAL calendar (see the module note). */\nexport function todayIso(): string {\n return toLocalIso(new Date());\n}\n\n/** A Date as a local-time \"YYYY-MM-DD\" string. */\nexport function toLocalIso(d: Date): string {\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/** A Date as a local-time \"YYYY-MM\" month key. */\nexport function monthKey(d: Date): string {\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;\n}\n\n// ---------- Date-range presets + calendar helpers ----------\n// Local-calendar like todayIso (see the module note); used by the data-table date\n// filter and its MiniCalendar. Each one mutates a local Date and must therefore READ\n// it back locally — going out through toISOString() is what shifted every one of\n// these by a day near midnight (dev#471).\n\n/** Today shifted by `days`, as \"YYYY-MM-DD\". */\nexport function shiftIso(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n return toLocalIso(d);\n}\n\n/** First day (Monday) of the current week, as \"YYYY-MM-DD\". */\nexport function startOfWeekIso(): string {\n const d = new Date();\n const day = d.getDay();\n const diff = (day + 6) % 7; // monday = 0\n d.setDate(d.getDate() - diff);\n return toLocalIso(d);\n}\n\n/** First day of the month `offsetMonths` from now, as \"YYYY-MM-DD\". */\nexport function startOfMonthIso(offsetMonths = 0): string {\n const d = new Date();\n d.setDate(1);\n d.setMonth(d.getMonth() + offsetMonths);\n return toLocalIso(d);\n}\n\n/** Last day of the month `offsetMonths` from now, as \"YYYY-MM-DD\". */\nexport function endOfMonthIso(offsetMonths = 0): string {\n const d = new Date();\n d.setDate(1);\n d.setMonth(d.getMonth() + offsetMonths + 1);\n d.setDate(0);\n return toLocalIso(d);\n}\n\n/** First day of the year `offsetYears` from now, as \"YYYY-MM-DD\". */\nexport function startOfYearIso(offsetYears = 0): string {\n const d = new Date();\n d.setMonth(0);\n d.setDate(1);\n d.setFullYear(d.getFullYear() + offsetYears);\n return toLocalIso(d);\n}\n\n/** Last day of the year `offsetYears` from now, as \"YYYY-MM-DD\". */\nexport function endOfYearIso(offsetYears = 0): string {\n const d = new Date();\n d.setMonth(11);\n d.setDate(31);\n d.setFullYear(d.getFullYear() + offsetYears);\n return toLocalIso(d);\n}\n\n/** Parse a \"YYYY-MM-DD\" string to a local-midnight Date, or null if malformed —\n * where \"malformed\" includes a date that does not exist.\n *\n * The falsy check alone was not enough. `new Date(y, m - 1, d)` ROLLS OVER out-of-range\n * components rather than refusing them, so \"2026-13-45\" came back as 2027-02-14 and\n * \"2026-02-30\" as 2026-03-02: a valid-looking Date for a day that never happened. No\n * caller could tell — {@link formatIsoDate} blanks only on `null`, so the invented\n * date formatted and displayed exactly like a real one.\n *\n * The round-trip is the check: build the Date, then read the components back off it.\n * A value that rolled over cannot answer with the numbers it was given. That is the\n * same shape {@link sameYmd} already expresses, applied to a Date and its source. */\nexport function parseIsoDate(s: string): Date | null {\n if (!s) return null;\n const [y, m, d] = s.split(\"-\").map(Number);\n if (!y || !m || !d) return null;\n if (!Number.isInteger(y) || !Number.isInteger(m) || !Number.isInteger(d)) return null;\n const dt = new Date(y, m - 1, d);\n if (Number.isNaN(dt.getTime())) return null;\n if (dt.getFullYear() !== y || dt.getMonth() !== m - 1 || dt.getDate() !== d) return null;\n return dt;\n}\n\n/** Format a \"YYYY-MM-DD\" string for display in `locale` (empty string when\n * unparseable). Defaults to the locale's short numeric date; pass `options` to\n * override (e.g. `{ dateStyle: \"medium\" }`). `locale` may be `undefined` since 0.34 —\n * the runtime's default, as `Intl` reads it and as every other kit formatter takes it\n * (DUP-11: DatePicker re-wrapped this function only to pass none). */\nexport function formatIsoDate(iso: string, locale: string | undefined, options?: Intl.DateTimeFormatOptions): string {\n const d = parseIsoDate(iso);\n return d ? d.toLocaleDateString(locale, options) : \"\";\n}\n\n/** A \"YYYY-MM-DD\" string shifted by `days` (negative shifts back), or the input\n * unchanged when it isn't a parseable date.\n *\n * Local-time throughout — {@link parseIsoDate} builds a local-midnight Date and\n * {@link toLocalIso} reads local components back, so this never slides a day the way\n * a `toISOString()` round-trip does west of UTC. */\nexport function addDaysIso(iso: string, days: number): string {\n const d = parseIsoDate(iso);\n if (!d) return iso;\n d.setDate(d.getDate() + days);\n return toLocalIso(d);\n}\n\n/**\n * `d` moved by whole months, keeping the day of the month where the target has one.\n * `setMonth` alone ROLLS OVER — 31 January + 1 month is 3 March — which would skip\n * February entirely for anyone paging through the year with PageDown. Returns a new\n * Date; `d` is not touched.\n */\nexport function addMonthsClamped(d: Date, months: number): Date {\n const day = d.getDate();\n const out = new Date(d.getFullYear(), d.getMonth() + months, 1);\n const lastOfTarget = new Date(out.getFullYear(), out.getMonth() + 1, 0).getDate();\n out.setDate(Math.min(day, lastOfTarget));\n return out;\n}\n\n/**\n * The first day of the week in `locale`, as a `Date#getDay` index (0 = Sunday).\n *\n * `Intl.Locale#getWeekInfo()` is the standard spelling; V8 shipped it first as the\n * `weekInfo` accessor, and Firefox has neither yet — hence both reads and a Monday\n * fallback, which is ISO 8601. Its `firstDay` counts 1 = Monday … 7 = Sunday, so `% 7`\n * maps it onto `getDay`. `undefined` asks for the runtime's default locale.\n */\nexport function localeWeekStart(locale: string | undefined): 0 | 1 | 2 | 3 | 4 | 5 | 6 {\n try {\n const tag = locale ?? new Intl.DateTimeFormat().resolvedOptions().locale;\n const loc = new Intl.Locale(tag) as Intl.Locale & {\n getWeekInfo?: () => { firstDay: number };\n weekInfo?: { firstDay: number };\n };\n const firstDay = (loc.getWeekInfo?.() ?? loc.weekInfo)?.firstDay;\n if (typeof firstDay === \"number\") return (firstDay % 7) as 0 | 1 | 2 | 3 | 4 | 5 | 6;\n } catch {\n // A malformed tag throws from `Intl.Locale`; a calendar still has to render.\n }\n return 1;\n}\n\n/** Whether two Dates fall on the same local calendar day. */\nexport function sameYmd(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\nexport interface DateRangePreset {\n key: string;\n from: string;\n to: string;\n}\n\n/**\n * The named ranges offered by the data-table date filter (local-calendar, like\n * {@link todayIso} — see the module note). `key` maps to the consumer's\n * `table.preset_*` labels.\n */\nexport function dateRangePresets(): DateRangePreset[] {\n const mondayOffset = (new Date().getDay() + 6) % 7; // monday = 0\n return [\n { key: \"today\", from: todayIso(), to: todayIso() },\n { key: \"yesterday\", from: shiftIso(-1), to: shiftIso(-1) },\n { key: \"this_week\", from: startOfWeekIso(), to: todayIso() },\n { key: \"last_week\", from: shiftIso(-7 - mondayOffset), to: shiftIso(-1 - mondayOffset) },\n { key: \"last_7_days\", from: shiftIso(-6), to: todayIso() },\n { key: \"last_30_days\", from: shiftIso(-29), to: todayIso() },\n { key: \"this_month\", from: startOfMonthIso(0), to: todayIso() },\n { key: \"last_month\", from: startOfMonthIso(-1), to: endOfMonthIso(-1) },\n { key: \"last_3_months\", from: shiftIso(-90), to: todayIso() },\n { key: \"ytd\", from: startOfYearIso(0), to: todayIso() },\n { key: \"last_year\", from: startOfYearIso(-1), to: endOfYearIso(-1) },\n ];\n}\n\n/** The last `n` WHOLE calendar months before the current one, as an inclusive\n * `{ from, to }` — on 15 May, `n = 3` is 1 February … 30 April. Local-calendar, like\n * everything here; `now` is for tests and for a host with its own clock. */\nexport function lastFullMonthsRange(n: number, now: Date = new Date()): { from: string; to: string } {\n const from = new Date(now.getFullYear(), now.getMonth() - n, 1);\n // Day 0 of the current month is the last day of the previous one.\n const to = new Date(now.getFullYear(), now.getMonth(), 0);\n return { from: toLocalIso(from), to: toLocalIso(to) };\n}\n\n/** The last `n` WHOLE calendar years before the current one — in 2026, `n = 2` is\n * 1 January 2024 … 31 December 2025. */\nexport function lastFullYearsRange(n: number, now: Date = new Date()): { from: string; to: string } {\n const year = now.getFullYear();\n return { from: `${year - n}-01-01`, to: `${year - 1}-12-31` };\n}\n\nexport interface CalendarMonthPresetOptions {\n /** How many whole months each month preset spans. Default `[3, 6]`. */\n months?: readonly number[];\n /** How many whole years each year preset spans. Default `[2]`. */\n years?: readonly number[];\n /** The day to count back from. Default: now, on the local calendar. */\n now?: Date;\n}\n\n/**\n * Month-ALIGNED presets, beside the rolling-day ones of {@link dateRangePresets}.\n *\n * kastlan's accounting reports are closed by the month: \"the last three months\" there\n * means February, March and April, never \"the 90 days up to this morning\", which\n * starts mid-month and ends with a day that is still being booked. `last_3_months`\n * above is the rolling kind and stays that way for the data table's filter, so these\n * are separate keys rather than a changed meaning under an old one.\n *\n * Keys are `last_<n>_full_months` and `last_<n>_full_years`; the host labels them (the\n * same arrangement as `dateRangePresets`' keys and its `table.preset_*` strings). With\n * `DateRangePicker`, pass the key as the preset's `id` so the choice keeps its\n * identity while the dates are recomputed on every render.\n */\nexport function calendarMonthPresets(options: CalendarMonthPresetOptions = {}): DateRangePreset[] {\n const { months = [3, 6], years = [2], now = new Date() } = options;\n return [\n ...months.map((n) => ({ key: `last_${n}_full_months`, ...lastFullMonthsRange(n, now) })),\n ...years.map((n) => ({ key: `last_${n}_full_years`, ...lastFullYearsRange(n, now) })),\n ];\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/lib/dates.ts"],"sourcesContent":["/**\n * ISO date helpers, collected from copies that were scattered across the pages.\n *\n * **Every \"what day is it\" helper here reads the LOCAL calendar** — one family, not\n * two. It used to be two: these all built a local Date and then read it back through\n * `toISOString()`, which is UTC, so east of Greenwich they all returned YESTERDAY\n * between local midnight and the UTC rollover. The old module note called that\n * deliberate and warned against changing it. It was not defensible, and dev#471 is\n * what it cost:\n *\n * > *\"Just entered a tx now from scheduled. But it still shows as upcoming. Is there\n * > a problem with timezones? It is currently 1 o clock middle european summer time.\"*\n *\n * There was: at 01:00 CEST `todayIso()` answered with the 14th, the row the scheduler\n * had just booked was dated the 15th, and `isUpcoming` compares the two strings — so a\n * transaction entered a second ago read as one still to come. The same slip moved\n * `startOfMonthIso()` onto the last day of the PREVIOUS month for an hour every first\n * of the month, which is a report range nobody would have questioned.\n *\n * A calendar day only means anything to the human reading it, and every caller here —\n * the register's today, the date-range presets, the budget cursor, a new\n * transaction's default date — is asking that question. There is no caller for whom\n * \"the UTC day\" is the right answer; the server compares plain dates with no zone at\n * all, so sending it the user's local day is also what it wants.\n */\n\n// Imports nothing else: `@eifi1/ui-kit/dates` reaches no package (packaging-contract).\nimport { dateFormatter } from \"./intl-cache\";\n\n/** Zero-pad a number to two digits (\"3\" -> \"03\"). */\nexport function pad(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\n// ---------- Where \"today\" comes from (0.55) ----------\n// keksdose's 0.51 adoption N2: the dev clock (server-kit 0.20's `DevSwitchAnswer.today`,\n// docs/top-bar-harmonization.md §2.7.1 rule 5) moves the day the SERVER takes for today,\n// and the app's own `todayIso()` followed it — but every helper below and the kit's\n// calendars (DatePicker's today, MonthPicker's this month) read the device clock, so a\n// month end or a \"today\" worked out by the kit disagreed with the server's. One settable\n// source now, which all of them read: the device clock until something moves it.\n\n/** The day {@link setToday} moved \"today\" to, or `null`: the device's own. */\nlet movedToday: string | null = null;\nconst todayListeners = new Set<() => void>();\n\n/**\n * Move \"today\" for every helper here, and for the kit's calendars and pickers, to `day`\n * (\"YYYY-MM-DD\") — the dev clock's day, which the app reads at boot and from its dev menu's\n * Time rows (`setToday(answer.today)`) — or back to the device's own with `null`. A string\n * that is not a real day is the device's own too: a dev tool must not stop the app.\n *\n * Only the DATE moves, as on the server: {@link currentDate} keeps the device's time of\n * day, and a timestamp the kit takes (`new Date()` for a sync, a crash report, a relative\n * time) stays on the device's real clock. Not for production code: the app gates the call\n * (`import.meta.env.DEV`), as it gates the menu. The kit's `DevMenu` puts its \"Overrides\n * on\" dot on the flask while a day is set.\n */\nexport function setToday(day: string | null): void {\n const next = day !== null && parseIsoDate(day) !== null ? day : null;\n if (next === movedToday) return;\n movedToday = next;\n for (const listener of [...todayListeners]) listener();\n}\n\n/** The day {@link setToday} moved \"today\" to, or `null` while it is the device's own. */\nexport function todayOverride(): string | null {\n return movedToday;\n}\n\n/** Hear {@link setToday} move the day; returns the unsubscribe. With {@link todayOverride},\n * `useSyncExternalStore`'s pair. */\nexport function subscribeToday(listener: () => void): () => void {\n todayListeners.add(listener);\n return () => {\n todayListeners.delete(listener);\n };\n}\n\n/**\n * Now, on the day the kit takes for today: the device's clock, or — while {@link setToday}\n * has moved it — that day at the device's time of day. A new Date on every call, free to\n * mutate. Every \"what day is it\" here reads it; read it for an app's own.\n */\nexport function currentDate(): Date {\n const now = new Date();\n if (movedToday !== null) {\n const [y, m, d] = movedToday.split(\"-\").map(Number);\n // The date's three parts at once, so no day is passed through that does not exist\n // (31 March set to February first); the time of day is kept.\n now.setFullYear(y, m - 1, d);\n }\n return now;\n}\n\n/** Today as \"YYYY-MM-DD\" on the LOCAL calendar (see the module note), on the day\n * {@link currentDate} takes for today. */\nexport function todayIso(): string {\n return toLocalIso(currentDate());\n}\n\n/** A Date as a local-time \"YYYY-MM-DD\" string. */\nexport function toLocalIso(d: Date): string {\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/** A Date as a local-time \"YYYY-MM\" month key. */\nexport function monthKey(d: Date): string {\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;\n}\n\n// ---------- Date-range presets + calendar helpers ----------\n// Local-calendar like todayIso (see the module note); used by the data-table date\n// filter and its MiniCalendar. Each one mutates a local Date and must therefore READ\n// it back locally — going out through toISOString() is what shifted every one of\n// these by a day near midnight (dev#471). \"Now\" is `currentDate()`, so a day moved by\n// `setToday` moves them all (0.55).\n\n/** Today shifted by `days`, as \"YYYY-MM-DD\". */\nexport function shiftIso(days: number): string {\n const d = currentDate();\n d.setDate(d.getDate() + days);\n return toLocalIso(d);\n}\n\n/** First day (Monday) of the current week, as \"YYYY-MM-DD\". */\nexport function startOfWeekIso(): string {\n const d = currentDate();\n const day = d.getDay();\n const diff = (day + 6) % 7; // monday = 0\n d.setDate(d.getDate() - diff);\n return toLocalIso(d);\n}\n\n/** First day of the month `offsetMonths` from now, as \"YYYY-MM-DD\". */\nexport function startOfMonthIso(offsetMonths = 0): string {\n const d = currentDate();\n d.setDate(1);\n d.setMonth(d.getMonth() + offsetMonths);\n return toLocalIso(d);\n}\n\n/** Last day of the month `offsetMonths` from now, as \"YYYY-MM-DD\". */\nexport function endOfMonthIso(offsetMonths = 0): string {\n const d = currentDate();\n d.setDate(1);\n d.setMonth(d.getMonth() + offsetMonths + 1);\n d.setDate(0);\n return toLocalIso(d);\n}\n\n/** First day of the year `offsetYears` from now, as \"YYYY-MM-DD\". */\nexport function startOfYearIso(offsetYears = 0): string {\n const d = currentDate();\n d.setMonth(0);\n d.setDate(1);\n d.setFullYear(d.getFullYear() + offsetYears);\n return toLocalIso(d);\n}\n\n/** Last day of the year `offsetYears` from now, as \"YYYY-MM-DD\". */\nexport function endOfYearIso(offsetYears = 0): string {\n const d = currentDate();\n d.setMonth(11);\n d.setDate(31);\n d.setFullYear(d.getFullYear() + offsetYears);\n return toLocalIso(d);\n}\n\n/** Parse a \"YYYY-MM-DD\" string to a local-midnight Date, or null if malformed —\n * where \"malformed\" includes a date that does not exist.\n *\n * The falsy check alone was not enough. `new Date(y, m - 1, d)` ROLLS OVER out-of-range\n * components rather than refusing them, so \"2026-13-45\" came back as 2027-02-14 and\n * \"2026-02-30\" as 2026-03-02: a valid-looking Date for a day that never happened. No\n * caller could tell — {@link formatIsoDate} blanks only on `null`, so the invented\n * date formatted and displayed exactly like a real one.\n *\n * The round-trip is the check: build the Date, then read the components back off it.\n * A value that rolled over cannot answer with the numbers it was given. That is the\n * same shape {@link sameYmd} already expresses, applied to a Date and its source. */\nexport function parseIsoDate(s: string): Date | null {\n if (!s) return null;\n const [y, m, d] = s.split(\"-\").map(Number);\n if (!y || !m || !d) return null;\n if (!Number.isInteger(y) || !Number.isInteger(m) || !Number.isInteger(d)) return null;\n const dt = new Date(y, m - 1, d);\n if (Number.isNaN(dt.getTime())) return null;\n if (dt.getFullYear() !== y || dt.getMonth() !== m - 1 || dt.getDate() !== d) return null;\n return dt;\n}\n\n/** The fields that make an option set a DATE for `toLocaleDateString` (ECMA-402's\n * ToDateTimeOptions, required \"date\"): with none of them it adds year, month and day. */\nconst DATE_FIELDS = [\"weekday\", \"year\", \"month\", \"day\", \"dateStyle\"] as const;\nconst NUMERIC_DATE: Intl.DateTimeFormatOptions = { year: \"numeric\", month: \"numeric\", day: \"numeric\" };\n\n/** `options` as `toLocaleDateString` reads them: with a date in them, whatever else they\n * ask for. `{ hour: \"numeric\" }` alone is a date and an hour there, never the hour alone\n * that `Intl.DateTimeFormat` would print for it. */\nfunction asDateOptions(options: Intl.DateTimeFormatOptions | undefined): Intl.DateTimeFormatOptions {\n if (!options) return NUMERIC_DATE;\n return DATE_FIELDS.some((field) => options[field] !== undefined) ? options : { ...options, ...NUMERIC_DATE };\n}\n\n/** Format a \"YYYY-MM-DD\" string for display in `locale` (empty string when\n * unparseable). Defaults to the locale's short numeric date; pass `options` to\n * override (e.g. `{ dateStyle: \"medium\" }`). `locale` may be `undefined` since 0.34 —\n * the runtime's default, as `Intl` reads it and as every other kit formatter takes it\n * (DUP-11: DatePicker re-wrapped this function only to pass none).\n *\n * Through the kit's formatter cache since 0.54 (keksdose audit follow-up, 2026-11-01):\n * `toLocaleDateString` builds a formatter per call, and this one runs for the date\n * pickers' trigger on every render and for an app's date column once per row. The output\n * is `toLocaleDateString`'s: the same options, with the date it adds when they name none\n * (`asDateOptions`). */\nexport function formatIsoDate(iso: string, locale: string | undefined, options?: Intl.DateTimeFormatOptions): string {\n const d = parseIsoDate(iso);\n return d ? dateFormatter(locale, asDateOptions(options)).format(d) : \"\";\n}\n\n/** A \"YYYY-MM-DD\" string shifted by `days` (negative shifts back), or the input\n * unchanged when it isn't a parseable date.\n *\n * Local-time throughout — {@link parseIsoDate} builds a local-midnight Date and\n * {@link toLocalIso} reads local components back, so this never slides a day the way\n * a `toISOString()` round-trip does west of UTC. */\nexport function addDaysIso(iso: string, days: number): string {\n const d = parseIsoDate(iso);\n if (!d) return iso;\n d.setDate(d.getDate() + days);\n return toLocalIso(d);\n}\n\n/**\n * `d` moved by whole months, keeping the day of the month where the target has one.\n * `setMonth` alone ROLLS OVER — 31 January + 1 month is 3 March — which would skip\n * February entirely for anyone paging through the year with PageDown. Returns a new\n * Date; `d` is not touched.\n */\nexport function addMonthsClamped(d: Date, months: number): Date {\n const day = d.getDate();\n const out = new Date(d.getFullYear(), d.getMonth() + months, 1);\n const lastOfTarget = new Date(out.getFullYear(), out.getMonth() + 1, 0).getDate();\n out.setDate(Math.min(day, lastOfTarget));\n return out;\n}\n\n/**\n * The first day of the week in `locale`, as a `Date#getDay` index (0 = Sunday).\n *\n * `Intl.Locale#getWeekInfo()` is the standard spelling; V8 shipped it first as the\n * `weekInfo` accessor, and Firefox has neither yet — hence both reads and a Monday\n * fallback, which is ISO 8601. Its `firstDay` counts 1 = Monday … 7 = Sunday, so `% 7`\n * maps it onto `getDay`. `undefined` asks for the runtime's default locale.\n */\nexport function localeWeekStart(locale: string | undefined): 0 | 1 | 2 | 3 | 4 | 5 | 6 {\n try {\n const tag = locale ?? new Intl.DateTimeFormat().resolvedOptions().locale;\n const loc = new Intl.Locale(tag) as Intl.Locale & {\n getWeekInfo?: () => { firstDay: number };\n weekInfo?: { firstDay: number };\n };\n const firstDay = (loc.getWeekInfo?.() ?? loc.weekInfo)?.firstDay;\n if (typeof firstDay === \"number\") return (firstDay % 7) as 0 | 1 | 2 | 3 | 4 | 5 | 6;\n } catch {\n // A malformed tag throws from `Intl.Locale`; a calendar still has to render.\n }\n return 1;\n}\n\n/** Whether two Dates fall on the same local calendar day. */\nexport function sameYmd(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\nexport interface DateRangePreset {\n key: string;\n from: string;\n to: string;\n}\n\n/**\n * The named ranges offered by the data-table date filter (local-calendar, like\n * {@link todayIso} — see the module note). `key` maps to the consumer's\n * `table.preset_*` labels.\n */\nexport function dateRangePresets(): DateRangePreset[] {\n const mondayOffset = (currentDate().getDay() + 6) % 7; // monday = 0\n return [\n { key: \"today\", from: todayIso(), to: todayIso() },\n { key: \"yesterday\", from: shiftIso(-1), to: shiftIso(-1) },\n { key: \"this_week\", from: startOfWeekIso(), to: todayIso() },\n { key: \"last_week\", from: shiftIso(-7 - mondayOffset), to: shiftIso(-1 - mondayOffset) },\n { key: \"last_7_days\", from: shiftIso(-6), to: todayIso() },\n { key: \"last_30_days\", from: shiftIso(-29), to: todayIso() },\n { key: \"this_month\", from: startOfMonthIso(0), to: todayIso() },\n { key: \"last_month\", from: startOfMonthIso(-1), to: endOfMonthIso(-1) },\n { key: \"last_3_months\", from: shiftIso(-90), to: todayIso() },\n { key: \"ytd\", from: startOfYearIso(0), to: todayIso() },\n { key: \"last_year\", from: startOfYearIso(-1), to: endOfYearIso(-1) },\n ];\n}\n\n/** The last `n` WHOLE calendar months before the current one, as an inclusive\n * `{ from, to }` — on 15 May, `n = 3` is 1 February … 30 April. Local-calendar, like\n * everything here; `now` is for tests and for a host with its own clock (default\n * {@link currentDate}). */\nexport function lastFullMonthsRange(n: number, now: Date = currentDate()): { from: string; to: string } {\n const from = new Date(now.getFullYear(), now.getMonth() - n, 1);\n // Day 0 of the current month is the last day of the previous one.\n const to = new Date(now.getFullYear(), now.getMonth(), 0);\n return { from: toLocalIso(from), to: toLocalIso(to) };\n}\n\n/** The last `n` WHOLE calendar years before the current one — in 2026, `n = 2` is\n * 1 January 2024 … 31 December 2025. */\nexport function lastFullYearsRange(n: number, now: Date = currentDate()): { from: string; to: string } {\n const year = now.getFullYear();\n return { from: `${year - n}-01-01`, to: `${year - 1}-12-31` };\n}\n\nexport interface CalendarMonthPresetOptions {\n /** How many whole months each month preset spans. Default `[3, 6]`. */\n months?: readonly number[];\n /** How many whole years each year preset spans. Default `[2]`. */\n years?: readonly number[];\n /** The day to count back from. Default: now, on the local calendar ({@link currentDate}). */\n now?: Date;\n}\n\n/**\n * Month-ALIGNED presets, beside the rolling-day ones of {@link dateRangePresets}.\n *\n * kastlan's accounting reports are closed by the month: \"the last three months\" there\n * means February, March and April, never \"the 90 days up to this morning\", which\n * starts mid-month and ends with a day that is still being booked. `last_3_months`\n * above is the rolling kind and stays that way for the data table's filter, so these\n * are separate keys rather than a changed meaning under an old one.\n *\n * Keys are `last_<n>_full_months` and `last_<n>_full_years`; the host labels them (the\n * same arrangement as `dateRangePresets`' keys and its `table.preset_*` strings). With\n * `DateRangePicker`, pass the key as the preset's `id` so the choice keeps its\n * identity while the dates are recomputed on every render.\n */\nexport function calendarMonthPresets(options: CalendarMonthPresetOptions = {}): DateRangePreset[] {\n const { months = [3, 6], years = [2], now = currentDate() } = options;\n return [\n ...months.map((n) => ({ key: `last_${n}_full_months`, ...lastFullMonthsRange(n, now) })),\n ...years.map((n) => ({ key: `last_${n}_full_years`, ...lastFullYearsRange(n, now) })),\n ];\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Internal (not exported from the package): the kit's one reading of "this is editable
3
+ * content", for the five places that leave typed text alone — `useHotkey`'s typing check,
4
+ * the tour's Enter and arrows (tour-overlay `ownsKey`), the bulk bar's arrows
5
+ * (`ownsArrows`), the clipboard's minus (clipboard-minus `isEditable`) and the Lightbox's
6
+ * paging keys.
7
+ *
8
+ * 0.54 wrote it for `useHotkey` (keksdose audit follow-up, 2026-11-01); the other three
9
+ * read `isContentEditable` alone, or a `[contenteditable=""], [contenteditable="true"]`
10
+ * selector beside it, and had the gap it closed (0.54 follow-up). The Lightbox's
11
+ * `[contenteditable='true']` selector had it too (0.52 code review 2026-10-31, held to
12
+ * 0.55). Here, so the five cannot drift, and in lib/ because it is DOM reading with no
13
+ * component code: the
14
+ * clipboard handler, which `UiKitProvider` loads on its own, must not bring a component
15
+ * with it.
16
+ */
17
+ /**
18
+ * Whether `el` is editable content — `isContentEditable`, read from the attributes where
19
+ * the engine has no such property. jsdom is one: there a hotkey fired while typing in a
20
+ * contenteditable, the tour stepped on and the bulk bar took the caret's arrows, so no
21
+ * test could show an editor keeping its keys, and an engine without the property would
22
+ * do the same.
23
+ *
24
+ * The nearest element with a `contenteditable` that names a state decides: `""`,
25
+ * `"true"` and `"plaintext-only"` edit, `"false"` does not — a `false` island inside an
26
+ * editor (a mention chip) is not typed in. Any other value is the attribute's
27
+ * invalid-value default, which inherits, so the search goes on to the next one up.
28
+ * Keywords are case-insensitive. None at all: editable only in a `designMode` document.
29
+ *
30
+ * An `Element`, not only an `HTMLElement`: the clipboard asks about whatever node a
31
+ * selection starts in, an SVG icon inside an editor included.
32
+ */
33
+ export declare function isEditableContent(el: Element): boolean;
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ function isEditableContent(el) {
3
+ const native = el.isContentEditable;
4
+ if (native !== void 0) return native;
5
+ for (let at = el.closest("[contenteditable]"); at; at = at.parentElement?.closest("[contenteditable]") ?? null) {
6
+ const state = (at.getAttribute("contenteditable") ?? "").toLowerCase();
7
+ if (state === "" || state === "true" || state === "plaintext-only") return true;
8
+ if (state === "false") return false;
9
+ }
10
+ return el.ownerDocument.designMode === "on";
11
+ }
12
+ export {
13
+ isEditableContent
14
+ };
15
+ //# sourceMappingURL=editable-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/editable-content.ts"],"sourcesContent":["/**\n * Internal (not exported from the package): the kit's one reading of \"this is editable\n * content\", for the five places that leave typed text alone — `useHotkey`'s typing check,\n * the tour's Enter and arrows (tour-overlay `ownsKey`), the bulk bar's arrows\n * (`ownsArrows`), the clipboard's minus (clipboard-minus `isEditable`) and the Lightbox's\n * paging keys.\n *\n * 0.54 wrote it for `useHotkey` (keksdose audit follow-up, 2026-11-01); the other three\n * read `isContentEditable` alone, or a `[contenteditable=\"\"], [contenteditable=\"true\"]`\n * selector beside it, and had the gap it closed (0.54 follow-up). The Lightbox's\n * `[contenteditable='true']` selector had it too (0.52 code review 2026-10-31, held to\n * 0.55). Here, so the five cannot drift, and in lib/ because it is DOM reading with no\n * component code: the\n * clipboard handler, which `UiKitProvider` loads on its own, must not bring a component\n * with it.\n */\n\n/**\n * Whether `el` is editable content — `isContentEditable`, read from the attributes where\n * the engine has no such property. jsdom is one: there a hotkey fired while typing in a\n * contenteditable, the tour stepped on and the bulk bar took the caret's arrows, so no\n * test could show an editor keeping its keys, and an engine without the property would\n * do the same.\n *\n * The nearest element with a `contenteditable` that names a state decides: `\"\"`,\n * `\"true\"` and `\"plaintext-only\"` edit, `\"false\"` does not — a `false` island inside an\n * editor (a mention chip) is not typed in. Any other value is the attribute's\n * invalid-value default, which inherits, so the search goes on to the next one up.\n * Keywords are case-insensitive. None at all: editable only in a `designMode` document.\n *\n * An `Element`, not only an `HTMLElement`: the clipboard asks about whatever node a\n * selection starts in, an SVG icon inside an editor included.\n */\nexport function isEditableContent(el: Element): boolean {\n const native = (el as { isContentEditable?: boolean }).isContentEditable;\n if (native !== undefined) return native;\n for (let at = el.closest(\"[contenteditable]\"); at; at = at.parentElement?.closest(\"[contenteditable]\") ?? null) {\n const state = (at.getAttribute(\"contenteditable\") ?? \"\").toLowerCase();\n if (state === \"\" || state === \"true\" || state === \"plaintext-only\") return true;\n if (state === \"false\") return false;\n }\n return el.ownerDocument.designMode === \"on\";\n}\n"],"mappings":";AAiCO,SAAS,kBAAkB,IAAsB;AACtD,QAAM,SAAU,GAAuC;AACvD,MAAI,WAAW,OAAW,QAAO;AACjC,WAAS,KAAK,GAAG,QAAQ,mBAAmB,GAAG,IAAI,KAAK,GAAG,eAAe,QAAQ,mBAAmB,KAAK,MAAM;AAC9G,UAAM,SAAS,GAAG,aAAa,iBAAiB,KAAK,IAAI,YAAY;AACrE,QAAI,UAAU,MAAM,UAAU,UAAU,UAAU,iBAAkB,QAAO;AAC3E,QAAI,UAAU,QAAS,QAAO;AAAA,EAChC;AACA,SAAO,GAAG,cAAc,eAAe;AACzC;","names":[]}