@cosmicdrift/kumiko-renderer 0.374.0 → 0.377.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 (455) hide show
  1. package/README.md +2 -2
  2. package/dist/app/action-form-support.d.ts +9 -2
  3. package/dist/app/action-form-support.js +24 -2
  4. package/dist/app/client-actions.d.ts +2 -2
  5. package/dist/app/column-renderers.js +1 -1
  6. package/dist/app/config-edit-shim.js +1 -1
  7. package/dist/app/content-preview.js +1 -6
  8. package/dist/app/custom-screens.js +1 -1
  9. package/dist/app/dashboard-body.d.ts +1 -1
  10. package/dist/app/entity-list-expanded-row.d.ts +1 -1
  11. package/dist/app/form-schema.d.ts +1 -1
  12. package/dist/app/form-schema.js +2 -2
  13. package/dist/app/kumiko-screen.d.ts +1 -1
  14. package/dist/app/kumiko-screen.js +7 -6
  15. package/dist/app/list-empty-content.d.ts +1 -1
  16. package/dist/app/list-facets.d.ts +1 -1
  17. package/dist/app/nav.js +2 -2
  18. package/dist/app/query-state-banners.d.ts +1 -1
  19. package/dist/app/reference-link.d.ts +1 -1
  20. package/dist/app/row-actions.d.ts +1 -1
  21. package/dist/app/secret-mint-body.d.ts +1 -1
  22. package/dist/app/secrets-edit-body.d.ts +1 -1
  23. package/dist/app/use-record-actions.d.ts +1 -1
  24. package/dist/app/write-failed-error.d.ts +1 -1
  25. package/dist/components/availability-options-select.d.ts +1 -1
  26. package/dist/components/embedded-list-field.d.ts +1 -1
  27. package/dist/components/embedded-list-field.js +1 -1
  28. package/dist/components/file-list-field.d.ts +1 -1
  29. package/dist/components/grid-cell-for-field.d.ts +1 -1
  30. package/dist/components/multi-select-checkboxes.d.ts +1 -1
  31. package/dist/components/multi-select-checkboxes.js +1 -1
  32. package/dist/components/query-options-select.d.ts +1 -1
  33. package/dist/components/related-list-section.d.ts +1 -1
  34. package/dist/components/render-edit-logic.d.ts +1 -1
  35. package/dist/components/render-edit-types.d.ts +2 -2
  36. package/dist/components/render-edit.d.ts +1 -1
  37. package/dist/components/render-edit.js +4 -2
  38. package/dist/components/render-field.d.ts +1 -1
  39. package/dist/components/render-field.js +4 -1
  40. package/dist/components/render-list.d.ts +1 -1
  41. package/dist/components/render-list.js +20 -4
  42. package/dist/components/write-form-section.d.ts +1 -1
  43. package/dist/components/write-form-section.js +1 -1
  44. package/dist/context/dispatcher-context.d.ts +1 -1
  45. package/dist/format-when.js +1 -1
  46. package/dist/headless/apex/css.d.ts +3 -0
  47. package/dist/headless/apex/css.js +298 -0
  48. package/dist/headless/apex/index.d.ts +236 -0
  49. package/dist/headless/apex/index.js +377 -0
  50. package/dist/headless/apex/lightbox.d.ts +4 -0
  51. package/dist/headless/apex/lightbox.js +81 -0
  52. package/dist/headless/contracts/asset.d.ts +15 -0
  53. package/dist/headless/contracts/asset.js +17 -0
  54. package/dist/headless/contracts/index.d.ts +3 -0
  55. package/dist/headless/contracts/index.js +1 -0
  56. package/dist/headless/contracts/locale.d.ts +7 -0
  57. package/dist/headless/contracts/locale.js +11 -0
  58. package/dist/headless/contracts/primitives.d.ts +93 -0
  59. package/dist/headless/contracts/primitives.js +21 -0
  60. package/dist/headless/dispatcher/index.d.ts +2 -0
  61. package/dist/headless/dispatcher/index.js +1 -0
  62. package/dist/headless/dispatcher/types.d.ts +86 -0
  63. package/dist/headless/dispatcher/types.js +34 -0
  64. package/dist/headless/form/form-controller.d.ts +2 -0
  65. package/dist/headless/form/form-controller.js +409 -0
  66. package/dist/headless/form/index.d.ts +3 -0
  67. package/dist/headless/form/index.js +2 -0
  68. package/dist/headless/form/types.d.ts +76 -0
  69. package/dist/headless/form/types.js +1 -0
  70. package/dist/headless/form/zod-bridge.d.ts +10 -0
  71. package/dist/headless/form/zod-bridge.js +103 -0
  72. package/dist/headless/format/document-values.d.ts +12 -0
  73. package/dist/headless/format/document-values.js +70 -0
  74. package/dist/headless/format/escape.d.ts +5 -0
  75. package/dist/headless/format/escape.js +75 -0
  76. package/dist/headless/format/html-template.d.ts +9 -0
  77. package/dist/headless/format/html-template.js +39 -0
  78. package/dist/headless/format/index.d.ts +12 -0
  79. package/dist/headless/format/index.js +237 -0
  80. package/dist/headless/format/money.d.ts +3 -0
  81. package/dist/headless/format/money.js +39 -0
  82. package/dist/headless/index.d.ts +14 -0
  83. package/dist/headless/index.js +9 -0
  84. package/dist/headless/locale-routing/index.d.ts +41 -0
  85. package/dist/headless/locale-routing/index.js +145 -0
  86. package/dist/headless/nav/index.d.ts +2 -0
  87. package/dist/headless/nav/index.js +1 -0
  88. package/dist/headless/nav/resolve.d.ts +2 -0
  89. package/dist/headless/nav/resolve.js +71 -0
  90. package/dist/headless/nav/types.d.ts +26 -0
  91. package/dist/headless/nav/types.js +1 -0
  92. package/dist/headless/store/create-store.d.ts +2 -0
  93. package/dist/headless/store/create-store.js +43 -0
  94. package/dist/headless/store/equality.d.ts +1 -0
  95. package/dist/headless/store/equality.js +34 -0
  96. package/dist/headless/store/index.d.ts +3 -0
  97. package/dist/headless/store/index.js +2 -0
  98. package/dist/headless/store/types.d.ts +7 -0
  99. package/dist/headless/store/types.js +10 -0
  100. package/dist/headless/view-model/edit.d.ts +18 -0
  101. package/dist/headless/view-model/edit.js +501 -0
  102. package/dist/headless/view-model/embedded-list.d.ts +27 -0
  103. package/dist/headless/view-model/embedded-list.js +63 -0
  104. package/dist/headless/view-model/index.d.ts +7 -0
  105. package/dist/headless/view-model/index.js +3 -0
  106. package/dist/headless/view-model/list-file-display.d.ts +3 -0
  107. package/dist/headless/view-model/list-file-display.js +27 -0
  108. package/dist/headless/view-model/list.d.ts +16 -0
  109. package/dist/headless/view-model/list.js +229 -0
  110. package/dist/headless/view-model/types.d.ts +382 -0
  111. package/dist/headless/view-model/types.js +1 -0
  112. package/dist/hooks/use-ai-text.d.ts +1 -1
  113. package/dist/hooks/use-form.d.ts +1 -1
  114. package/dist/hooks/use-form.js +1 -1
  115. package/dist/hooks/use-mutation.d.ts +1 -1
  116. package/dist/hooks/use-query.d.ts +1 -1
  117. package/dist/hooks/use-store.d.ts +1 -1
  118. package/dist/hooks/use-store.js +7 -7
  119. package/dist/hooks/use-stream-handler.d.ts +1 -1
  120. package/dist/i18n.d.ts +1 -1
  121. package/dist/i18n.js +1 -1
  122. package/dist/index.js +4 -4
  123. package/dist/live/csrf.d.ts +2 -0
  124. package/dist/live/csrf.js +60 -0
  125. package/dist/live/dispatcher-live.d.ts +7 -0
  126. package/dist/live/dispatcher-live.js +286 -0
  127. package/dist/live/envelopes.d.ts +101 -0
  128. package/dist/live/envelopes.js +35 -0
  129. package/dist/live/error-mapping.d.ts +16 -0
  130. package/dist/live/error-mapping.js +82 -0
  131. package/dist/live/index.d.ts +7 -0
  132. package/dist/live/index.js +5 -0
  133. package/dist/live/locale.d.ts +2 -0
  134. package/dist/live/locale.js +25 -0
  135. package/dist/live/sse-stream.d.ts +14 -0
  136. package/dist/live/sse-stream.js +128 -0
  137. package/dist/primitives.d.ts +35 -36
  138. package/dist/primitives.js +1 -1
  139. package/dist/session/session-ended.d.ts +1 -1
  140. package/dist/tokens.d.ts +3 -4
  141. package/dist/tokens.js +4 -5
  142. package/dist/web/app/app-schema-boot.d.ts +19 -0
  143. package/dist/web/app/app-schema-boot.js +124 -0
  144. package/dist/web/app/browser-locale.d.ts +22 -0
  145. package/dist/web/app/browser-locale.js +78 -0
  146. package/dist/web/app/client-plugin.d.ts +106 -0
  147. package/dist/web/app/client-plugin.js +12 -0
  148. package/dist/web/app/create-app.d.ts +104 -0
  149. package/dist/web/app/create-app.js +498 -0
  150. package/dist/web/app/create-public-surface.d.ts +36 -0
  151. package/dist/web/app/create-public-surface.js +40 -0
  152. package/dist/web/app/dashboard-body.d.ts +3 -0
  153. package/dist/web/app/dashboard-body.js +865 -0
  154. package/dist/web/app/dashboard-refresh.d.ts +6 -0
  155. package/dist/web/app/dashboard-refresh.js +15 -0
  156. package/dist/web/app/document-lang-sync.d.ts +4 -0
  157. package/dist/web/app/document-lang-sync.js +27 -0
  158. package/dist/web/app/draft-storage.d.ts +11 -0
  159. package/dist/web/app/draft-storage.js +53 -0
  160. package/dist/web/app/nav-providers-context.d.ts +13 -0
  161. package/dist/web/app/nav-providers-context.js +18 -0
  162. package/dist/web/app/nav.d.ts +40 -0
  163. package/dist/web/app/nav.js +231 -0
  164. package/dist/web/app/plain-content-editor.d.ts +3 -0
  165. package/dist/web/app/plain-content-editor.js +30 -0
  166. package/dist/web/app/relative-time.d.ts +10 -0
  167. package/dist/web/app/relative-time.js +56 -0
  168. package/dist/web/app/resolvers-context.d.ts +12 -0
  169. package/dist/web/app/resolvers-context.js +10 -0
  170. package/dist/web/app/rich-content-editor.d.ts +3 -0
  171. package/dist/web/app/rich-content-editor.js +15 -0
  172. package/dist/web/app/root-error-fallback.d.ts +2 -0
  173. package/dist/web/app/root-error-fallback.js +21 -0
  174. package/dist/web/app/screen-error-fallback.d.ts +6 -0
  175. package/dist/web/app/screen-error-fallback.js +17 -0
  176. package/dist/web/app/tiptap-editor.d.ts +3 -0
  177. package/dist/web/app/tiptap-editor.js +120 -0
  178. package/dist/web/app/toast-action-notifier.d.ts +5 -0
  179. package/dist/web/app/toast-action-notifier.js +17 -0
  180. package/dist/web/components/config-cascade.d.ts +13 -0
  181. package/dist/web/components/config-cascade.js +155 -0
  182. package/dist/web/components/config-source-badge.d.ts +6 -0
  183. package/dist/web/components/config-source-badge.js +55 -0
  184. package/dist/web/default-color-scheme.d.ts +2 -0
  185. package/dist/web/default-color-scheme.js +3 -0
  186. package/dist/web/icons.d.ts +94 -0
  187. package/dist/web/icons.js +100 -0
  188. package/dist/web/index.d.ts +78 -0
  189. package/dist/web/index.js +53 -0
  190. package/dist/web/layout/app-layout.d.ts +21 -0
  191. package/dist/web/layout/app-layout.js +8 -0
  192. package/dist/web/layout/avatar.d.ts +12 -0
  193. package/dist/web/layout/avatar.js +63 -0
  194. package/dist/web/layout/default-app-shell.d.ts +41 -0
  195. package/dist/web/layout/default-app-shell.js +16 -0
  196. package/dist/web/layout/editor-panel.d.ts +10 -0
  197. package/dist/web/layout/editor-panel.js +29 -0
  198. package/dist/web/layout/fill-classes.d.ts +9 -0
  199. package/dist/web/layout/fill-classes.js +15 -0
  200. package/dist/web/layout/filter-app-schema-navs.d.ts +7 -0
  201. package/dist/web/layout/filter-app-schema-navs.js +68 -0
  202. package/dist/web/layout/header-action-group.d.ts +6 -0
  203. package/dist/web/layout/header-action-group.js +8 -0
  204. package/dist/web/layout/header-overflow-menu.d.ts +4 -0
  205. package/dist/web/layout/header-overflow-menu.js +8 -0
  206. package/dist/web/layout/language-menu-items.d.ts +8 -0
  207. package/dist/web/layout/language-menu-items.js +16 -0
  208. package/dist/web/layout/language-switcher.d.ts +25 -0
  209. package/dist/web/layout/language-switcher.js +46 -0
  210. package/dist/web/layout/nav-tree.d.ts +19 -0
  211. package/dist/web/layout/nav-tree.js +536 -0
  212. package/dist/web/layout/page-header-slot.d.ts +25 -0
  213. package/dist/web/layout/page-header-slot.js +50 -0
  214. package/dist/web/layout/profile-menu.d.ts +13 -0
  215. package/dist/web/layout/profile-menu.js +6 -0
  216. package/dist/web/layout/shell-breadcrumb.d.ts +11 -0
  217. package/dist/web/layout/shell-breadcrumb.js +34 -0
  218. package/dist/web/layout/shell-header.d.ts +12 -0
  219. package/dist/web/layout/shell-header.js +159 -0
  220. package/dist/web/layout/sidebar-brand.d.ts +15 -0
  221. package/dist/web/layout/sidebar-brand.js +9 -0
  222. package/dist/web/layout/sidebar-panel.d.ts +41 -0
  223. package/dist/web/layout/sidebar-panel.js +107 -0
  224. package/dist/web/layout/sidebar-user.d.ts +8 -0
  225. package/dist/web/layout/sidebar-user.js +17 -0
  226. package/dist/web/layout/sidebar.d.ts +17 -0
  227. package/dist/web/layout/sidebar.js +11 -0
  228. package/dist/web/layout/target-resolver-stub.d.ts +13 -0
  229. package/dist/web/layout/target-resolver-stub.js +61 -0
  230. package/dist/web/layout/target-url.d.ts +12 -0
  231. package/dist/web/layout/target-url.js +77 -0
  232. package/dist/web/layout/theme-label.d.ts +15 -0
  233. package/dist/web/layout/theme-label.js +35 -0
  234. package/dist/web/layout/theme-menu-item.d.ts +12 -0
  235. package/dist/web/layout/theme-menu-item.js +16 -0
  236. package/dist/web/layout/theme-toggle.d.ts +17 -0
  237. package/dist/web/layout/theme-toggle.js +22 -0
  238. package/dist/web/layout/topbar.d.ts +8 -0
  239. package/dist/web/layout/topbar.js +4 -0
  240. package/dist/web/layout/workspace-shell.d.ts +63 -0
  241. package/dist/web/layout/workspace-shell.js +220 -0
  242. package/dist/web/layout/workspace-switcher.d.ts +15 -0
  243. package/dist/web/layout/workspace-switcher.js +36 -0
  244. package/dist/web/lib/accept-attr.d.ts +1 -0
  245. package/dist/web/lib/accept-attr.js +6 -0
  246. package/dist/web/lib/clamp.d.ts +1 -0
  247. package/dist/web/lib/clamp.js +3 -0
  248. package/dist/web/lib/cn.d.ts +6 -0
  249. package/dist/web/lib/cn.js +9 -0
  250. package/dist/web/lib/download.d.ts +2 -0
  251. package/dist/web/lib/download.js +24 -0
  252. package/dist/web/lib/file-ref-download.d.ts +15 -0
  253. package/dist/web/lib/file-ref-download.js +62 -0
  254. package/dist/web/lib/file-upload-form-data.d.ts +7 -0
  255. package/dist/web/lib/file-upload-form-data.js +11 -0
  256. package/dist/web/lib/resize-image.d.ts +1 -0
  257. package/dist/web/lib/resize-image.js +66 -0
  258. package/dist/web/lib/upload-file-ref.d.ts +9 -0
  259. package/dist/web/lib/upload-file-ref.js +29 -0
  260. package/dist/web/primitives/action-menu.d.ts +34 -0
  261. package/dist/web/primitives/action-menu.js +30 -0
  262. package/dist/web/primitives/calendar-popover.d.ts +13 -0
  263. package/dist/web/primitives/calendar-popover.js +76 -0
  264. package/dist/web/primitives/combobox.d.ts +56 -0
  265. package/dist/web/primitives/combobox.js +127 -0
  266. package/dist/web/primitives/date-field.d.ts +18 -0
  267. package/dist/web/primitives/date-field.js +74 -0
  268. package/dist/web/primitives/date-input.d.ts +1 -0
  269. package/dist/web/primitives/date-input.js +5 -0
  270. package/dist/web/primitives/date-parse.d.ts +11 -0
  271. package/dist/web/primitives/date-parse.js +159 -0
  272. package/dist/web/primitives/dialog.d.ts +3 -0
  273. package/dist/web/primitives/dialog.js +42 -0
  274. package/dist/web/primitives/drawer.d.ts +3 -0
  275. package/dist/web/primitives/drawer.js +17 -0
  276. package/dist/web/primitives/dropdown-menu.d.ts +15 -0
  277. package/dist/web/primitives/dropdown-menu.js +56 -0
  278. package/dist/web/primitives/embedded-list-input.d.ts +9 -0
  279. package/dist/web/primitives/embedded-list-input.js +382 -0
  280. package/dist/web/primitives/file-thumbnail.d.ts +25 -0
  281. package/dist/web/primitives/file-thumbnail.js +90 -0
  282. package/dist/web/primitives/file-upload.d.ts +21 -0
  283. package/dist/web/primitives/file-upload.js +56 -0
  284. package/dist/web/primitives/index.d.ts +34 -0
  285. package/dist/web/primitives/index.js +2021 -0
  286. package/dist/web/primitives/json-view.d.ts +7 -0
  287. package/dist/web/primitives/json-view.js +84 -0
  288. package/dist/web/primitives/layout.d.ts +27 -0
  289. package/dist/web/primitives/layout.js +30 -0
  290. package/dist/web/primitives/lightbox.d.ts +3 -0
  291. package/dist/web/primitives/lightbox.js +36 -0
  292. package/dist/web/primitives/locale-number.d.ts +2 -0
  293. package/dist/web/primitives/locale-number.js +54 -0
  294. package/dist/web/primitives/located-timestamp-input.d.ts +22 -0
  295. package/dist/web/primitives/located-timestamp-input.js +22 -0
  296. package/dist/web/primitives/metric.d.ts +5 -0
  297. package/dist/web/primitives/metric.js +18 -0
  298. package/dist/web/primitives/modal-shell.d.ts +17 -0
  299. package/dist/web/primitives/modal-shell.js +9 -0
  300. package/dist/web/primitives/modal.d.ts +3 -0
  301. package/dist/web/primitives/modal.js +13 -0
  302. package/dist/web/primitives/money-input.d.ts +18 -0
  303. package/dist/web/primitives/money-input.js +122 -0
  304. package/dist/web/primitives/narrow-pinned-footer.d.ts +12 -0
  305. package/dist/web/primitives/narrow-pinned-footer.js +153 -0
  306. package/dist/web/primitives/number-input.d.ts +18 -0
  307. package/dist/web/primitives/number-input.js +53 -0
  308. package/dist/web/primitives/option-groups.d.ts +11 -0
  309. package/dist/web/primitives/option-groups.js +27 -0
  310. package/dist/web/primitives/page-header.d.ts +3 -0
  311. package/dist/web/primitives/page-header.js +52 -0
  312. package/dist/web/primitives/promo-panel.d.ts +3 -0
  313. package/dist/web/primitives/promo-panel.js +11 -0
  314. package/dist/web/primitives/segmented-select.d.ts +21 -0
  315. package/dist/web/primitives/segmented-select.js +62 -0
  316. package/dist/web/primitives/share-actions.d.ts +4 -0
  317. package/dist/web/primitives/share-actions.js +56 -0
  318. package/dist/web/primitives/status-badge.d.ts +3 -0
  319. package/dist/web/primitives/status-badge.js +5 -0
  320. package/dist/web/primitives/sticky-action-bar.d.ts +5 -0
  321. package/dist/web/primitives/sticky-action-bar.js +15 -0
  322. package/dist/web/primitives/tabs.d.ts +3 -0
  323. package/dist/web/primitives/tabs.js +107 -0
  324. package/dist/web/primitives/timestamp-input.d.ts +27 -0
  325. package/dist/web/primitives/timestamp-input.js +87 -0
  326. package/dist/web/primitives/toast.d.ts +19 -0
  327. package/dist/web/primitives/toast.js +63 -0
  328. package/dist/web/primitives/tz-input.d.ts +11 -0
  329. package/dist/web/primitives/tz-input.js +8 -0
  330. package/dist/web/primitives/tz-options.d.ts +4 -0
  331. package/dist/web/primitives/tz-options.js +17 -0
  332. package/dist/web/primitives/use-keyboard-inset.d.ts +1 -0
  333. package/dist/web/primitives/use-keyboard-inset.js +28 -0
  334. package/dist/web/primitives/use-narrow-viewport.d.ts +4 -0
  335. package/dist/web/primitives/use-narrow-viewport.js +46 -0
  336. package/dist/web/react-css-custom-properties.d.ts +6 -0
  337. package/dist/web/react-css-custom-properties.js +1 -0
  338. package/dist/web/sse/live-events.d.ts +15 -0
  339. package/dist/web/sse/live-events.js +148 -0
  340. package/dist/web/theme-plugin.d.ts +41 -0
  341. package/dist/web/theme-plugin.js +123 -0
  342. package/dist/web/tokens.d.ts +24 -0
  343. package/dist/web/tokens.js +203 -0
  344. package/dist/web/ui/avatar.d.ts +11 -0
  345. package/dist/web/ui/avatar.js +25 -0
  346. package/dist/web/ui/badge.d.ts +9 -0
  347. package/dist/web/ui/badge.js +26 -0
  348. package/dist/web/ui/breadcrumb.d.ts +11 -0
  349. package/dist/web/ui/breadcrumb.js +29 -0
  350. package/dist/web/ui/button.d.ts +10 -0
  351. package/dist/web/ui/button.js +37 -0
  352. package/dist/web/ui/checkbox.d.ts +4 -0
  353. package/dist/web/ui/checkbox.js +11 -0
  354. package/dist/web/ui/input.d.ts +3 -0
  355. package/dist/web/ui/input.js +8 -0
  356. package/dist/web/ui/label.d.ts +4 -0
  357. package/dist/web/ui/label.js +10 -0
  358. package/dist/web/ui/resizable.d.ts +7 -0
  359. package/dist/web/ui/resizable.js +16 -0
  360. package/dist/web/ui/separator.d.ts +4 -0
  361. package/dist/web/ui/separator.js +10 -0
  362. package/dist/web/ui/sheet.d.ts +14 -0
  363. package/dist/web/ui/sheet.js +42 -0
  364. package/dist/web/ui/sidebar.d.ts +73 -0
  365. package/dist/web/ui/sidebar.js +211 -0
  366. package/dist/web/ui/skeleton.d.ts +2 -0
  367. package/dist/web/ui/skeleton.js +7 -0
  368. package/dist/web/ui/switch.d.ts +6 -0
  369. package/dist/web/ui/switch.js +10 -0
  370. package/dist/web/ui/table.d.ts +10 -0
  371. package/dist/web/ui/table.js +30 -0
  372. package/dist/web/ui/tabs.d.ts +11 -0
  373. package/dist/web/ui/tabs.js +31 -0
  374. package/dist/web/ui/textarea.d.ts +3 -0
  375. package/dist/web/ui/textarea.js +8 -0
  376. package/dist/web/ui/tooltip.d.ts +7 -0
  377. package/dist/web/ui/tooltip.js +19 -0
  378. package/dist/web/ui/use-mobile.d.ts +1 -0
  379. package/dist/web/ui/use-mobile.js +16 -0
  380. package/dist/web/version/update-checker.d.ts +9 -0
  381. package/dist/web/version/update-checker.js +88 -0
  382. package/dist/web/widgets/ai-text-field.d.ts +36 -0
  383. package/dist/web/widgets/ai-text-field.js +137 -0
  384. package/dist/web/widgets/chart-window.d.ts +58 -0
  385. package/dist/web/widgets/chart-window.js +190 -0
  386. package/dist/web/widgets/charts.d.ts +208 -0
  387. package/dist/web/widgets/charts.js +685 -0
  388. package/dist/web/widgets/collapsible-section.d.ts +10 -0
  389. package/dist/web/widgets/collapsible-section.js +17 -0
  390. package/dist/web/widgets/dashboard-list.d.ts +21 -0
  391. package/dist/web/widgets/dashboard-list.js +45 -0
  392. package/dist/web/widgets/detail-list.d.ts +15 -0
  393. package/dist/web/widgets/detail-list.js +8 -0
  394. package/dist/web/widgets/drawer.d.ts +65 -0
  395. package/dist/web/widgets/drawer.js +142 -0
  396. package/dist/web/widgets/feed-list.d.ts +13 -0
  397. package/dist/web/widgets/feed-list.js +9 -0
  398. package/dist/web/widgets/floating-panel.d.ts +30 -0
  399. package/dist/web/widgets/floating-panel.js +242 -0
  400. package/dist/web/widgets/form-fields.d.ts +93 -0
  401. package/dist/web/widgets/form-fields.js +50 -0
  402. package/dist/web/widgets/index.d.ts +27 -0
  403. package/dist/web/widgets/index.js +30 -0
  404. package/dist/web/widgets/infinity-list.d.ts +46 -0
  405. package/dist/web/widgets/infinity-list.js +211 -0
  406. package/dist/web/widgets/mode-switch.d.ts +21 -0
  407. package/dist/web/widgets/mode-switch.js +25 -0
  408. package/dist/web/widgets/photo-slots.d.ts +29 -0
  409. package/dist/web/widgets/photo-slots.js +52 -0
  410. package/dist/web/widgets/plan-card.d.ts +36 -0
  411. package/dist/web/widgets/plan-card.js +13 -0
  412. package/dist/web/widgets/progress-bar.d.ts +19 -0
  413. package/dist/web/widgets/progress-bar.js +24 -0
  414. package/dist/web/widgets/progress-list.d.ts +19 -0
  415. package/dist/web/widgets/progress-list.js +11 -0
  416. package/dist/web/widgets/public-shell.d.ts +22 -0
  417. package/dist/web/widgets/public-shell.js +15 -0
  418. package/dist/web/widgets/query-table.d.ts +29 -0
  419. package/dist/web/widgets/query-table.js +30 -0
  420. package/dist/web/widgets/result-panel.d.ts +69 -0
  421. package/dist/web/widgets/result-panel.js +30 -0
  422. package/dist/web/widgets/section-card.d.ts +12 -0
  423. package/dist/web/widgets/section-card.js +9 -0
  424. package/dist/web/widgets/sheet-parts.d.ts +9 -0
  425. package/dist/web/widgets/sheet-parts.js +11 -0
  426. package/dist/web/widgets/side-by-side-table.d.ts +31 -0
  427. package/dist/web/widgets/side-by-side-table.js +11 -0
  428. package/dist/web/widgets/stat.d.ts +54 -0
  429. package/dist/web/widgets/stat.js +65 -0
  430. package/dist/web/widgets/states.d.ts +23 -0
  431. package/dist/web/widgets/states.js +20 -0
  432. package/dist/web/widgets/status-badge.d.ts +15 -0
  433. package/dist/web/widgets/status-badge.js +24 -0
  434. package/dist/web/widgets/step-bar.d.ts +35 -0
  435. package/dist/web/widgets/step-bar.js +60 -0
  436. package/dist/web/widgets/upload-zone.d.ts +24 -0
  437. package/dist/web/widgets/upload-zone.js +108 -0
  438. package/dist/web/widgets/use-draft.d.ts +20 -0
  439. package/dist/web/widgets/use-draft.js +21 -0
  440. package/dist/web/widgets/use-pointer-drag.d.ts +21 -0
  441. package/dist/web/widgets/use-pointer-drag.js +53 -0
  442. package/package.json +105 -7
  443. package/src/changes.json +53 -0
  444. package/src/web/fonts/OFL.txt +93 -0
  445. package/src/web/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
  446. package/src/web/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
  447. package/src/web/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
  448. package/src/web/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
  449. package/src/web/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
  450. package/src/web/fonts/ibm-plex-sans-latin-500.woff2 +0 -0
  451. package/src/web/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
  452. package/src/web/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
  453. package/src/web/fonts/ibm-plex-sans-latin-ext-500.woff2 +0 -0
  454. package/src/web/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
  455. package/src/web/styles.css +401 -0
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ import type { JsonViewProps } from "../../index.ts";
3
+ /** Syntax-highlighted, whitespace-preserving JSON display. Bounded height
4
+ * with its own vertical scroll (job logs get long) instead of growing the
5
+ * page; wraps rather than overflowing horizontally. Colors come from the
6
+ * dedicated syntax-* theme tokens (styles.css), readable in light + dark. */
7
+ export declare function DefaultJsonView({ value, indent, testId }: JsonViewProps): ReactNode;
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Recursive pre-pass (not JSX) that turns `value` into a JSON.stringify-safe
3
+ // plain structure: BigInt → string, and a real ancestor-path cycle check
4
+ // (not a global "seen" set — a value referenced twice via two different,
5
+ // non-circular paths must still render twice, not collapse to "[Circular]").
6
+ function toSafeJson(value, ancestors = []) {
7
+ if (typeof value === "bigint")
8
+ return `${value.toString()}n`;
9
+ if (typeof value !== "object" || value === null)
10
+ return value;
11
+ if (ancestors.includes(value))
12
+ return "[Circular]";
13
+ const nextAncestors = [...ancestors, value];
14
+ if (Array.isArray(value))
15
+ return value.map((item) => toSafeJson(item, nextAncestors));
16
+ const out = {};
17
+ for (const [k, v] of Object.entries(value))
18
+ out[k] = toSafeJson(v, nextAncestors);
19
+ return out;
20
+ }
21
+ // A broken audit/log payload must not take the whole screen down with it —
22
+ // double try/catch so even a throwing getter on the input falls back to a
23
+ // plain string instead of propagating.
24
+ function safeStringify(value, indent) {
25
+ try {
26
+ const json = JSON.stringify(toSafeJson(value), null, indent);
27
+ return json ?? String(value);
28
+ }
29
+ catch {
30
+ try {
31
+ return String(value);
32
+ }
33
+ catch {
34
+ return "[unserializable value]";
35
+ }
36
+ }
37
+ }
38
+ const TOKEN_CLASS_NAME = {
39
+ key: "text-syntax-key font-medium",
40
+ string: "text-syntax-string",
41
+ number: "text-syntax-number",
42
+ boolean: "text-syntax-literal",
43
+ null: "text-syntax-literal italic",
44
+ };
45
+ function classifyToken(token) {
46
+ if (token.startsWith('"'))
47
+ return token.endsWith(":") ? "key" : "string";
48
+ if (token === "true" || token === "false")
49
+ return "boolean";
50
+ if (token === "null")
51
+ return "null";
52
+ return "number";
53
+ }
54
+ // The stringify output is canonical JSON — a regex tokenizer over that
55
+ // string is a fraction of the code of a recursive value-tree renderer, and
56
+ // tokens become plain React elements (never dangerouslySetInnerHTML): audit
57
+ // payloads and job logs carry untrusted third-party data, so this must not
58
+ // become an HTML-injection path.
59
+ function tokenizeJson(json) {
60
+ const tokenRe = /("(?:\\u[a-fA-F0-9]{4}|\\.|[^\\"])*"(?:\s*:)?|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\btrue\b|\bfalse\b|\bnull\b)/g;
61
+ const nodes = [];
62
+ let lastIndex = 0;
63
+ let match = tokenRe.exec(json);
64
+ let key = 0;
65
+ while (match !== null) {
66
+ if (match.index > lastIndex)
67
+ nodes.push(json.slice(lastIndex, match.index));
68
+ const token = match[0];
69
+ nodes.push(_jsx("span", { className: TOKEN_CLASS_NAME[classifyToken(token)], children: token }, key++));
70
+ lastIndex = tokenRe.lastIndex;
71
+ match = tokenRe.exec(json);
72
+ }
73
+ if (lastIndex < json.length)
74
+ nodes.push(json.slice(lastIndex));
75
+ return nodes;
76
+ }
77
+ /** Syntax-highlighted, whitespace-preserving JSON display. Bounded height
78
+ * with its own vertical scroll (job logs get long) instead of growing the
79
+ * page; wraps rather than overflowing horizontally. Colors come from the
80
+ * dedicated syntax-* theme tokens (styles.css), readable in light + dark. */
81
+ export function DefaultJsonView({ value, indent = 2, testId }) {
82
+ const json = safeStringify(value, indent);
83
+ return (_jsx("pre", { "data-testid": testId, className: "max-h-80 overflow-y-auto whitespace-pre-wrap break-words rounded bg-muted p-2 font-mono text-xs", children: tokenizeJson(json) }));
84
+ }
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from "react";
2
+ import type { FormWidth } from "../../index.ts";
3
+ export type ScreenWidth = FormWidth;
4
+ export declare const screenWidthClassName: Record<ScreenWidth, string>;
5
+ export declare const screenFormColumnWidthClassName: Record<ScreenWidth, string>;
6
+ export declare const screenPaddingClassName = "px-4 pb-6 pt-5 md:px-10 md:pb-10 md:pt-7";
7
+ declare const STACK_GAP: {
8
+ readonly sm: "gap-2";
9
+ readonly md: "gap-4";
10
+ readonly lg: "gap-6";
11
+ };
12
+ type StackGap = keyof typeof STACK_GAP;
13
+ type StackProps = {
14
+ readonly gap?: StackGap;
15
+ readonly className?: string;
16
+ readonly children?: ReactNode;
17
+ readonly testId?: string;
18
+ };
19
+ export declare function Stack({ gap, className, children, testId }: StackProps): ReactNode;
20
+ type PageSectionProps = {
21
+ readonly className?: string;
22
+ readonly children?: ReactNode;
23
+ readonly testId?: string;
24
+ readonly maxWidth?: ScreenWidth;
25
+ };
26
+ export declare function PageSection({ className, children, testId, maxWidth, }: PageSectionProps): ReactNode;
27
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ // Shared 3xl/4xl/full → max-w-*/mx-auto map. FormScreenShell
4
+ // (primitives/index.tsx) reuses this instead of keeping its own copy —
5
+ // same width tokens must render the same class everywhere (fw#2640).
6
+ export const screenWidthClassName = {
7
+ "3xl": "max-w-3xl mx-auto",
8
+ "4xl": "max-w-4xl mx-auto",
9
+ full: "max-w-full",
10
+ };
11
+ // Screen-form column variant: left-aligned (no mx-auto) so the column lines up
12
+ // with the screen's other left-aligned content, e.g. the header region.
13
+ export const screenFormColumnWidthClassName = {
14
+ "3xl": "max-w-3xl",
15
+ "4xl": "max-w-4xl",
16
+ full: "max-w-full",
17
+ };
18
+ // Shared screen padding for every screen container (PageSection,
19
+ // FormScreenShell, list screens and the pinned-footer screen form). The inset
20
+ // is the one the settings screens use, so a list, a dashboard and a form start
21
+ // at the same edge. The bottom inset keeps the last field or table row off the
22
+ // viewport edge (fw#2640).
23
+ export const screenPaddingClassName = "px-4 pb-6 pt-5 md:px-10 md:pb-10 md:pt-7";
24
+ const STACK_GAP = { sm: "gap-2", md: "gap-4", lg: "gap-6" };
25
+ export function Stack({ gap = "md", className, children, testId }) {
26
+ return (_jsx("div", { "data-testid": testId, className: cn("flex flex-col", STACK_GAP[gap], className), children: children }));
27
+ }
28
+ export function PageSection({ className, children, testId, maxWidth = "full", }) {
29
+ return (_jsx("div", { "data-testid": testId, className: cn(screenPaddingClassName, screenWidthClassName[maxWidth], className), children: children }));
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { LightboxProps } from "../../index.ts";
3
+ export declare function DefaultLightbox(props: LightboxProps): ReactNode;
@@ -0,0 +1,36 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Full-size image overlay — ModalShell without confirm/cancel actions.
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+ import { ChevronLeft, ChevronRight } from "lucide-react";
5
+ import { useTranslation } from "../../index.js";
6
+ import { cn } from "../lib/cn.js";
7
+ import { ModalShell } from "./modal-shell.js";
8
+ const NAV_BUTTON_CLASS = "absolute top-1/2 -translate-y-1/2 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground opacity-80 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring";
9
+ export function DefaultLightbox(props) {
10
+ const { open, onOpenChange, testId, actions, showPosition = true } = props;
11
+ const t = useTranslation();
12
+ const images = "images" in props ? props.images : [{ src: props.src, alt: props.alt }];
13
+ const onIndexChange = "images" in props ? props.onIndexChange : undefined;
14
+ const total = images.length;
15
+ // Clamped here so a stale index after the image set shrinks never renders blank.
16
+ const requestedIndex = "images" in props ? props.index : 0;
17
+ const index = Math.min(Math.max(requestedIndex, 0), total - 1);
18
+ const current = images[index];
19
+ if (current === undefined)
20
+ return null;
21
+ const canNavigate = onIndexChange !== undefined && total > 1;
22
+ const goTo = (target) => {
23
+ onIndexChange?.((target + total) % total);
24
+ };
25
+ const handleKeyDown = (event) => {
26
+ if (event.key === "ArrowRight") {
27
+ event.preventDefault();
28
+ goTo(index + 1);
29
+ }
30
+ else if (event.key === "ArrowLeft") {
31
+ event.preventDefault();
32
+ goTo(index - 1);
33
+ }
34
+ };
35
+ return (_jsxs(ModalShell, { open: open, onOpenChange: onOpenChange, testId: testId, closeLabel: t("kumiko.dialog.close"), noAriaDescription: true, ...(canNavigate && { onContentKeyDown: handleKeyDown }), contentClassName: cn("border-0 bg-transparent p-0 shadow-none", "max-w-[95vw] max-h-[90vh] w-auto"), children: [_jsx(DialogPrimitive.Title, { className: "sr-only", children: current.alt }), _jsx("img", { src: current.src, alt: current.alt, className: "block max-h-[85vh] max-w-[90vw] w-auto rounded-lg border border-border bg-card shadow-lg" }), canNavigate && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": t("kumiko.lightbox.previous"), onClick: () => goTo(index - 1), className: cn(NAV_BUTTON_CLASS, "left-2"), children: _jsx(ChevronLeft, { className: "size-5" }) }), _jsx("button", { type: "button", "aria-label": t("kumiko.lightbox.next"), onClick: () => goTo(index + 1), className: cn(NAV_BUTTON_CLASS, "right-2"), children: _jsx(ChevronRight, { className: "size-5" }) }), showPosition && (_jsx("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-card px-3 py-1 text-xs text-foreground shadow-sm", children: t("kumiko.lightbox.position", { current: index + 1, total }) }))] })), actions !== undefined && (_jsx("div", { "data-testid": testId !== undefined ? `${testId}-actions` : undefined, className: "absolute left-2 top-2 flex items-center gap-2", children: actions }))] }));
36
+ }
@@ -0,0 +1,2 @@
1
+ export declare function resolveSafeLocale(locale: string | undefined): string;
2
+ export declare function parseLocaleNumber(raw: string, locale: string): number;
@@ -0,0 +1,54 @@
1
+ // Shared locale handling for the text-based numeric inputs (MoneyInput,
2
+ // NumberInput).
3
+ const FALLBACK_LOCALE = "en-US";
4
+ // An invalid tag makes every Intl constructor and toLocaleString throw a
5
+ // RangeError; without an ErrorBoundary that would take down the page on
6
+ // focus/blur. Canonicalizing once up front keeps all call sites safe.
7
+ export function resolveSafeLocale(locale) {
8
+ const candidate = locale ?? (typeof navigator !== "undefined" ? navigator.language : undefined) ?? "";
9
+ try {
10
+ return Intl.getCanonicalLocales(candidate)[0] ?? FALLBACK_LOCALE;
11
+ }
12
+ catch {
13
+ return FALLBACK_LOCALE;
14
+ }
15
+ }
16
+ // Separators come from Intl for the locale. A minus is only accepted at the
17
+ // very start: "1-23" is invalid rather than -123, so a typo never turns into
18
+ // a wrong amount. Anything besides digits and separators yields NaN so callers
19
+ // drop the input instead of storing a corrupted number.
20
+ export function parseLocaleNumber(raw, locale) {
21
+ const { groupSep, decimalSep } = localeSeparators(locale);
22
+ const trimmed = raw.trim();
23
+ const negative = trimmed.startsWith("-");
24
+ const body = negative ? trimmed.slice(1) : trimmed;
25
+ const [integerPart = "", fractionPart, ...extra] = body.split(decimalSep);
26
+ if (extra.length > 0)
27
+ return Number.NaN;
28
+ if (!isValidIntegerPart(integerPart, fractionPart, groupSep))
29
+ return Number.NaN;
30
+ if (fractionPart !== undefined && !/^\d*$/.test(fractionPart))
31
+ return Number.NaN;
32
+ const digits = integerPart.replace(/\D/g, "");
33
+ const n = Number(`${digits === "" ? "0" : digits}${fractionPart ? `.${fractionPart}` : ""}`);
34
+ return negative ? -n : n;
35
+ }
36
+ function localeSeparators(locale) {
37
+ const parts = new Intl.NumberFormat(resolveSafeLocale(locale)).formatToParts(1234.5);
38
+ return {
39
+ groupSep: parts.find((p) => p.type === "group")?.value ?? ",",
40
+ decimalSep: parts.find((p) => p.type === "decimal")?.value ?? ".",
41
+ };
42
+ }
43
+ // Group separators only count in valid positions: "1.5" in de is rejected
44
+ // instead of silently becoming 15. An empty integer part (",5") stays valid
45
+ // when a fraction follows.
46
+ function isValidIntegerPart(integerPart, fractionPart, groupSep) {
47
+ if (integerPart === "")
48
+ return fractionPart !== undefined && fractionPart !== "";
49
+ const groupClass = /\s/.test(groupSep) ? "[\\s\\u00a0\\u202f]" : escapeRegExp(groupSep);
50
+ return new RegExp(`^(?:\\d+|\\d{1,3}(?:${groupClass}\\d{3})+)$`).test(integerPart);
51
+ }
52
+ function escapeRegExp(value) {
53
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
54
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ export type LocatedTimestampValue = {
3
+ readonly at: string;
4
+ readonly tz: string;
5
+ readonly utc?: string;
6
+ };
7
+ export type LocatedTimestampInputProps = {
8
+ readonly id: string;
9
+ readonly name: string;
10
+ readonly value: LocatedTimestampValue | "";
11
+ readonly onChange: (v: {
12
+ readonly at: string;
13
+ readonly tz: string;
14
+ } | undefined) => void;
15
+ readonly locale?: string;
16
+ readonly min?: string;
17
+ readonly max?: string;
18
+ readonly disabled?: boolean;
19
+ readonly required?: boolean;
20
+ readonly hasError?: boolean;
21
+ };
22
+ export declare function LocatedTimestampInput({ id, name, value, onChange, locale, min, max, disabled, required, hasError, }: LocatedTimestampInputProps): ReactNode;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "../../index.js";
3
+ import { ComboboxInput } from "./combobox.js";
4
+ import { TimestampInput } from "./timestamp-input.js";
5
+ import { TZ_OPTIONS } from "./tz-options.js";
6
+ export function LocatedTimestampInput({ id, name, value, onChange, locale, min, max, disabled, required, hasError, }) {
7
+ const t = useTranslation();
8
+ const at = value === "" ? "" : value.at;
9
+ const tz = value === "" ? "" : value.tz;
10
+ // `at` ist Minuten-Wallclock (z.iso.datetime({local:true}) akzeptiert das);
11
+ // tz ein IANA-Name. Solange beides leer ist gilt das Feld als leer; sobald
12
+ // eines gesetzt ist, fließt das Paar — die Vollständigkeit erzwingt die
13
+ // Required-/Zod-Validierung beim Submit (so bleibt eine halbe Eingabe sichtbar).
14
+ function emit(nextAt, nextTz) {
15
+ if (nextAt === "" && nextTz === "") {
16
+ onChange(undefined);
17
+ return;
18
+ }
19
+ onChange({ at: nextAt, tz: nextTz });
20
+ }
21
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(TimestampInput, { id: id, name: name, value: at, onChange: (v) => emit(v ?? "", tz), wallClock: true, ...(locale !== undefined && { locale }), ...(min !== undefined && { min }), ...(max !== undefined && { max }), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }) }), _jsx("div", { className: "min-w-[12rem]", children: _jsx(ComboboxInput, { id: `${id}-tz`, name: `${name}-tz`, options: TZ_OPTIONS, value: tz, onChange: (v) => emit(at, v), placeholder: t("kumiko.field.timezone"), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }) }) })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("kumiko.field.locatedTzHint") })] }));
22
+ }
@@ -0,0 +1,5 @@
1
+ import { type ReactNode } from "react";
2
+ import type { MetricBandProps, MetricProps } from "../../index.ts";
3
+ export declare const InsideScreenFormColumnContext: import("react").Context<boolean>;
4
+ export declare function DefaultMetricBand({ lead, subtitle, children, testId, }: MetricBandProps): ReactNode;
5
+ export declare function DefaultMetric({ label, value, testId, onPress }: MetricProps): ReactNode;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ const InsideMetricBandContext = createContext(false);
5
+ // The screen-form column already insets and spaces its children; a band in its
6
+ // header region must not add its own padding on top. DefaultCard resets this
7
+ // to false because a card body is a different inset surface.
8
+ export const InsideScreenFormColumnContext = createContext(false);
9
+ // Column counts follow the board: 2 below md, 3 below lg, 5 from lg.
10
+ export function DefaultMetricBand({ lead, subtitle, children, testId, }) {
11
+ const insideScreenFormColumn = useContext(InsideScreenFormColumnContext);
12
+ return (_jsxs("div", { className: cn("flex shrink-0 flex-col gap-3", !insideScreenFormColumn && "px-6 pb-4 pt-3.5"), "data-testid": testId, children: [lead !== undefined && _jsx("div", { "data-kumiko-layout": "metric-band-lead", children: lead }), subtitle !== undefined && (_jsx("div", { className: "flex flex-wrap items-center gap-x-2 text-[13px] text-foreground-secondary [&_a]:text-primary", children: subtitle })), _jsx(InsideMetricBandContext.Provider, { value: true, children: _jsx("dl", { className: "m-0 grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5", children: children }) })] }));
13
+ }
14
+ export function DefaultMetric({ label, value, testId, onPress }) {
15
+ const insideBand = useContext(InsideMetricBandContext);
16
+ const item = (_jsxs("div", { "data-testid": testId, className: cn("flex min-w-0 flex-col gap-0.5", onPress !== undefined && "cursor-pointer"), ...(onPress !== undefined && { onClick: onPress }), children: [_jsx("dt", { className: "text-xs text-muted-foreground", "data-testid": testId !== undefined ? `${testId}-label` : undefined, children: label }), _jsx("dd", { className: "m-0 text-base font-medium tabular-nums text-foreground", "data-testid": testId !== undefined ? `${testId}-value` : undefined, children: onPress !== undefined ? (_jsx("button", { type: "button", className: "text-left hover:underline", children: value })) : (value) })] }));
17
+ return insideBand ? item : _jsx("dl", { className: "m-0", children: item });
18
+ }
@@ -0,0 +1,17 @@
1
+ import type { KeyboardEvent, ReactNode } from "react";
2
+ export type ModalShellProps = {
3
+ readonly open: boolean;
4
+ readonly onOpenChange: (open: boolean) => void;
5
+ readonly children: ReactNode;
6
+ readonly testId?: string;
7
+ readonly contentClassName?: string;
8
+ /** Radix wants aria-describedby={undefined} when there is no Description. */
9
+ readonly noAriaDescription?: boolean;
10
+ readonly closeLabel?: string;
11
+ readonly showCloseButton?: boolean;
12
+ /** Lets the caller take over Radix's initial-focus target — call
13
+ * `event.preventDefault()` and focus the desired element yourself. */
14
+ readonly onOpenAutoFocus?: (event: Event) => void;
15
+ readonly onContentKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
16
+ };
17
+ export declare function ModalShell({ open, onOpenChange, children, testId, contentClassName, noAriaDescription, closeLabel, showCloseButton, onOpenAutoFocus, onContentKeyDown, }: ModalShellProps): ReactNode;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Shared Radix dialog chrome for confirm Dialog + image Lightbox.
3
+ // Not exported from the package — only an internal building block.
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { X } from "lucide-react";
6
+ import { cn } from "../lib/cn.js";
7
+ export function ModalShell({ open, onOpenChange, children, testId, contentClassName, noAriaDescription, closeLabel, showCloseButton = true, onOpenAutoFocus, onContentKeyDown, }) {
8
+ return (_jsx(DialogPrimitive.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" }), _jsxs(DialogPrimitive.Content, { "data-testid": testId, ...(noAriaDescription && { "aria-describedby": undefined }), ...(onOpenAutoFocus !== undefined && { onOpenAutoFocus }), ...(onContentKeyDown !== undefined && { onKeyDown: onContentKeyDown }), className: cn("fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", contentClassName), children: [children, showCloseButton && closeLabel !== undefined && (_jsx(DialogPrimitive.Close, { asChild: true, children: _jsx("button", { type: "button", "aria-label": closeLabel, className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: _jsx(X, { className: "size-4" }) }) }))] })] }) }));
9
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ModalProps } from "../../index.ts";
3
+ export declare function DefaultModal({ open, onOpenChange, title, children, testId, }: ModalProps): ReactNode;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Bare content shell for hosting self-contained widgets (own submit/cancel
3
+ // buttons) in a modal overlay — same Radix chrome as DefaultDialog, no
4
+ // footer buttons of its own.
5
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
6
+ import { useTranslation } from "../../index.js";
7
+ import { cn } from "../lib/cn.js";
8
+ import { BareFormProvider } from "./index.js";
9
+ import { ModalShell } from "./modal-shell.js";
10
+ export function DefaultModal({ open, onOpenChange, title, children, testId, }) {
11
+ const t = useTranslation();
12
+ return (_jsxs(ModalShell, { open: open, onOpenChange: onOpenChange, testId: testId, closeLabel: t("kumiko.dialog.close"), noAriaDescription: true, contentClassName: cn("grid w-full max-w-lg gap-4 border bg-card p-6 shadow-lg rounded-lg"), children: [_jsx(DialogPrimitive.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: title }), _jsx("div", { onSubmit: (e) => e.stopPropagation(), children: _jsx(BareFormProvider, { children: children }) })] }));
13
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ import { currencyDecimals } from "../../headless/index.ts";
3
+ import { parseLocaleNumber } from "./locale-number.ts";
4
+ export { currencyDecimals, parseLocaleNumber };
5
+ export type MoneyInputProps = {
6
+ readonly id: string;
7
+ readonly name: string;
8
+ readonly value: number | "";
9
+ readonly onChange: (v: number | undefined) => void;
10
+ readonly currency: string;
11
+ readonly locale?: string;
12
+ readonly disabled?: boolean;
13
+ readonly required?: boolean;
14
+ readonly hasError?: boolean;
15
+ readonly ariaLabel?: string;
16
+ };
17
+ export declare function MoneyInput({ id, name, value, onChange, currency, locale, disabled, required, hasError, ariaLabel, }: MoneyInputProps): ReactNode;
18
+ export declare function formatMoney(amountMinor: number, currency: string, locale?: string, fractionDigits?: number): string;
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // MoneyInput — type=text mit focus-aware Locale-Format. Canonical-Wert
3
+ // bleiben Minor-Units (Cents) wie auf der Wire; während Focus zeigt das
4
+ // Input einen rohen, editierbaren Decimal-String. Bei Blur formatiert
5
+ // Intl.NumberFormat mit `style: "currency"` — das liefert Currency-
6
+ // Symbol (€/$/¥) UND Tausender-Trenner UND korrekte Decimals in einem
7
+ // Aufruf.
8
+ //
9
+ // Warum nicht type=number: number-Inputs lehnen formatierte Strings
10
+ // ("1.234,56 €") ab — kein Browser akzeptiert Locale-Decimals (Komma)
11
+ // in number-Inputs. inputMode="decimal" gibt mobiles Numpad-Keyboard
12
+ // trotzdem.
13
+ import { useLayoutEffect, useMemo, useRef, useState } from "react";
14
+ import { currencyDecimals, formatMoney as formatMoneyForLocale } from "../../headless/index.js";
15
+ import { cn } from "../lib/cn.js";
16
+ import { parseLocaleNumber, resolveSafeLocale } from "./locale-number.js";
17
+ // Re-exported for backward compat — callers used to import this from here
18
+ // before it moved to headless (shared with RenderField, kumiko-framework#1923).
19
+ export { currencyDecimals, parseLocaleNumber };
20
+ // The border and focus ring live on the wrapper so the currency symbol can be
21
+ // a flex sibling of the input: a symbol of any width ("€", "CHF", "R$") takes
22
+ // its own space instead of overlapping the digits.
23
+ const wrapperClass = "flex h-9 w-full items-center rounded-md border border-input bg-transparent text-sm shadow-sm " +
24
+ "transition-colors focus-within:ring-1 focus-within:ring-ring";
25
+ const inputClass = "h-full min-w-0 flex-1 bg-transparent px-3 placeholder:text-muted-foreground " +
26
+ "focus-visible:outline-none disabled:cursor-not-allowed " +
27
+ // Numerische Inputs rechtsbündig — wie native type=number — damit
28
+ // Beträge unter Listen-Spalten an den Tausender-Stellen alignen.
29
+ "text-right tabular-nums";
30
+ export function MoneyInput({ id, name, value, onChange, currency, locale, disabled, required, hasError, ariaLabel, }) {
31
+ const decimals = currencyDecimals(currency);
32
+ const factor = 10 ** decimals;
33
+ const resolvedLocale = resolveSafeLocale(locale);
34
+ const [focused, setFocused] = useState(false);
35
+ // Raw-Edit-Buffer während Focus. Sonst würde jeder Tipp-Step durch
36
+ // Math.round → format-Roundtrip jagen und der Cursor würde springen.
37
+ const [draft, setDraft] = useState("");
38
+ const inputRef = useRef(null);
39
+ const major = value === "" ? null : value / factor;
40
+ const { symbol, symbolPosition, formatNumber } = useMemo(() => moneyFormatParts(currency, resolvedLocale), [currency, resolvedLocale]);
41
+ const formatted = value === "" ? "" : formatNumber(value / factor);
42
+ // Edit-Mode: Decimal-String ohne Tausender-Trenner.
43
+ const toEditable = (m) => m === null
44
+ ? ""
45
+ : m.toLocaleString(resolvedLocale, {
46
+ minimumFractionDigits: decimals,
47
+ maximumFractionDigits: decimals,
48
+ useGrouping: false,
49
+ });
50
+ const handleFocus = () => {
51
+ setDraft(toEditable(major));
52
+ setFocused(true);
53
+ };
54
+ // Select-all-on-focus: deliberate, not just a Playwright accommodation.
55
+ // Focus always swaps the displayed value from the formatted string
56
+ // ("1.234,56 €") to the raw editable one ("1234,56"). Measured in a real
57
+ // browser: on this kind of value swap, the browser collapses the cursor
58
+ // to the *end* of the new value rather than preserving position — so
59
+ // without an explicit re-select, typing right after focus appends
60
+ // instead of replacing. That's what silently corrupted values set via
61
+ // Playwright's `.fill()` (framework#1856). It also matches standard
62
+ // money-input UX (immediate overtype on click), and sidesteps mapping a
63
+ // click position from the formatted view to the editable one, which has
64
+ // no well-defined equivalent once separators and the currency symbol
65
+ // are stripped.
66
+ useLayoutEffect(() => {
67
+ if (focused)
68
+ inputRef.current?.select();
69
+ }, [focused]);
70
+ const handleBlur = () => {
71
+ setFocused(false);
72
+ if (draft.trim() === "") {
73
+ onChange(undefined);
74
+ return;
75
+ }
76
+ const parsed = parseLocaleNumber(draft, resolvedLocale);
77
+ if (Number.isNaN(parsed))
78
+ return;
79
+ onChange(Math.round(parsed * factor));
80
+ };
81
+ const symbolEl = symbol !== "" ? (_jsx("span", { id: `${id}-currency`, "data-testid": `${id}-currency`, className: cn("shrink-0 select-none text-muted-foreground", symbolPosition === "prefix" ? "pl-3" : "pr-3"), children: symbol })) : null;
82
+ return (_jsxs("div", { className: cn(wrapperClass, hasError === true && "border-destructive focus-within:ring-destructive", disabled === true && "cursor-not-allowed opacity-50"), children: [symbolPosition === "prefix" && symbolEl, _jsx("input", { ref: inputRef, id: id, name: name, type: "text", inputMode: "decimal", autoComplete: "off", disabled: disabled, "aria-required": required, "aria-invalid": hasError === true ? true : undefined, "aria-label": ariaLabel, "aria-describedby": symbol !== "" ? `${id}-currency` : undefined, value: focused ? draft : formatted, onFocus: handleFocus, onBlur: handleBlur, onChange: (e) => setDraft(e.target.value), className: cn(inputClass, symbolPosition === "prefix" ? "pl-2" : "pr-2") }), symbolPosition === "suffix" && symbolEl] }));
83
+ }
84
+ // Shared with defaultCellRender (index.tsx) so both formatting paths stay
85
+ // identical. Resolves the browser locale here; headless stays navigator-free.
86
+ export function formatMoney(amountMinor, currency, locale, fractionDigits) {
87
+ return formatMoneyForLocale(amountMinor, currency, resolveSafeLocale(locale), fractionDigits);
88
+ }
89
+ // The currency symbol is rendered as a separate adornment, so the field
90
+ // value shows only the grouped number. Position follows the locale's pattern.
91
+ function moneyFormatParts(currency, locale) {
92
+ if (!/^[A-Za-z]{3}$/.test(currency)) {
93
+ return { symbol: "", symbolPosition: "suffix", formatNumber: String };
94
+ }
95
+ const decimals = currencyDecimals(currency);
96
+ try {
97
+ const formatter = new Intl.NumberFormat(locale, {
98
+ style: "currency",
99
+ currency,
100
+ minimumFractionDigits: decimals,
101
+ maximumFractionDigits: decimals,
102
+ });
103
+ const sample = formatter.formatToParts(1);
104
+ const symbolIndex = sample.findIndex((p) => p.type === "currency");
105
+ const integerIndex = sample.findIndex((p) => p.type === "integer");
106
+ return {
107
+ symbol: sample[symbolIndex]?.value ?? currency,
108
+ symbolPosition: symbolIndex !== -1 && symbolIndex < integerIndex ? "prefix" : "suffix",
109
+ formatNumber: (major) => formatter
110
+ .formatToParts(major)
111
+ .filter((p) => p.type !== "currency")
112
+ .map((p) => p.value)
113
+ .join("")
114
+ .trim(),
115
+ };
116
+ }
117
+ catch {
118
+ // An invalid locale tag makes Intl throw a RangeError; without an
119
+ // ErrorBoundary that would take down the page over a display adornment.
120
+ return { symbol: "", symbolPosition: "suffix", formatNumber: String };
121
+ }
122
+ }
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from "react";
2
+ /** Pinned footer below `sm`: always one fixed-height row, Back as icon on the left,
3
+ * everything else in a "…" popover (hidden while none of it is enabled), the primary
4
+ * action filling the rest. */
5
+ export declare function NarrowPinnedFooter({ nodes, overflowLabel, unsavedCount, unsavedStatus, testId, keyboardInset, }: {
6
+ readonly nodes: readonly ReactNode[];
7
+ readonly overflowLabel: string;
8
+ readonly unsavedCount: number | undefined;
9
+ readonly unsavedStatus: string | undefined;
10
+ readonly testId: string | undefined;
11
+ readonly keyboardInset: number;
12
+ }): ReactNode;