@cosmicdrift/kumiko-renderer 0.373.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 (466) hide show
  1. package/README.md +2 -2
  2. package/dist/app/action-form-support.d.ts +58 -0
  3. package/dist/app/action-form-support.js +96 -0
  4. package/dist/app/client-actions.d.ts +46 -0
  5. package/dist/app/client-actions.js +60 -0
  6. package/dist/app/column-renderers.js +1 -1
  7. package/dist/app/config-edit-shim.js +1 -1
  8. package/dist/app/content-preview.js +1 -6
  9. package/dist/app/custom-screens.js +1 -1
  10. package/dist/app/dashboard-body.d.ts +1 -1
  11. package/dist/app/entity-list-expanded-row.d.ts +1 -1
  12. package/dist/app/entity-list-expanded-row.js +4 -0
  13. package/dist/app/form-schema.d.ts +1 -1
  14. package/dist/app/form-schema.js +2 -2
  15. package/dist/app/header-actions-bar.d.ts +19 -0
  16. package/dist/app/header-actions-bar.js +30 -0
  17. package/dist/app/kumiko-screen.d.ts +1 -1
  18. package/dist/app/kumiko-screen.js +134 -66
  19. package/dist/app/list-empty-content.d.ts +2 -2
  20. package/dist/app/list-empty-content.js +3 -2
  21. package/dist/app/list-facets.d.ts +1 -1
  22. package/dist/app/nav.js +2 -2
  23. package/dist/app/query-state-banners.d.ts +1 -1
  24. package/dist/app/reference-link.d.ts +1 -1
  25. package/dist/app/row-actions.d.ts +10 -2
  26. package/dist/app/row-actions.js +85 -6
  27. package/dist/app/secret-mint-body.d.ts +1 -1
  28. package/dist/app/secrets-edit-body.d.ts +1 -1
  29. package/dist/app/use-record-actions.d.ts +12 -0
  30. package/dist/app/use-record-actions.js +30 -0
  31. package/dist/app/write-failed-error.d.ts +1 -1
  32. package/dist/components/availability-options-select.d.ts +1 -1
  33. package/dist/components/embedded-list-field.d.ts +1 -1
  34. package/dist/components/embedded-list-field.js +1 -1
  35. package/dist/components/file-list-field.d.ts +1 -1
  36. package/dist/components/grid-cell-for-field.d.ts +4 -2
  37. package/dist/components/grid-cell-for-field.js +2 -2
  38. package/dist/components/multi-select-checkboxes.d.ts +1 -1
  39. package/dist/components/multi-select-checkboxes.js +1 -1
  40. package/dist/components/query-options-select.d.ts +1 -1
  41. package/dist/components/related-list-section.d.ts +1 -1
  42. package/dist/components/related-list-section.js +16 -3
  43. package/dist/components/render-edit-logic.d.ts +1 -1
  44. package/dist/components/render-edit-types.d.ts +2 -2
  45. package/dist/components/render-edit.d.ts +1 -1
  46. package/dist/components/render-edit.js +5 -3
  47. package/dist/components/render-field.d.ts +5 -2
  48. package/dist/components/render-field.js +103 -9
  49. package/dist/components/render-list.d.ts +1 -1
  50. package/dist/components/render-list.js +20 -4
  51. package/dist/components/write-form-section.d.ts +1 -1
  52. package/dist/components/write-form-section.js +1 -1
  53. package/dist/context/dispatcher-context.d.ts +1 -1
  54. package/dist/format-when.js +1 -1
  55. package/dist/headless/apex/css.d.ts +3 -0
  56. package/dist/headless/apex/css.js +298 -0
  57. package/dist/headless/apex/index.d.ts +236 -0
  58. package/dist/headless/apex/index.js +377 -0
  59. package/dist/headless/apex/lightbox.d.ts +4 -0
  60. package/dist/headless/apex/lightbox.js +81 -0
  61. package/dist/headless/contracts/asset.d.ts +15 -0
  62. package/dist/headless/contracts/asset.js +17 -0
  63. package/dist/headless/contracts/index.d.ts +3 -0
  64. package/dist/headless/contracts/index.js +1 -0
  65. package/dist/headless/contracts/locale.d.ts +7 -0
  66. package/dist/headless/contracts/locale.js +11 -0
  67. package/dist/headless/contracts/primitives.d.ts +93 -0
  68. package/dist/headless/contracts/primitives.js +21 -0
  69. package/dist/headless/dispatcher/index.d.ts +2 -0
  70. package/dist/headless/dispatcher/index.js +1 -0
  71. package/dist/headless/dispatcher/types.d.ts +86 -0
  72. package/dist/headless/dispatcher/types.js +34 -0
  73. package/dist/headless/form/form-controller.d.ts +2 -0
  74. package/dist/headless/form/form-controller.js +409 -0
  75. package/dist/headless/form/index.d.ts +3 -0
  76. package/dist/headless/form/index.js +2 -0
  77. package/dist/headless/form/types.d.ts +76 -0
  78. package/dist/headless/form/types.js +1 -0
  79. package/dist/headless/form/zod-bridge.d.ts +10 -0
  80. package/dist/headless/form/zod-bridge.js +103 -0
  81. package/dist/headless/format/document-values.d.ts +12 -0
  82. package/dist/headless/format/document-values.js +70 -0
  83. package/dist/headless/format/escape.d.ts +5 -0
  84. package/dist/headless/format/escape.js +75 -0
  85. package/dist/headless/format/html-template.d.ts +9 -0
  86. package/dist/headless/format/html-template.js +39 -0
  87. package/dist/headless/format/index.d.ts +12 -0
  88. package/dist/headless/format/index.js +237 -0
  89. package/dist/headless/format/money.d.ts +3 -0
  90. package/dist/headless/format/money.js +39 -0
  91. package/dist/headless/index.d.ts +14 -0
  92. package/dist/headless/index.js +9 -0
  93. package/dist/headless/locale-routing/index.d.ts +41 -0
  94. package/dist/headless/locale-routing/index.js +145 -0
  95. package/dist/headless/nav/index.d.ts +2 -0
  96. package/dist/headless/nav/index.js +1 -0
  97. package/dist/headless/nav/resolve.d.ts +2 -0
  98. package/dist/headless/nav/resolve.js +71 -0
  99. package/dist/headless/nav/types.d.ts +26 -0
  100. package/dist/headless/nav/types.js +1 -0
  101. package/dist/headless/store/create-store.d.ts +2 -0
  102. package/dist/headless/store/create-store.js +43 -0
  103. package/dist/headless/store/equality.d.ts +1 -0
  104. package/dist/headless/store/equality.js +34 -0
  105. package/dist/headless/store/index.d.ts +3 -0
  106. package/dist/headless/store/index.js +2 -0
  107. package/dist/headless/store/types.d.ts +7 -0
  108. package/dist/headless/store/types.js +10 -0
  109. package/dist/headless/view-model/edit.d.ts +18 -0
  110. package/dist/headless/view-model/edit.js +501 -0
  111. package/dist/headless/view-model/embedded-list.d.ts +27 -0
  112. package/dist/headless/view-model/embedded-list.js +63 -0
  113. package/dist/headless/view-model/index.d.ts +7 -0
  114. package/dist/headless/view-model/index.js +3 -0
  115. package/dist/headless/view-model/list-file-display.d.ts +3 -0
  116. package/dist/headless/view-model/list-file-display.js +27 -0
  117. package/dist/headless/view-model/list.d.ts +16 -0
  118. package/dist/headless/view-model/list.js +229 -0
  119. package/dist/headless/view-model/types.d.ts +382 -0
  120. package/dist/headless/view-model/types.js +1 -0
  121. package/dist/hooks/use-ai-text.d.ts +1 -1
  122. package/dist/hooks/use-form.d.ts +1 -1
  123. package/dist/hooks/use-form.js +1 -1
  124. package/dist/hooks/use-mutation.d.ts +1 -1
  125. package/dist/hooks/use-query.d.ts +1 -1
  126. package/dist/hooks/use-store.d.ts +1 -1
  127. package/dist/hooks/use-store.js +7 -7
  128. package/dist/hooks/use-stream-handler.d.ts +1 -1
  129. package/dist/i18n-defaults.js +7 -0
  130. package/dist/i18n.d.ts +1 -1
  131. package/dist/i18n.js +1 -1
  132. package/dist/index.d.ts +5 -1
  133. package/dist/index.js +7 -4
  134. package/dist/live/csrf.d.ts +2 -0
  135. package/dist/live/csrf.js +60 -0
  136. package/dist/live/dispatcher-live.d.ts +7 -0
  137. package/dist/live/dispatcher-live.js +286 -0
  138. package/dist/live/envelopes.d.ts +101 -0
  139. package/dist/live/envelopes.js +35 -0
  140. package/dist/live/error-mapping.d.ts +16 -0
  141. package/dist/live/error-mapping.js +82 -0
  142. package/dist/live/index.d.ts +7 -0
  143. package/dist/live/index.js +5 -0
  144. package/dist/live/locale.d.ts +2 -0
  145. package/dist/live/locale.js +25 -0
  146. package/dist/live/sse-stream.d.ts +14 -0
  147. package/dist/live/sse-stream.js +128 -0
  148. package/dist/primitives.d.ts +53 -36
  149. package/dist/primitives.js +1 -1
  150. package/dist/session/session-ended.d.ts +1 -1
  151. package/dist/tokens.d.ts +3 -4
  152. package/dist/tokens.js +4 -5
  153. package/dist/web/app/app-schema-boot.d.ts +19 -0
  154. package/dist/web/app/app-schema-boot.js +124 -0
  155. package/dist/web/app/browser-locale.d.ts +22 -0
  156. package/dist/web/app/browser-locale.js +78 -0
  157. package/dist/web/app/client-plugin.d.ts +106 -0
  158. package/dist/web/app/client-plugin.js +12 -0
  159. package/dist/web/app/create-app.d.ts +104 -0
  160. package/dist/web/app/create-app.js +498 -0
  161. package/dist/web/app/create-public-surface.d.ts +36 -0
  162. package/dist/web/app/create-public-surface.js +40 -0
  163. package/dist/web/app/dashboard-body.d.ts +3 -0
  164. package/dist/web/app/dashboard-body.js +865 -0
  165. package/dist/web/app/dashboard-refresh.d.ts +6 -0
  166. package/dist/web/app/dashboard-refresh.js +15 -0
  167. package/dist/web/app/document-lang-sync.d.ts +4 -0
  168. package/dist/web/app/document-lang-sync.js +27 -0
  169. package/dist/web/app/draft-storage.d.ts +11 -0
  170. package/dist/web/app/draft-storage.js +53 -0
  171. package/dist/web/app/nav-providers-context.d.ts +13 -0
  172. package/dist/web/app/nav-providers-context.js +18 -0
  173. package/dist/web/app/nav.d.ts +40 -0
  174. package/dist/web/app/nav.js +231 -0
  175. package/dist/web/app/plain-content-editor.d.ts +3 -0
  176. package/dist/web/app/plain-content-editor.js +30 -0
  177. package/dist/web/app/relative-time.d.ts +10 -0
  178. package/dist/web/app/relative-time.js +56 -0
  179. package/dist/web/app/resolvers-context.d.ts +12 -0
  180. package/dist/web/app/resolvers-context.js +10 -0
  181. package/dist/web/app/rich-content-editor.d.ts +3 -0
  182. package/dist/web/app/rich-content-editor.js +15 -0
  183. package/dist/web/app/root-error-fallback.d.ts +2 -0
  184. package/dist/web/app/root-error-fallback.js +21 -0
  185. package/dist/web/app/screen-error-fallback.d.ts +6 -0
  186. package/dist/web/app/screen-error-fallback.js +17 -0
  187. package/dist/web/app/tiptap-editor.d.ts +3 -0
  188. package/dist/web/app/tiptap-editor.js +120 -0
  189. package/dist/web/app/toast-action-notifier.d.ts +5 -0
  190. package/dist/web/app/toast-action-notifier.js +17 -0
  191. package/dist/web/components/config-cascade.d.ts +13 -0
  192. package/dist/web/components/config-cascade.js +155 -0
  193. package/dist/web/components/config-source-badge.d.ts +6 -0
  194. package/dist/web/components/config-source-badge.js +55 -0
  195. package/dist/web/default-color-scheme.d.ts +2 -0
  196. package/dist/web/default-color-scheme.js +3 -0
  197. package/dist/web/icons.d.ts +94 -0
  198. package/dist/web/icons.js +100 -0
  199. package/dist/web/index.d.ts +78 -0
  200. package/dist/web/index.js +53 -0
  201. package/dist/web/layout/app-layout.d.ts +21 -0
  202. package/dist/web/layout/app-layout.js +8 -0
  203. package/dist/web/layout/avatar.d.ts +12 -0
  204. package/dist/web/layout/avatar.js +63 -0
  205. package/dist/web/layout/default-app-shell.d.ts +41 -0
  206. package/dist/web/layout/default-app-shell.js +16 -0
  207. package/dist/web/layout/editor-panel.d.ts +10 -0
  208. package/dist/web/layout/editor-panel.js +29 -0
  209. package/dist/web/layout/fill-classes.d.ts +9 -0
  210. package/dist/web/layout/fill-classes.js +15 -0
  211. package/dist/web/layout/filter-app-schema-navs.d.ts +7 -0
  212. package/dist/web/layout/filter-app-schema-navs.js +68 -0
  213. package/dist/web/layout/header-action-group.d.ts +6 -0
  214. package/dist/web/layout/header-action-group.js +8 -0
  215. package/dist/web/layout/header-overflow-menu.d.ts +4 -0
  216. package/dist/web/layout/header-overflow-menu.js +8 -0
  217. package/dist/web/layout/language-menu-items.d.ts +8 -0
  218. package/dist/web/layout/language-menu-items.js +16 -0
  219. package/dist/web/layout/language-switcher.d.ts +25 -0
  220. package/dist/web/layout/language-switcher.js +46 -0
  221. package/dist/web/layout/nav-tree.d.ts +19 -0
  222. package/dist/web/layout/nav-tree.js +536 -0
  223. package/dist/web/layout/page-header-slot.d.ts +25 -0
  224. package/dist/web/layout/page-header-slot.js +50 -0
  225. package/dist/web/layout/profile-menu.d.ts +13 -0
  226. package/dist/web/layout/profile-menu.js +6 -0
  227. package/dist/web/layout/shell-breadcrumb.d.ts +11 -0
  228. package/dist/web/layout/shell-breadcrumb.js +34 -0
  229. package/dist/web/layout/shell-header.d.ts +12 -0
  230. package/dist/web/layout/shell-header.js +159 -0
  231. package/dist/web/layout/sidebar-brand.d.ts +15 -0
  232. package/dist/web/layout/sidebar-brand.js +9 -0
  233. package/dist/web/layout/sidebar-panel.d.ts +41 -0
  234. package/dist/web/layout/sidebar-panel.js +107 -0
  235. package/dist/web/layout/sidebar-user.d.ts +8 -0
  236. package/dist/web/layout/sidebar-user.js +17 -0
  237. package/dist/web/layout/sidebar.d.ts +17 -0
  238. package/dist/web/layout/sidebar.js +11 -0
  239. package/dist/web/layout/target-resolver-stub.d.ts +13 -0
  240. package/dist/web/layout/target-resolver-stub.js +61 -0
  241. package/dist/web/layout/target-url.d.ts +12 -0
  242. package/dist/web/layout/target-url.js +77 -0
  243. package/dist/web/layout/theme-label.d.ts +15 -0
  244. package/dist/web/layout/theme-label.js +35 -0
  245. package/dist/web/layout/theme-menu-item.d.ts +12 -0
  246. package/dist/web/layout/theme-menu-item.js +16 -0
  247. package/dist/web/layout/theme-toggle.d.ts +17 -0
  248. package/dist/web/layout/theme-toggle.js +22 -0
  249. package/dist/web/layout/topbar.d.ts +8 -0
  250. package/dist/web/layout/topbar.js +4 -0
  251. package/dist/web/layout/workspace-shell.d.ts +63 -0
  252. package/dist/web/layout/workspace-shell.js +220 -0
  253. package/dist/web/layout/workspace-switcher.d.ts +15 -0
  254. package/dist/web/layout/workspace-switcher.js +36 -0
  255. package/dist/web/lib/accept-attr.d.ts +1 -0
  256. package/dist/web/lib/accept-attr.js +6 -0
  257. package/dist/web/lib/clamp.d.ts +1 -0
  258. package/dist/web/lib/clamp.js +3 -0
  259. package/dist/web/lib/cn.d.ts +6 -0
  260. package/dist/web/lib/cn.js +9 -0
  261. package/dist/web/lib/download.d.ts +2 -0
  262. package/dist/web/lib/download.js +24 -0
  263. package/dist/web/lib/file-ref-download.d.ts +15 -0
  264. package/dist/web/lib/file-ref-download.js +62 -0
  265. package/dist/web/lib/file-upload-form-data.d.ts +7 -0
  266. package/dist/web/lib/file-upload-form-data.js +11 -0
  267. package/dist/web/lib/resize-image.d.ts +1 -0
  268. package/dist/web/lib/resize-image.js +66 -0
  269. package/dist/web/lib/upload-file-ref.d.ts +9 -0
  270. package/dist/web/lib/upload-file-ref.js +29 -0
  271. package/dist/web/primitives/action-menu.d.ts +34 -0
  272. package/dist/web/primitives/action-menu.js +30 -0
  273. package/dist/web/primitives/calendar-popover.d.ts +13 -0
  274. package/dist/web/primitives/calendar-popover.js +76 -0
  275. package/dist/web/primitives/combobox.d.ts +56 -0
  276. package/dist/web/primitives/combobox.js +127 -0
  277. package/dist/web/primitives/date-field.d.ts +18 -0
  278. package/dist/web/primitives/date-field.js +74 -0
  279. package/dist/web/primitives/date-input.d.ts +1 -0
  280. package/dist/web/primitives/date-input.js +5 -0
  281. package/dist/web/primitives/date-parse.d.ts +11 -0
  282. package/dist/web/primitives/date-parse.js +159 -0
  283. package/dist/web/primitives/dialog.d.ts +3 -0
  284. package/dist/web/primitives/dialog.js +42 -0
  285. package/dist/web/primitives/drawer.d.ts +3 -0
  286. package/dist/web/primitives/drawer.js +17 -0
  287. package/dist/web/primitives/dropdown-menu.d.ts +15 -0
  288. package/dist/web/primitives/dropdown-menu.js +56 -0
  289. package/dist/web/primitives/embedded-list-input.d.ts +9 -0
  290. package/dist/web/primitives/embedded-list-input.js +382 -0
  291. package/dist/web/primitives/file-thumbnail.d.ts +25 -0
  292. package/dist/web/primitives/file-thumbnail.js +90 -0
  293. package/dist/web/primitives/file-upload.d.ts +21 -0
  294. package/dist/web/primitives/file-upload.js +56 -0
  295. package/dist/web/primitives/index.d.ts +34 -0
  296. package/dist/web/primitives/index.js +2021 -0
  297. package/dist/web/primitives/json-view.d.ts +7 -0
  298. package/dist/web/primitives/json-view.js +84 -0
  299. package/dist/web/primitives/layout.d.ts +27 -0
  300. package/dist/web/primitives/layout.js +30 -0
  301. package/dist/web/primitives/lightbox.d.ts +3 -0
  302. package/dist/web/primitives/lightbox.js +36 -0
  303. package/dist/web/primitives/locale-number.d.ts +2 -0
  304. package/dist/web/primitives/locale-number.js +54 -0
  305. package/dist/web/primitives/located-timestamp-input.d.ts +22 -0
  306. package/dist/web/primitives/located-timestamp-input.js +22 -0
  307. package/dist/web/primitives/metric.d.ts +5 -0
  308. package/dist/web/primitives/metric.js +18 -0
  309. package/dist/web/primitives/modal-shell.d.ts +17 -0
  310. package/dist/web/primitives/modal-shell.js +9 -0
  311. package/dist/web/primitives/modal.d.ts +3 -0
  312. package/dist/web/primitives/modal.js +13 -0
  313. package/dist/web/primitives/money-input.d.ts +18 -0
  314. package/dist/web/primitives/money-input.js +122 -0
  315. package/dist/web/primitives/narrow-pinned-footer.d.ts +12 -0
  316. package/dist/web/primitives/narrow-pinned-footer.js +153 -0
  317. package/dist/web/primitives/number-input.d.ts +18 -0
  318. package/dist/web/primitives/number-input.js +53 -0
  319. package/dist/web/primitives/option-groups.d.ts +11 -0
  320. package/dist/web/primitives/option-groups.js +27 -0
  321. package/dist/web/primitives/page-header.d.ts +3 -0
  322. package/dist/web/primitives/page-header.js +52 -0
  323. package/dist/web/primitives/promo-panel.d.ts +3 -0
  324. package/dist/web/primitives/promo-panel.js +11 -0
  325. package/dist/web/primitives/segmented-select.d.ts +21 -0
  326. package/dist/web/primitives/segmented-select.js +62 -0
  327. package/dist/web/primitives/share-actions.d.ts +4 -0
  328. package/dist/web/primitives/share-actions.js +56 -0
  329. package/dist/web/primitives/status-badge.d.ts +3 -0
  330. package/dist/web/primitives/status-badge.js +5 -0
  331. package/dist/web/primitives/sticky-action-bar.d.ts +5 -0
  332. package/dist/web/primitives/sticky-action-bar.js +15 -0
  333. package/dist/web/primitives/tabs.d.ts +3 -0
  334. package/dist/web/primitives/tabs.js +107 -0
  335. package/dist/web/primitives/timestamp-input.d.ts +27 -0
  336. package/dist/web/primitives/timestamp-input.js +87 -0
  337. package/dist/web/primitives/toast.d.ts +19 -0
  338. package/dist/web/primitives/toast.js +63 -0
  339. package/dist/web/primitives/tz-input.d.ts +11 -0
  340. package/dist/web/primitives/tz-input.js +8 -0
  341. package/dist/web/primitives/tz-options.d.ts +4 -0
  342. package/dist/web/primitives/tz-options.js +17 -0
  343. package/dist/web/primitives/use-keyboard-inset.d.ts +1 -0
  344. package/dist/web/primitives/use-keyboard-inset.js +28 -0
  345. package/dist/web/primitives/use-narrow-viewport.d.ts +4 -0
  346. package/dist/web/primitives/use-narrow-viewport.js +46 -0
  347. package/dist/web/react-css-custom-properties.d.ts +6 -0
  348. package/dist/web/react-css-custom-properties.js +1 -0
  349. package/dist/web/sse/live-events.d.ts +15 -0
  350. package/dist/web/sse/live-events.js +148 -0
  351. package/dist/web/theme-plugin.d.ts +41 -0
  352. package/dist/web/theme-plugin.js +123 -0
  353. package/dist/web/tokens.d.ts +24 -0
  354. package/dist/web/tokens.js +203 -0
  355. package/dist/web/ui/avatar.d.ts +11 -0
  356. package/dist/web/ui/avatar.js +25 -0
  357. package/dist/web/ui/badge.d.ts +9 -0
  358. package/dist/web/ui/badge.js +26 -0
  359. package/dist/web/ui/breadcrumb.d.ts +11 -0
  360. package/dist/web/ui/breadcrumb.js +29 -0
  361. package/dist/web/ui/button.d.ts +10 -0
  362. package/dist/web/ui/button.js +37 -0
  363. package/dist/web/ui/checkbox.d.ts +4 -0
  364. package/dist/web/ui/checkbox.js +11 -0
  365. package/dist/web/ui/input.d.ts +3 -0
  366. package/dist/web/ui/input.js +8 -0
  367. package/dist/web/ui/label.d.ts +4 -0
  368. package/dist/web/ui/label.js +10 -0
  369. package/dist/web/ui/resizable.d.ts +7 -0
  370. package/dist/web/ui/resizable.js +16 -0
  371. package/dist/web/ui/separator.d.ts +4 -0
  372. package/dist/web/ui/separator.js +10 -0
  373. package/dist/web/ui/sheet.d.ts +14 -0
  374. package/dist/web/ui/sheet.js +42 -0
  375. package/dist/web/ui/sidebar.d.ts +73 -0
  376. package/dist/web/ui/sidebar.js +211 -0
  377. package/dist/web/ui/skeleton.d.ts +2 -0
  378. package/dist/web/ui/skeleton.js +7 -0
  379. package/dist/web/ui/switch.d.ts +6 -0
  380. package/dist/web/ui/switch.js +10 -0
  381. package/dist/web/ui/table.d.ts +10 -0
  382. package/dist/web/ui/table.js +30 -0
  383. package/dist/web/ui/tabs.d.ts +11 -0
  384. package/dist/web/ui/tabs.js +31 -0
  385. package/dist/web/ui/textarea.d.ts +3 -0
  386. package/dist/web/ui/textarea.js +8 -0
  387. package/dist/web/ui/tooltip.d.ts +7 -0
  388. package/dist/web/ui/tooltip.js +19 -0
  389. package/dist/web/ui/use-mobile.d.ts +1 -0
  390. package/dist/web/ui/use-mobile.js +16 -0
  391. package/dist/web/version/update-checker.d.ts +9 -0
  392. package/dist/web/version/update-checker.js +88 -0
  393. package/dist/web/widgets/ai-text-field.d.ts +36 -0
  394. package/dist/web/widgets/ai-text-field.js +137 -0
  395. package/dist/web/widgets/chart-window.d.ts +58 -0
  396. package/dist/web/widgets/chart-window.js +190 -0
  397. package/dist/web/widgets/charts.d.ts +208 -0
  398. package/dist/web/widgets/charts.js +685 -0
  399. package/dist/web/widgets/collapsible-section.d.ts +10 -0
  400. package/dist/web/widgets/collapsible-section.js +17 -0
  401. package/dist/web/widgets/dashboard-list.d.ts +21 -0
  402. package/dist/web/widgets/dashboard-list.js +45 -0
  403. package/dist/web/widgets/detail-list.d.ts +15 -0
  404. package/dist/web/widgets/detail-list.js +8 -0
  405. package/dist/web/widgets/drawer.d.ts +65 -0
  406. package/dist/web/widgets/drawer.js +142 -0
  407. package/dist/web/widgets/feed-list.d.ts +13 -0
  408. package/dist/web/widgets/feed-list.js +9 -0
  409. package/dist/web/widgets/floating-panel.d.ts +30 -0
  410. package/dist/web/widgets/floating-panel.js +242 -0
  411. package/dist/web/widgets/form-fields.d.ts +93 -0
  412. package/dist/web/widgets/form-fields.js +50 -0
  413. package/dist/web/widgets/index.d.ts +27 -0
  414. package/dist/web/widgets/index.js +30 -0
  415. package/dist/web/widgets/infinity-list.d.ts +46 -0
  416. package/dist/web/widgets/infinity-list.js +211 -0
  417. package/dist/web/widgets/mode-switch.d.ts +21 -0
  418. package/dist/web/widgets/mode-switch.js +25 -0
  419. package/dist/web/widgets/photo-slots.d.ts +29 -0
  420. package/dist/web/widgets/photo-slots.js +52 -0
  421. package/dist/web/widgets/plan-card.d.ts +36 -0
  422. package/dist/web/widgets/plan-card.js +13 -0
  423. package/dist/web/widgets/progress-bar.d.ts +19 -0
  424. package/dist/web/widgets/progress-bar.js +24 -0
  425. package/dist/web/widgets/progress-list.d.ts +19 -0
  426. package/dist/web/widgets/progress-list.js +11 -0
  427. package/dist/web/widgets/public-shell.d.ts +22 -0
  428. package/dist/web/widgets/public-shell.js +15 -0
  429. package/dist/web/widgets/query-table.d.ts +29 -0
  430. package/dist/web/widgets/query-table.js +30 -0
  431. package/dist/web/widgets/result-panel.d.ts +69 -0
  432. package/dist/web/widgets/result-panel.js +30 -0
  433. package/dist/web/widgets/section-card.d.ts +12 -0
  434. package/dist/web/widgets/section-card.js +9 -0
  435. package/dist/web/widgets/sheet-parts.d.ts +9 -0
  436. package/dist/web/widgets/sheet-parts.js +11 -0
  437. package/dist/web/widgets/side-by-side-table.d.ts +31 -0
  438. package/dist/web/widgets/side-by-side-table.js +11 -0
  439. package/dist/web/widgets/stat.d.ts +54 -0
  440. package/dist/web/widgets/stat.js +65 -0
  441. package/dist/web/widgets/states.d.ts +23 -0
  442. package/dist/web/widgets/states.js +20 -0
  443. package/dist/web/widgets/status-badge.d.ts +15 -0
  444. package/dist/web/widgets/status-badge.js +24 -0
  445. package/dist/web/widgets/step-bar.d.ts +35 -0
  446. package/dist/web/widgets/step-bar.js +60 -0
  447. package/dist/web/widgets/upload-zone.d.ts +24 -0
  448. package/dist/web/widgets/upload-zone.js +108 -0
  449. package/dist/web/widgets/use-draft.d.ts +20 -0
  450. package/dist/web/widgets/use-draft.js +21 -0
  451. package/dist/web/widgets/use-pointer-drag.d.ts +21 -0
  452. package/dist/web/widgets/use-pointer-drag.js +53 -0
  453. package/package.json +105 -7
  454. package/src/changes.json +77 -0
  455. package/src/web/fonts/OFL.txt +93 -0
  456. package/src/web/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
  457. package/src/web/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
  458. package/src/web/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
  459. package/src/web/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
  460. package/src/web/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
  461. package/src/web/fonts/ibm-plex-sans-latin-500.woff2 +0 -0
  462. package/src/web/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
  463. package/src/web/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
  464. package/src/web/fonts/ibm-plex-sans-latin-ext-500.woff2 +0 -0
  465. package/src/web/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
  466. package/src/web/styles.css +401 -0
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Delegates keyboard nav + ARIA (role=tablist/tab) to Radix instead of hand-rolling it.
3
+ import { useCallback, useLayoutEffect, useRef, useState } from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { Tabs, TabsList, TabsTrigger } from "../ui/tabs.js";
6
+ // The underline is a real border-b-2 (board) instead of the vendored ::after bar, so the
7
+ // vendored active/dark border resets are overridden with identical variant chains.
8
+ const TAB_TRIGGER_CLASS = "h-full shrink-0 flex-none whitespace-nowrap rounded-none border-0 border-b-2 border-transparent focus-visible:border-transparent bg-transparent px-0 py-0 text-sm font-medium text-foreground-secondary after:hidden hover:text-foreground data-[state=active]:border-primary data-[state=active]:focus-visible:border-primary data-[state=active]:bg-transparent data-[state=active]:font-semibold data-[state=active]:text-foreground dark:text-foreground-secondary dark:data-[state=active]:border-primary dark:data-[state=active]:bg-transparent dark:data-[state=active]:text-foreground dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-primary";
9
+ // A single fade-width both edges share — mask-image needs the same offset
10
+ // on each side or the gradient reads as lopsided.
11
+ const EDGE_FADE_PX = 24;
12
+ function edgeFadeMaskImage(start, end) {
13
+ if (start && end) {
14
+ return `linear-gradient(to right, transparent, black ${EDGE_FADE_PX}px, black calc(100% - ${EDGE_FADE_PX}px), transparent)`;
15
+ }
16
+ if (start)
17
+ return `linear-gradient(to right, transparent, black ${EDGE_FADE_PX}px)`;
18
+ if (end)
19
+ return `linear-gradient(to left, transparent, black ${EDGE_FADE_PX}px)`;
20
+ return undefined;
21
+ }
22
+ export function DefaultTabs({ items, activeId, onSelect, testId }) {
23
+ const scrollerRef = useRef(null);
24
+ // Two primitive booleans instead of one object: setState with a fresh
25
+ // object on every measurement would always change reference and force
26
+ // another render — with the unconditional-per-render effect below that
27
+ // would loop forever. Primitive setState lets React bail out once the
28
+ // value stops changing.
29
+ const [scrollStart, setScrollStart] = useState(false);
30
+ const [scrollEnd, setScrollEnd] = useState(false);
31
+ const updateScrollEdges = useCallback(() => {
32
+ const el = scrollerRef.current;
33
+ if (el === null) {
34
+ setScrollStart(false);
35
+ setScrollEnd(false);
36
+ return;
37
+ }
38
+ const maxScrollLeft = el.scrollWidth - el.clientWidth;
39
+ // 1px slack absorbs sub-pixel layout rounding that would otherwise leave
40
+ // a fade permanently on for a strip that doesn't visually overflow.
41
+ setScrollStart(el.scrollLeft > 1);
42
+ setScrollEnd(el.scrollLeft < maxScrollLeft - 1);
43
+ }, []);
44
+ // Gated on [activeId] instead of running unconditionally: an unconditional
45
+ // effect also re-ran this on every render, including the render triggered
46
+ // by the scroll listener's own setScrollStart/End below — so scrolling the
47
+ // strip by hand immediately snapped it back to the active tab, making it
48
+ // impossible to reach any other tab by scrolling (fw#follow-up). Only a
49
+ // mount or an actual activeId change should force the active tab back
50
+ // into view.
51
+ // biome-ignore lint/correctness/useExhaustiveDependencies: activeId is the intended re-run trigger even though the DOM query below doesn't reference it directly; updateScrollEdges is a stable useCallback([]) ref and doesn't need to be listed.
52
+ useLayoutEffect(() => {
53
+ const el = scrollerRef.current;
54
+ if (el === null)
55
+ return;
56
+ // Radix marks the active trigger via aria-selected — reading it back
57
+ // through the DOM avoids threading a ref through the vendored
58
+ // TabsTrigger wrapper for every item.
59
+ const trigger = el.querySelector('[role="tab"][aria-selected="true"]');
60
+ if (trigger !== null) {
61
+ const triggerStart = trigger.offsetLeft;
62
+ const triggerEnd = triggerStart + trigger.offsetWidth;
63
+ // scrollIntoView would also scroll the page vertically to reveal the
64
+ // trigger — setting scrollLeft directly touches only this strip.
65
+ if (triggerStart < el.scrollLeft) {
66
+ el.scrollLeft = triggerStart;
67
+ }
68
+ else if (triggerEnd > el.scrollLeft + el.clientWidth) {
69
+ el.scrollLeft = triggerEnd - el.clientWidth;
70
+ }
71
+ }
72
+ updateScrollEdges();
73
+ }, [activeId]);
74
+ // Edge-fade measurement stays independent of activeId: a changing item
75
+ // count can change scrollWidth without activeId changing (e.g. a count
76
+ // badge finishing a load), and this must not fight the scroll-to-active
77
+ // effect above by re-running on every scroll-driven render.
78
+ // biome-ignore lint/correctness/useExhaustiveDependencies: items.length is the intended re-run trigger even though the effect body doesn't reference it directly.
79
+ useLayoutEffect(() => {
80
+ updateScrollEdges();
81
+ }, [items.length, updateScrollEdges]);
82
+ useLayoutEffect(() => {
83
+ const el = scrollerRef.current;
84
+ if (el === null)
85
+ return;
86
+ el.addEventListener("scroll", updateScrollEdges, { passive: true });
87
+ if (typeof ResizeObserver === "undefined") {
88
+ return () => el.removeEventListener("scroll", updateScrollEdges);
89
+ }
90
+ // Observes both the scroller and the list: a growing TabsList (e.g. a
91
+ // count badge finishing a load) can change scrollWidth without the
92
+ // scroller's own box ever resizing.
93
+ const observer = new ResizeObserver(updateScrollEdges);
94
+ observer.observe(el);
95
+ const list = el.firstElementChild;
96
+ if (list !== null)
97
+ observer.observe(list);
98
+ return () => {
99
+ el.removeEventListener("scroll", updateScrollEdges);
100
+ observer.disconnect();
101
+ };
102
+ }, [updateScrollEdges]);
103
+ const maskImage = edgeFadeMaskImage(scrollStart, scrollEnd);
104
+ return (_jsx(Tabs, { value: activeId, onValueChange: onSelect, "data-testid": testId, className: "min-w-0", children: _jsx("div", { ref: scrollerRef, "data-scroll-start": scrollStart ? "" : undefined, "data-scroll-end": scrollEnd ? "" : undefined, className: "relative min-w-0 overflow-x-auto border-b border-border px-6 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", style: maskImage !== undefined ? { maskImage, WebkitMaskImage: maskImage } : undefined, children: _jsx(TabsList, { variant: "line", className: "h-11 gap-6 p-0", children: items.map((item) => (_jsxs(TabsTrigger, { value: item.id, className: TAB_TRIGGER_CLASS, "aria-controls": undefined, "data-testid": testId !== undefined ? `${testId}-${item.id}` : undefined, children: [item.label, item.count !== undefined && (_jsx("span", { className: cn("ml-1.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full px-[5px] text-xs font-medium", item.id === activeId
105
+ ? "bg-primary/10 text-foreground"
106
+ : "bg-status-neutral-surface text-foreground-secondary"), "data-testid": testId !== undefined ? `${testId}-${item.id}-count` : undefined, children: item.count }))] }, item.id))) }) }) }));
107
+ }
@@ -0,0 +1,27 @@
1
+ import { type ReactNode } from "react";
2
+ /** Form-State → datetime-local-Format (`yyyy-MM-ddTHH:mm`).
3
+ * UTC-Instants (mit `Z`/Offset) werden in lokale Wall-Clock
4
+ * umgerechnet; offset-lose Werte nur auf Minuten gekürzt. */
5
+ export declare function timestampToInputValue(value: string): string;
6
+ /** datetime-local-Wert → Wire-Format. wallClock=true reicht die
7
+ * Wall-Clock unverändert durch; sonst wird die lokale Zeit als
8
+ * UTC-Instant mit `Z`-Suffix emittiert. */
9
+ export declare function inputValueToTimestamp(raw: string, wallClock: boolean): string | undefined;
10
+ export type TimestampInputProps = {
11
+ readonly id: string;
12
+ readonly name: string;
13
+ readonly value: string;
14
+ readonly onChange: (v: string | undefined) => void;
15
+ readonly wallClock?: boolean;
16
+ readonly disabled?: boolean;
17
+ readonly required?: boolean;
18
+ readonly hasError?: boolean;
19
+ readonly ariaLabel?: string;
20
+ readonly locale?: string;
21
+ /** Untere/obere Grenze als ISO-Datetime. Begrenzt den Kalender auf
22
+ * Tages-Granularität; die exakte Uhrzeit-Grenze setzt die Zod-Validierung
23
+ * durch. */
24
+ readonly min?: string;
25
+ readonly max?: string;
26
+ };
27
+ export declare function TimestampInput({ id, name, value, onChange, wallClock, disabled, required, hasError, ariaLabel, locale, min, max, }: TimestampInputProps): ReactNode;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // TimestampInput (kind:"timestamp") — gemeinsame Datums-Eingabe (DateField,
3
+ // Tippen + Jahres-Kalender) plus ein Uhrzeit-Input. Bis #369 war dies ein
4
+ // natives <input type="datetime-local"> — tippbar, aber mit nicht-stylebarem
5
+ // Browser-Picker ohne Jahres-Navigation und divergent zu DateInput.
6
+ //
7
+ // Die Wire-Konvertierung bleibt unverändert: der Server validiert timestamp
8
+ // als ISO-UTC mit `Z` (z.iso.datetime()) bzw. als Wall-Clock ohne Offset bei
9
+ // locatedTimestamps (z.iso.datetime({ local: true })). Intern rechnen wir
10
+ // über die datetime-local-Form `yyyy-MM-ddTHH:mm`; timestampToInputValue /
11
+ // inputValueToTimestamp kapseln die UTC↔Wall-Clock-Umrechnung (Bug-Bash-2,
12
+ // 2026-06-08) und sind separat getestet.
13
+ import { useState } from "react";
14
+ import { useTranslation } from "../../index.js";
15
+ import { cn } from "../lib/cn.js";
16
+ import { DateField } from "./date-field.js";
17
+ const LOCAL_MINUTES = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
18
+ const HAS_OFFSET = /(?:Z|[+-]\d{2}:\d{2})$/;
19
+ function pad(n) {
20
+ return String(n).padStart(2, "0");
21
+ }
22
+ /** Form-State → datetime-local-Format (`yyyy-MM-ddTHH:mm`).
23
+ * UTC-Instants (mit `Z`/Offset) werden in lokale Wall-Clock
24
+ * umgerechnet; offset-lose Werte nur auf Minuten gekürzt. */
25
+ export function timestampToInputValue(value) {
26
+ if (value === "")
27
+ return "";
28
+ if (HAS_OFFSET.test(value)) {
29
+ const d = new Date(value);
30
+ if (Number.isNaN(d.getTime()))
31
+ return "";
32
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
33
+ }
34
+ const m = LOCAL_MINUTES.exec(value);
35
+ return m !== null ? m[0] : "";
36
+ }
37
+ /** datetime-local-Wert → Wire-Format. wallClock=true reicht die
38
+ * Wall-Clock unverändert durch; sonst wird die lokale Zeit als
39
+ * UTC-Instant mit `Z`-Suffix emittiert. */
40
+ export function inputValueToTimestamp(raw, wallClock) {
41
+ if (raw === "")
42
+ return undefined;
43
+ if (wallClock)
44
+ return raw;
45
+ // Offset-loser Datetime-String wird von Date als LOKALE Zeit geparst
46
+ // (ES2020+) — genau die Semantik die datetime-local liefert.
47
+ const d = new Date(raw);
48
+ if (Number.isNaN(d.getTime()))
49
+ return undefined;
50
+ // Seconds are mandatory: write schemas validate offset-bearing timestamps
51
+ // with z.iso.datetime(), which requires `HH:mm:ssZ`.
52
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`;
53
+ }
54
+ const timeInputClass = "h-9 w-[7.5rem] shrink-0 rounded-md border border-input bg-transparent px-3 py-1 text-sm " +
55
+ "shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 " +
56
+ "focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
57
+ function toDatePart(iso) {
58
+ if (iso === undefined || iso === "")
59
+ return undefined;
60
+ const v = timestampToInputValue(iso).slice(0, 10);
61
+ return v !== "" ? v : undefined;
62
+ }
63
+ export function TimestampInput({ id, name, value, onChange, wallClock, disabled, required, hasError, ariaLabel, locale, min, max, }) {
64
+ const t = useTranslation();
65
+ const local = timestampToInputValue(value);
66
+ const isoDate = local !== "" ? local.slice(0, 10) : "";
67
+ // Uhrzeit ohne gesetztes Datum würde im Wire-Wert verschwinden — lokal
68
+ // halten, bis ein Datum gewählt ist. Sobald `value` ein Datum trägt, ist
69
+ // dessen Uhrzeit maßgeblich.
70
+ const [timeText, setTimeText] = useState("");
71
+ const effectiveTime = local !== "" ? local.slice(11, 16) : timeText;
72
+ function emit(nextIsoDate, nextTime) {
73
+ if (nextIsoDate === "") {
74
+ onChange(undefined);
75
+ return;
76
+ }
77
+ const localStr = `${nextIsoDate}T${nextTime === "" ? "00:00" : nextTime}`;
78
+ onChange(inputValueToTimestamp(localStr, wallClock === true));
79
+ }
80
+ const minPart = toDatePart(min);
81
+ const maxPart = toDatePart(max);
82
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(DateField, { id: id, name: name, value: isoDate, onChange: (d) => emit(d ?? "", effectiveTime), ...(locale !== undefined && { locale }), ...(minPart !== undefined && { min: minPart }), ...(maxPart !== undefined && { max: maxPart }), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }), ...(ariaLabel !== undefined && { ariaLabel }) }), _jsx("input", { type: "time", "aria-label": t("kumiko.field.time"), disabled: disabled, "aria-invalid": hasError === true ? true : undefined, value: effectiveTime, onChange: (e) => {
83
+ setTimeText(e.target.value);
84
+ if (isoDate !== "")
85
+ emit(isoDate, e.target.value);
86
+ }, className: cn(timeInputClass, hasError === true && "border-destructive focus-visible:ring-destructive") })] }));
87
+ }
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from "react";
2
+ import type { StatusTone } from "../widgets/status-badge.tsx";
3
+ export type ToastVariant = StatusTone;
4
+ export type ToastOptions = {
5
+ readonly title: string;
6
+ readonly description?: string;
7
+ readonly variant?: ToastVariant;
8
+ readonly docsUrl?: string;
9
+ readonly docsLinkLabel?: string;
10
+ };
11
+ type ToastApi = {
12
+ readonly toast: (opts: ToastOptions) => void;
13
+ };
14
+ export declare function useToast(): ToastApi;
15
+ export type ToastProviderProps = {
16
+ readonly children: ReactNode;
17
+ };
18
+ export declare function ToastProvider({ children }: ToastProviderProps): ReactNode;
19
+ export {};
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Toast-Primitive auf @radix-ui/react-toast. Ein globaler Provider
3
+ // + ein useToast()-Hook der von überall in der App aus Toasts auslösen
4
+ // kann. Kein Context-Boilerplate für Konsumenten — der Hook returned
5
+ // einen `toast()`-Trigger, mehr Public-API gibt's nicht.
6
+ //
7
+ // Variant = StatusTone (dieselbe Farb-Familie wie StatusBadge), Default
8
+ // "muted" (neutral). Auto-dismiss nach 5s, manuell schließbar via X-Button.
9
+ // Der ARIA-Live-Region-Setup kommt komplett aus Radix.
10
+ import * as Primitive from "@radix-ui/react-toast";
11
+ import { X } from "lucide-react";
12
+ import { createContext, useCallback, useContext, useId, useMemo, useRef, useState, } from "react";
13
+ import { useTranslation } from "../../index.js";
14
+ import { cn } from "../lib/cn.js";
15
+ const ToastContext = createContext(null);
16
+ export function useToast() {
17
+ const ctx = useContext(ToastContext);
18
+ if (ctx === null) {
19
+ // Nicht throwen — Tests die einen Component ohne Provider rendern
20
+ // sollen nicht crashen. No-op statt fehlerhaftes Mount.
21
+ return { toast: () => undefined };
22
+ }
23
+ return ctx;
24
+ }
25
+ export function ToastProvider({ children }) {
26
+ const [entries, setEntries] = useState([]);
27
+ // Monoton wachsender Counter via useRef — useState hätte einen
28
+ // closure-Bug: zwei toast()-Calls im selben Tick lesen denselben
29
+ // alten Wert aus dem Closure und kollidieren bei der ID. Ref ist
30
+ // synchron-mutabel, jeder Aufruf bekommt eine garantiert neue ID.
31
+ const idPrefix = useId();
32
+ const counterRef = useRef(0);
33
+ const toast = useCallback((opts) => {
34
+ counterRef.current += 1;
35
+ const id = `${idPrefix}-${counterRef.current}`;
36
+ setEntries((current) => [...current, { ...opts, id }]);
37
+ }, [idPrefix]);
38
+ const api = useMemo(() => ({ toast }), [toast]);
39
+ return (_jsx(ToastContext.Provider, { value: api, children: _jsxs(Primitive.Provider, { swipeDirection: "right", duration: 5000, children: [children, entries.map((entry) => (_jsx(ToastItem, { entry: entry, onClose: () => setEntries((current) => current.filter((e) => e.id !== entry.id)) }, entry.id))), _jsx(Primitive.Viewport, { className: cn("fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4", "sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]") })] }) }));
40
+ }
41
+ const rootClass = "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 " +
42
+ "overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all " +
43
+ "data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] " +
44
+ "data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none " +
45
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out " +
46
+ "data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full " +
47
+ "data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full";
48
+ const TONE_CLASS = {
49
+ ok: "border-status-ok/30 bg-status-ok/10 text-status-ok",
50
+ warn: "border-status-warn/30 bg-status-warn/10 text-status-warn",
51
+ bad: "border-status-bad/30 bg-status-bad/10 text-status-bad",
52
+ critical: "border-status-critical/40 bg-status-critical/15 text-status-critical",
53
+ muted: "border bg-background text-foreground",
54
+ };
55
+ function ToastItem({ entry, onClose, }) {
56
+ const t = useTranslation();
57
+ const learnMore = entry.docsLinkLabel ?? t("kumiko.toast.learn-more");
58
+ const variantClass = TONE_CLASS[entry.variant ?? "muted"];
59
+ return (_jsxs(Primitive.Root, { className: cn(rootClass, variantClass), onOpenChange: (open) => {
60
+ if (!open)
61
+ onClose();
62
+ }, children: [_jsxs("div", { className: "grid gap-1", children: [_jsx(Primitive.Title, { className: "text-sm font-semibold", children: entry.title }), entry.description !== undefined && (_jsx(Primitive.Description, { className: "text-sm opacity-90", children: entry.description })), entry.docsUrl !== undefined && (_jsx(Primitive.Action, { altText: learnMore, asChild: true, children: _jsxs("a", { href: entry.docsUrl, target: "_blank", rel: "noopener noreferrer", className: cn("text-xs underline opacity-90 hover:opacity-100", "focus:outline-none focus:ring-1 focus:ring-current rounded"), children: [learnMore, " \u2192"] }) }))] }), _jsx(Primitive.Close, { className: cn("absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity", "hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1", "group-hover:opacity-100"), "aria-label": t("kumiko.dialog.close"), children: _jsx(X, { className: "h-4 w-4" }) })] }));
63
+ }
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ export type TzInputProps = {
3
+ readonly id: string;
4
+ readonly name: string;
5
+ readonly value: string;
6
+ readonly onChange: (v: string | undefined) => void;
7
+ readonly disabled?: boolean;
8
+ readonly required?: boolean;
9
+ readonly hasError?: boolean;
10
+ };
11
+ export declare function TzInput({ id, name, value, onChange, disabled, required, hasError, }: TzInputProps): ReactNode;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from "../../index.js";
3
+ import { ComboboxInput } from "./combobox.js";
4
+ import { TZ_OPTIONS } from "./tz-options.js";
5
+ export function TzInput({ id, name, value, onChange, disabled, required, hasError, }) {
6
+ const t = useTranslation();
7
+ return (_jsx(ComboboxInput, { id: id, name: name, options: TZ_OPTIONS, value: value, onChange: (v) => onChange(v === "" ? undefined : v), placeholder: t("kumiko.field.timezone"), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }) }));
8
+ }
@@ -0,0 +1,4 @@
1
+ export declare const TZ_OPTIONS: readonly {
2
+ readonly value: string;
3
+ readonly label: string;
4
+ }[];
@@ -0,0 +1,17 @@
1
+ // Curated fallback list for runtimes without Intl.supportedValuesOf (pre
2
+ // ES2022). Covers the most common zones; modern browsers + Bun return the
3
+ // full IANA list.
4
+ const FALLBACK_ZONES = [
5
+ "UTC",
6
+ "Europe/Berlin",
7
+ "Europe/London",
8
+ "Europe/Paris",
9
+ "Europe/Madrid",
10
+ "America/New_York",
11
+ "America/Los_Angeles",
12
+ "America/Sao_Paulo",
13
+ "Asia/Tokyo",
14
+ "Asia/Singapore",
15
+ "Australia/Sydney",
16
+ ];
17
+ export const TZ_OPTIONS = (typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("timeZone") : FALLBACK_ZONES).map((zone) => ({ value: zone, label: zone }));
@@ -0,0 +1 @@
1
+ export declare function useKeyboardInset(): number;
@@ -0,0 +1,28 @@
1
+ import { useEffect, useState } from "react";
2
+ function readKeyboardInset() {
3
+ const viewport = window.visualViewport;
4
+ if (viewport === null || viewport === undefined)
5
+ return 0;
6
+ return Math.max(0, Math.round(window.innerHeight - viewport.height - viewport.offsetTop));
7
+ }
8
+ // iOS Safari ignores `interactive-widget=resizes-content`: the layout viewport keeps its full
9
+ // height under the keyboard, so `fixed bottom-0` bars sit behind it. Where the key is honoured
10
+ // (Chromium/Android) the layout viewport shrinks too, the difference is 0 and nothing shifts.
11
+ export function useKeyboardInset() {
12
+ const [inset, setInset] = useState(0);
13
+ useEffect(() => {
14
+ const viewport = window.visualViewport;
15
+ // skip: without visualViewport no keyboard inset is measurable
16
+ if (viewport === null || viewport === undefined)
17
+ return;
18
+ const update = () => setInset(readKeyboardInset());
19
+ update();
20
+ viewport.addEventListener("resize", update);
21
+ viewport.addEventListener("scroll", update);
22
+ return () => {
23
+ viewport.removeEventListener("resize", update);
24
+ viewport.removeEventListener("scroll", update);
25
+ };
26
+ }, []);
27
+ return inset;
28
+ }
@@ -0,0 +1,4 @@
1
+ export declare const NARROW_VIEWPORT_BREAKPOINT_PX = 768;
2
+ export declare function useIsViewportBelow(breakpointPx: number): boolean;
3
+ export declare function useIsNarrowViewport(): boolean;
4
+ export declare function useIsBelowSmViewport(): boolean;
@@ -0,0 +1,46 @@
1
+ import { useSyncExternalStore } from "react";
2
+ // Matches ui/use-mobile.ts's MOBILE_BREAKPOINT. Kept as a separate constant
3
+ // because that file is vendored shadcn (regenerated via scripts/sync-shadcn.ts)
4
+ // and cannot be imported from without risking a future overwrite.
5
+ export const NARROW_VIEWPORT_BREAKPOINT_PX = 768;
6
+ const SM_BREAKPOINT = 640;
7
+ // Module-level stores keep subscribe stable, so useSyncExternalStore does not
8
+ // resubscribe on every render (FormFooter re-renders on each keystroke).
9
+ function createBelowBreakpointStore(breakpoint) {
10
+ const query = `(max-width: ${breakpoint - 1}px)`;
11
+ return {
12
+ subscribe: (callback) => {
13
+ const mql = window.matchMedia(query);
14
+ mql.addEventListener("change", callback);
15
+ return () => mql.removeEventListener("change", callback);
16
+ },
17
+ getSnapshot: () => window.matchMedia(query).matches,
18
+ };
19
+ }
20
+ const belowBreakpointStores = new Map();
21
+ function belowBreakpointStore(breakpoint) {
22
+ const cached = belowBreakpointStores.get(breakpoint);
23
+ if (cached !== undefined)
24
+ return cached;
25
+ const created = createBelowBreakpointStore(breakpoint);
26
+ belowBreakpointStores.set(breakpoint, created);
27
+ return created;
28
+ }
29
+ function getServerSnapshot() {
30
+ return false;
31
+ }
32
+ // Unlike the vendored `useIsMobile` (ui/use-mobile.ts), which only sets its
33
+ // result in a `useEffect` and therefore always reports `false` on the first
34
+ // render regardless of actual viewport, this reads the real value up front
35
+ // via `useSyncExternalStore` — no wrong-then-corrected first render.
36
+ export function useIsViewportBelow(breakpointPx) {
37
+ const store = belowBreakpointStore(breakpointPx);
38
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);
39
+ }
40
+ export function useIsNarrowViewport() {
41
+ return useIsViewportBelow(NARROW_VIEWPORT_BREAKPOINT_PX);
42
+ }
43
+ // Tailwind's `sm` boundary: true below 640px.
44
+ export function useIsBelowSmViewport() {
45
+ return useIsViewportBelow(SM_BREAKPOINT);
46
+ }
@@ -0,0 +1,6 @@
1
+ declare module "react" {
2
+ interface CSSProperties {
3
+ [customProperty: `--${string}`]: string | number | undefined;
4
+ }
5
+ }
6
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { type LiveEventSubscriber } from "../../index.ts";
2
+ export type CreateEventSourceLiveEventsOptions = {
3
+ /** URL des SSE-Endpoints. Default: /api/sse (das ist wo
4
+ * createSseRoute im Kumiko-Server mountet). Override wenn der
5
+ * Mountpath divergiert. */
6
+ readonly url?: string;
7
+ /** Fires once per connection when the session ended: the server refused the SSE
8
+ * handshake, or opened the stream with a `session-ended` frame (stale auth
9
+ * cookie downgraded to anonymous). Same codes as the dispatcher path. */
10
+ readonly onSessionEnded?: () => void;
11
+ };
12
+ /** Liefert einen `LiveEventSubscriber` der EventSource-backed ist.
13
+ * Normalerweise einmal im App-Bootstrap gerufen und als value an
14
+ * `<LiveEventsProvider>` durchgereicht — createKumikoApp tut das. */
15
+ export declare function createEventSourceLiveEvents(options?: CreateEventSourceLiveEventsOptions): LiveEventSubscriber;
@@ -0,0 +1,148 @@
1
+ import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { isSessionEndedError } from "../../index.js";
3
+ // Wire name of the first frame on a stream downgraded from a stale auth cookie (sse-route.ts).
4
+ const SESSION_ENDED_EVENT = "session-ended";
5
+ function sessionEndedFrameCode(event) {
6
+ const data = propertyOf(event, "data");
7
+ if (typeof data !== "string")
8
+ return undefined;
9
+ try {
10
+ const code = propertyOf(JSON.parse(data), "code");
11
+ return typeof code === "string" ? code : undefined;
12
+ }
13
+ catch {
14
+ // skip: malformed frame, not evidence of a session end
15
+ return undefined;
16
+ }
17
+ }
18
+ function hasErrorCode(body) {
19
+ if (typeof body !== "object" || body === null || !("error" in body))
20
+ return false;
21
+ const { error } = body;
22
+ return (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string");
23
+ }
24
+ // EventSource never exposes the HTTP status, so one plain fetch of the same
25
+ // URL tells a session end apart from an outage. Aborted right after the
26
+ // headers/body so a 200 doesn't hold a second stream open.
27
+ async function isSessionEndedRejection(url) {
28
+ const controller = new AbortController();
29
+ try {
30
+ // guard-allow: same-origin fetch — url is the app's own SSE endpoint, probed with same-origin credentials
31
+ const res = await fetch(url, {
32
+ credentials: "same-origin",
33
+ headers: { Accept: "text/event-stream" },
34
+ signal: controller.signal,
35
+ });
36
+ if (res.status !== 401)
37
+ return false;
38
+ const body = await res.json();
39
+ return hasErrorCode(body) && isSessionEndedError({ code: body.error.code, httpStatus: 401 });
40
+ }
41
+ catch {
42
+ // skip: probe failed (offline, unparseable body) — not evidence of a session end
43
+ return false;
44
+ }
45
+ finally {
46
+ controller.abort();
47
+ }
48
+ }
49
+ /** Liefert einen `LiveEventSubscriber` der EventSource-backed ist.
50
+ * Normalerweise einmal im App-Bootstrap gerufen und als value an
51
+ * `<LiveEventsProvider>` durchgereicht — createKumikoApp tut das. */
52
+ export function createEventSourceLiveEvents(options = {}) {
53
+ const url = options.url ?? "/api/sse";
54
+ const subscribers = new Set();
55
+ let source;
56
+ const wiredEntities = new Set();
57
+ // `subscribers` is a flat Set across all entities — the browser-side
58
+ // addEventListener(entityName, ...) gate below only filters which frames
59
+ // arrive at all, not which subscriber a given frame is for. Without this
60
+ // filter, an `invoice` subscriber would also fire on every `user` frame.
61
+ const handleEvent = (raw) => {
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(raw);
65
+ }
66
+ catch {
67
+ // skip: malformed SSE payload, drop it rather than crash all subscribers
68
+ return;
69
+ }
70
+ const event = { type: parsed.aggregateType, data: parsed };
71
+ for (const sub of subscribers) {
72
+ if (sub.entityName === parsed.aggregateType)
73
+ sub.listener(event);
74
+ }
75
+ };
76
+ const ensureConnected = () => {
77
+ // skip: EventSource already connected
78
+ if (source !== undefined)
79
+ return;
80
+ // skip: no window/EventSource available (SSR/non-browser), nothing to connect
81
+ if (typeof window === "undefined" || typeof EventSource === "undefined")
82
+ return;
83
+ const connection = new EventSource(url);
84
+ source = connection;
85
+ const { onSessionEnded } = options;
86
+ // skip: no session-end consumer, a refused handshake needs no probe
87
+ if (onSessionEnded === undefined)
88
+ return;
89
+ let sessionEndedSignalled = false;
90
+ const signalSessionEnded = () => {
91
+ // skip: the frame and the error probe can both report the same session end
92
+ if (sessionEndedSignalled)
93
+ return;
94
+ sessionEndedSignalled = true;
95
+ onSessionEnded();
96
+ };
97
+ connection.addEventListener(SESSION_ENDED_EVENT, (e) => {
98
+ const code = sessionEndedFrameCode(e);
99
+ if (code !== undefined && isSessionEndedError({ code, httpStatus: 401 }))
100
+ signalSessionEnded();
101
+ });
102
+ connection.addEventListener("error", () => {
103
+ // skip: CONNECTING means a dropped stream the browser retries itself;
104
+ // only a refused handshake (non-200) closes the EventSource for good.
105
+ if (connection.readyState !== EventSource.CLOSED)
106
+ return;
107
+ void isSessionEndedRejection(url).then((sessionEnded) => {
108
+ if (sessionEnded)
109
+ signalSessionEnded();
110
+ });
111
+ });
112
+ };
113
+ const ensureListenersForEntity = (entityName) => {
114
+ // skip: not connected yet, listeners get wired once ensureConnected runs
115
+ if (source === undefined)
116
+ return;
117
+ // skip: already wired for this entity
118
+ if (wiredEntities.has(entityName))
119
+ return;
120
+ source.addEventListener(entityName, (e) => {
121
+ const data = propertyOf(e, "data");
122
+ if (typeof data === "string")
123
+ handleEvent(data);
124
+ });
125
+ wiredEntities.add(entityName);
126
+ };
127
+ const closeIfEmpty = () => {
128
+ // skip: subscribers remain, connection still needed
129
+ if (subscribers.size > 0)
130
+ return;
131
+ // skip: already closed, nothing to close
132
+ if (source === undefined)
133
+ return;
134
+ source.close();
135
+ source = undefined;
136
+ wiredEntities.clear();
137
+ };
138
+ return (entityName, listener) => {
139
+ ensureConnected();
140
+ ensureListenersForEntity(entityName);
141
+ const sub = { entityName, listener };
142
+ subscribers.add(sub);
143
+ return () => {
144
+ subscribers.delete(sub);
145
+ closeIfEmpty();
146
+ };
147
+ };
148
+ }