@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,536 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown, ChevronRight, Plus, Search } from "lucide-react";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
4
+ import { resolveNavigation } from "../../headless/index.js";
5
+ import { lastSegment, toAppSchema, useLiveEvents, useNav, useTranslation } from "../../index.js";
6
+ import { KumikoLink } from "../app/nav.js";
7
+ import { useNavEntities, useNavProviders } from "../app/nav-providers-context.js";
8
+ import { NAV_ICONS } from "../icons.js";
9
+ import { cn } from "../lib/cn.js";
10
+ import { SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarInput, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, useSidebar, } from "../ui/sidebar.js";
11
+ import { resolveParentScreenId } from "./shell-breadcrumb.js";
12
+ import { useDispatchTarget } from "./target-resolver-stub.js";
13
+ import { parseTargetFromSearchParams } from "./target-url.js";
14
+ // Widened alias for the two lookup sites below, which index by the plain
15
+ // `string` icon key of the resolved NavNode/TreeAction tree — not the
16
+ // closed NavIconKey union NAV_ICONS itself is typed against.
17
+ const NAV_ICON_LOOKUP = NAV_ICONS;
18
+ const EMPTY_BADGES = new Map();
19
+ const NavBadgesContext = createContext(EMPTY_BADGES);
20
+ const NavFilterContext = createContext({ q: "", matches: () => true });
21
+ const ActiveScreenMarkerContext = createContext(undefined);
22
+ // Walks only the static `children` — provider-emitted nodes (treeNodeToNavNode
23
+ // below) never set `screen`, so they can't hide a match this walk would miss.
24
+ function treeContainsScreen(nodes, screenId) {
25
+ return nodes.some((n) => (n.screen !== undefined && lastSegment(n.screen) === screenId) ||
26
+ treeContainsScreen(n.children, screenId));
27
+ }
28
+ // Ein Knoten überlebt den Filter, wenn er selbst oder ein Nachfahre matcht.
29
+ // Provider-Kinder sind zur Filterzeit schon materialisiert (Provider-Knoten
30
+ // sind default-expanded → eager geladen), darum reicht die statische
31
+ // children-Rekursion. ponytail: ein zur Filterzeit noch NICHT geladener
32
+ // (user-zugeklappter) Provider wird über den force-expand in useNavNodeState
33
+ // aufgeklappt, lädt und filtert dann mit.
34
+ function subtreeMatches(node, matches) {
35
+ return matches(node.label) || node.children.some((c) => subtreeMatches(c, matches));
36
+ }
37
+ export function NavTree({ schema, user, testId, allowedNavQns, navBadges, }) {
38
+ const app = useMemo(() => toAppSchema(schema), [schema]);
39
+ const tree = useMemo(() => {
40
+ const source = buildNavRegistrySliceForApp(app, allowedNavQns);
41
+ return resolveNavigation({ source, ...(user !== undefined && { user }) });
42
+ }, [app, user, allowedNavQns]);
43
+ const nav = useNav();
44
+ const activeScreenId = nav.route?.screenId;
45
+ const activeEntityId = nav.route?.entityId;
46
+ // Same "no nav entry of its own" gap the breadcrumb closes (fw#2724):
47
+ // when the routed screen has no node in {tree}, fall back to its
48
+ // resolved parent so a detail/edit/form screen still orients the user.
49
+ const activeMarker = useMemo(() => {
50
+ if (activeScreenId === undefined)
51
+ return undefined;
52
+ const allScreens = app.features.flatMap((f) => f.screens);
53
+ const parentScreenId = resolveParentScreenId(allScreens, activeScreenId);
54
+ // A nav entry on an edit screen means "create"; an existing record (entityId
55
+ // in the route) belongs to its list, same rule as the breadcrumb.
56
+ if (activeEntityId !== undefined &&
57
+ parentScreenId !== undefined &&
58
+ treeContainsScreen(tree, parentScreenId)) {
59
+ return { screenId: parentScreenId, exact: false };
60
+ }
61
+ if (treeContainsScreen(tree, activeScreenId))
62
+ return { screenId: activeScreenId, exact: true };
63
+ return parentScreenId !== undefined ? { screenId: parentScreenId, exact: false } : undefined;
64
+ }, [tree, app.features, activeScreenId, activeEntityId]);
65
+ // Collapsed-Set: nur die explizit zugeklappten qualified-names. Default
66
+ // ist also "alles auf" — neue Features tauchen sofort offen auf, ohne
67
+ // dass der User erst klicken muss.
68
+ const [collapsed, setCollapsed] = useState(() => new Set());
69
+ const onToggle = useCallback((qn) => {
70
+ setCollapsed((prev) => {
71
+ const next = new Set(prev);
72
+ if (next.has(qn))
73
+ next.delete(qn);
74
+ else
75
+ next.add(qn);
76
+ return next;
77
+ });
78
+ }, []);
79
+ const t = useTranslation();
80
+ const { state: sidebarState } = useSidebar();
81
+ const [filter, setFilter] = useState("");
82
+ // The search box is only CSS-hidden (`group-data-[collapsible=icon]:hidden`)
83
+ // when the sidebar collapses to icon rail, not unmounted — without this,
84
+ // a stale filter re-applies invisibly on the next expand (fw#1816).
85
+ useEffect(() => {
86
+ if (sidebarState === "collapsed")
87
+ setFilter("");
88
+ }, [sidebarState]);
89
+ const q = filter.trim().toLowerCase();
90
+ const matches = useCallback((raw) => {
91
+ const label = raw.includes(".") ? t(raw) : raw;
92
+ return label.toLowerCase().includes(q);
93
+ }, [t, q]);
94
+ const navFilter = useMemo(() => ({ q, matches }), [q, matches]);
95
+ return (_jsx(ActiveScreenMarkerContext.Provider, { value: activeMarker, children: _jsx(NavFilterContext.Provider, { value: navFilter, children: _jsx(NavBadgesContext.Provider, { value: navBadges ?? EMPTY_BADGES, children: _jsxs("div", { "data-testid": testId, "data-kumiko-layout": "nav-tree", className: "flex w-full flex-col gap-3", children: [_jsxs("div", { className: "relative mb-0 group-data-[collapsible=icon]:hidden", children: [_jsx(Search, { "aria-hidden": "true", className: "pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 text-sidebar-muted" }), _jsx(SidebarInput, { className: "pl-8", value: filter, onChange: (e) => setFilter(e.target.value), placeholder: t("kumiko.nav.search"), "aria-label": t("kumiko.nav.search") })] }), tree.map((node) => isPureSection(node) ? (_jsx(NavSection, { node: node, collapsed: collapsed, onToggle: onToggle }, node.qualifiedName)) : (_jsx(SidebarMenu, { children: _jsx(NavMenuNode, { node: node, collapsed: collapsed, onToggle: onToggle }) }, node.qualifiedName)))] }) }) }) }));
96
+ }
97
+ // Runtime-Badge-Slot rechts am Label, gekeyt auf die bare nav-id. Kein
98
+ // Eintrag → nichts (silent, kein Layout-Shift). shrink-0 + die ml-auto
99
+ // schiebt ihn ans rechte Ende; das truncate-Label gibt nach.
100
+ // ponytail: nur an screen/target-Leaves verdrahtet, nicht an Container-
101
+ // Knoten (die tragen den ml-auto-Chevron) — Tier-Badge ist ein Leaf.
102
+ // Defense-in-depth (555/4): every current call site already guards on
103
+ // node.screen/node.target !== undefined before rendering <NavBadge>, so this
104
+ // early-return changes no current behavior — it's here so a future call
105
+ // site added without that guard can't silently show a leaf's badge on an
106
+ // unrelated container node that happens to share its last-segment.
107
+ function NavBadge({ node }) {
108
+ const badges = useContext(NavBadgesContext);
109
+ if (node.screen === undefined && node.target === undefined)
110
+ return null;
111
+ const badge = badges.get(lastSegment(node.qualifiedName));
112
+ if (badge === undefined)
113
+ return null;
114
+ return _jsx("span", { className: "ml-auto shrink-0", children: badge });
115
+ }
116
+ // Pure section = ein Gruppen-Header ohne eigenen Screen, der nur seine
117
+ // children gruppiert. Auf Top-Level wird daraus eine SidebarGroup mit
118
+ // togglebarem GroupLabel; ein Node mit Screen (auch wenn er children hat)
119
+ // ist dagegen ein klickbarer Menu-Eintrag.
120
+ function isPureSection(node) {
121
+ return (node.screen === undefined &&
122
+ node.target === undefined &&
123
+ node.provider !== true &&
124
+ node.createAction === undefined &&
125
+ node.children.length > 0);
126
+ }
127
+ // i18n-Key Konvention: enthält das label einen Punkt, durchs t() laufen
128
+ // lassen (Bundle kennt den Key → übersetzt, sonst bleibt der Key sichtbar
129
+ // = vergessene Übersetzung). Reine String-Labels bleiben unangetastet.
130
+ function useLabel(node) {
131
+ const t = useTranslation();
132
+ return node.label.includes(".") ? t(node.label) : node.label;
133
+ }
134
+ // Without an icon only the collapsed rail shows the initial letter; expanded,
135
+ // the label stands alone.
136
+ function NavLeadingIcon({ node, active, expanded = false, label, }) {
137
+ const iconKey = expanded && node.icon === "folder" ? "folder-open" : node.icon;
138
+ const isKnownIcon = iconKey !== undefined && Object.hasOwn(NAV_ICONS, iconKey);
139
+ const NavIcon = iconKey !== undefined && isKnownIcon ? NAV_ICON_LOOKUP[iconKey] : undefined;
140
+ useEffect(() => {
141
+ if (iconKey !== undefined && !isKnownIcon) {
142
+ // biome-ignore lint/suspicious/noConsole: diagnostic for unregistered nav icon keys
143
+ console.warn(`[kumiko] Nav entry "${node.qualifiedName}" references icon "${iconKey}", which is not ` +
144
+ `registered in NAV_ICONS — falling back to the dot indicator. Check the icon key spelling.`);
145
+ }
146
+ }, [iconKey, isKnownIcon, node.qualifiedName]);
147
+ if (NavIcon !== undefined)
148
+ return _jsx(NavIcon, { "aria-hidden": "true", className: "shrink-0" });
149
+ const initial = label?.trim().charAt(0).toUpperCase();
150
+ if (initial === undefined || initial === "")
151
+ return null;
152
+ return (_jsx("span", { "aria-hidden": "true", className: cn("hidden size-4 shrink-0 items-center justify-center font-medium text-xs group-data-[collapsible=icon]:flex", active ? "text-sidebar-accent-foreground" : "text-sidebar-foreground/70"), children: initial }));
153
+ }
154
+ // Stable-reference empty-list — sonst destabilisiert ein neues `[]` pro
155
+ // Render die useEffect-deps der SSE-Subscription.
156
+ const EMPTY_ENTITY_LIST = [];
157
+ // Active-Target-Vergleich: featureId + action exakt, args shallow-equal
158
+ // (heute nur primitives). Gespiegelt aus dem alten TreeNodeRenderer.
159
+ function targetsEqual(a, b) {
160
+ if (b === undefined)
161
+ return false;
162
+ if (a.featureId !== b.featureId || a.action !== b.action)
163
+ return false;
164
+ const aKeys = a.args ? Object.keys(a.args) : [];
165
+ const bKeys = b.args ? Object.keys(b.args) : [];
166
+ if (aKeys.length !== bKeys.length)
167
+ return false;
168
+ for (const k of aKeys) {
169
+ if (a.args?.[k] !== b.args?.[k])
170
+ return false;
171
+ }
172
+ return true;
173
+ }
174
+ // Adapter: provider-emittierte TreeNodes → renderbare NavNodes. Synthetische
175
+ // QN aus parent-QN + idx-label (stabil über Emit-Order). Statische children
176
+ // rekursiv. ponytail: eine TreeChildrenSubscribe-Function als children wird
177
+ // als Leaf behandelt — nested-dynamic-Provider nutzt heute kein Consumer;
178
+ // upgrade-path = synthetische QN registrieren wenn einer sie emittet.
179
+ function treeNodeToNavNode(tn, parentQn, idx) {
180
+ const qualifiedName = `${parentQn}/${idx}-${tn.label}`;
181
+ const children = Array.isArray(tn.children)
182
+ ? tn.children.map((c, i) => treeNodeToNavNode(c, qualifiedName, i))
183
+ : [];
184
+ return {
185
+ qualifiedName,
186
+ label: tn.label,
187
+ order: idx,
188
+ children,
189
+ ...(tn.icon !== undefined && { icon: tn.icon }),
190
+ ...(tn.target !== undefined && { target: tn.target }),
191
+ ...(tn.actions !== undefined && { actions: tn.actions }),
192
+ ...(tn.createAction !== undefined && { createAction: tn.createAction }),
193
+ };
194
+ }
195
+ // Lazy provider-children + SSE-Live-Refresh für einen `provider: true`-Knoten.
196
+ // Subscribe läuft erst wenn der Knoten ausgeklappt ist (enabled); die
197
+ // treeEntities-Liste (per QN) re-fired den Provider bei Entity-Events → neu
198
+ // erstellte Knoten erscheinen live. Logik gespiegelt aus VisualTree.ProviderBranch.
199
+ function useNavProviderChildren(qn, enabled) {
200
+ const providers = useNavProviders();
201
+ const entitiesMap = useNavEntities();
202
+ const subscribeLive = useLiveEvents();
203
+ const provider = providers.get(qn);
204
+ const entities = entitiesMap.get(qn) ?? EMPTY_ENTITY_LIST;
205
+ const [raw, setRaw] = useState(null);
206
+ const [error, setError] = useState(null);
207
+ const [attempt, setAttempt] = useState(0);
208
+ useEffect(() => {
209
+ if (!enabled || entities.length === 0)
210
+ return;
211
+ const unsubs = entities.map((e) => subscribeLive(e, () => setAttempt((n) => n + 1)));
212
+ return () => {
213
+ for (const u of unsubs)
214
+ u();
215
+ };
216
+ }, [enabled, entities, subscribeLive]);
217
+ // `attempt` triggert SSE-Refresh + erneutes Subscribe — Biome sieht es
218
+ // nicht im body, semantisch ist es der re-fire-Trigger.
219
+ // biome-ignore lint/correctness/useExhaustiveDependencies: attempt = re-fire trigger
220
+ useEffect(() => {
221
+ if (!enabled) {
222
+ setRaw(null);
223
+ setError(null);
224
+ return;
225
+ }
226
+ if (provider === undefined) {
227
+ setError("Kein nav-provider registriert.");
228
+ return;
229
+ }
230
+ setError(null);
231
+ setRaw(null);
232
+ try {
233
+ const subscribe = provider();
234
+ try {
235
+ return subscribe(setRaw, (e) => setError(e instanceof Error ? e.message : String(e)));
236
+ }
237
+ catch (e) {
238
+ setError(e instanceof Error ? e.message : "Subscribe fehlgeschlagen.");
239
+ }
240
+ }
241
+ catch (e) {
242
+ setError(e instanceof Error ? e.message : "Provider-Init fehlgeschlagen.");
243
+ }
244
+ return undefined;
245
+ }, [enabled, provider, attempt]);
246
+ const nodes = useMemo(() => (raw === null ? null : raw.map((tn, i) => treeNodeToNavNode(tn, qn, i))), [raw, qn]);
247
+ return { nodes, error };
248
+ }
249
+ // Geteilte Knoten-Logik für Top-Level- (NavMenuNode) und Sub-Knoten
250
+ // (NavSubNode): Label, Expand/Collapse, active-State (screen ODER target),
251
+ // und die lazy Provider-Children. Die zwei Renderer unterscheiden sich nur
252
+ // in den shadcn-Primitives, nicht in dieser Logik.
253
+ function useNavNodeState(node, collapsed) {
254
+ const nav = useNav();
255
+ const { q: filterQ, matches } = useContext(NavFilterContext);
256
+ const filterActive = filterQ !== "";
257
+ const displayLabel = useLabel(node);
258
+ const isCollapsed = collapsed.has(node.qualifiedName);
259
+ const expandable = node.children.length > 0 || node.provider === true;
260
+ // Aktiver Filter klappt jeden Container auf, damit Treffer unter zugeklappten
261
+ // Ordnern sichtbar werden (und Provider-Kinder nachladen).
262
+ const isExpanded = expandable && (!isCollapsed || filterActive);
263
+ const { nodes: providerChildren, error: providerError } = useNavProviderChildren(node.qualifiedName, node.provider === true && isExpanded);
264
+ const activeTarget = useMemo(() => parseTargetFromSearchParams(nav.searchParams), [nav.searchParams]);
265
+ const activeMarker = useContext(ActiveScreenMarkerContext);
266
+ const screenActive = node.screen !== undefined &&
267
+ activeMarker !== undefined &&
268
+ lastSegment(node.screen) === activeMarker.screenId;
269
+ const screenIsExactMatch = screenActive && activeMarker?.exact === true;
270
+ const targetActive = node.target !== undefined && targetsEqual(node.target, activeTarget);
271
+ const childNodes = node.provider === true ? (providerChildren ?? []) : node.children;
272
+ const visibleChildNodes = filterActive
273
+ ? childNodes.filter((c) => subtreeMatches(c, matches))
274
+ : childNodes;
275
+ return {
276
+ displayLabel,
277
+ isCollapsed,
278
+ expandable,
279
+ isExpanded,
280
+ active: screenActive || targetActive,
281
+ ariaCurrent: screenIsExactMatch || targetActive,
282
+ childNodes: visibleChildNodes,
283
+ // Ein gefilterter Knoten verschwindet nur, wenn weder er selbst noch ein
284
+ // sichtbar gebliebenes Kind matcht — sonst bliebe ein leerer Ordner-Header.
285
+ hidden: filterActive && !matches(node.label) && visibleChildNodes.length === 0,
286
+ providerLoading: node.provider === true && isExpanded && providerChildren === null,
287
+ providerError,
288
+ workspaceId: nav.route?.workspaceId,
289
+ };
290
+ }
291
+ // Action icon lookup: registered NAV_ICONS key → Lucide icon, otherwise the
292
+ // raw string as text (provider convention, no boot-fail on an unknown key).
293
+ function ActionGlyph({ icon }) {
294
+ const Icon = Object.hasOwn(NAV_ICONS, icon) ? NAV_ICON_LOOKUP[icon] : undefined;
295
+ if (Icon !== undefined)
296
+ return _jsx(Icon, { "aria-hidden": true, className: "size-3.5" });
297
+ return (_jsx("span", { "aria-hidden": true, className: "text-xs", children: icon }));
298
+ }
299
+ // Boot validates screen XOR target for NavDefinition actions, but a
300
+ // provider-emitted TreeNode reaches here without going through boot — warn
301
+ // once per label instead of rendering a dead button.
302
+ const warnedTreeActionLabels = new Set();
303
+ function warnTreeActionMisconfigured(label, problem) {
304
+ const key = `${label}:${problem}`;
305
+ // skip: already warned for this label and problem — suppresses the repeat, not the warning itself.
306
+ if (warnedTreeActionLabels.has(key))
307
+ return;
308
+ warnedTreeActionLabels.add(key);
309
+ // biome-ignore lint/suspicious/noConsole: dev-warning for a setup error
310
+ console.warn(`[kumiko] Nav action "${label}" ${problem}`);
311
+ }
312
+ // An action button carries screen XOR target (see TreeAction) — `screen`
313
+ // renders a KumikoLink to the route, `target` a dispatch button. Same
314
+ // classes/size either way, only the element changes.
315
+ function TreeActionControl({ action, ariaLabel, className, dispatch, workspaceId, children, }) {
316
+ // Authored actions are typed screen-XOR-target, but schemas can arrive
317
+ // untyped over the wire — read both fields to stay defensive at runtime.
318
+ const loose = action;
319
+ if (loose.screen !== undefined && loose.target !== undefined) {
320
+ warnTreeActionMisconfigured(action.label, 'has both "screen" and "target" set — using "screen", "target" is ignored.');
321
+ }
322
+ if (loose.screen !== undefined) {
323
+ const screenId = lastSegment(loose.screen);
324
+ return (_jsx(KumikoLink, { to: { ...(workspaceId !== undefined && { workspaceId }), screenId }, "aria-label": ariaLabel, className: className, onClick: (e) => e.stopPropagation(), children: children }));
325
+ }
326
+ if (loose.target !== undefined) {
327
+ const target = loose.target;
328
+ return (_jsx("button", { type: "button", "aria-label": ariaLabel, className: className, onClick: (e) => {
329
+ e.stopPropagation();
330
+ dispatch(target);
331
+ }, children: children }));
332
+ }
333
+ warnTreeActionMisconfigured(action.label, 'has neither "screen" nor "target" set — it will not render.');
334
+ return null;
335
+ }
336
+ // Hover-actions + the "+" affordance, positioned absolute right.
337
+ // createAction is persistent (a user-visible "+"), the other actions only
338
+ // show on hover. stopPropagation so the row click (toggle/dispatch) doesn't
339
+ // also fire. `expandable` comes from useNavNodeState (s.expandable) instead
340
+ // of being re-derived here — a node without a chevron doesn't need the
341
+ // right-7 gap, otherwise the "+" hangs 28px off the edge.
342
+ function NodeActions({ node, expandable, workspaceId, }) {
343
+ const dispatch = useDispatchTarget();
344
+ const t = useTranslation();
345
+ const create = node.createAction;
346
+ const actions = node.actions ?? [];
347
+ if (create === undefined && actions.length === 0)
348
+ return null;
349
+ const label = (s) => (s.includes(".") ? t(s) : s);
350
+ const btn = "flex size-5 items-center justify-center rounded-md text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground";
351
+ const hover = "opacity-0 group-hover/menu-item:opacity-100 group-hover/menu-sub-item:opacity-100 group-focus-within/menu-item:opacity-100";
352
+ return (_jsxs("div", { className: cn("absolute top-1 flex items-center gap-0.5", expandable ? "right-7" : "right-1"), children: [create !== undefined && (_jsx(TreeActionControl, { action: create, ariaLabel: label(create.label), className: btn, dispatch: dispatch, workspaceId: workspaceId, children: _jsx(Plus, { className: "size-3.5" }) })), actions.map((a) => (_jsx(TreeActionControl, { action: a, ariaLabel: label(a.label), className: cn(btn, hover), dispatch: dispatch, workspaceId: workspaceId, children: _jsx(ActionGlyph, { icon: a.icon }) }, a.label)))] }));
353
+ }
354
+ // Status-Zeilen innerhalb einer Provider-Sub-Liste (lazy-loading / error).
355
+ function ProviderStatus({ loading, error, }) {
356
+ if (error !== null) {
357
+ return _jsx("li", { className: "px-2 py-1 text-xs text-destructive", children: error });
358
+ }
359
+ if (loading) {
360
+ return _jsx("li", { className: "px-2 py-1 text-xs text-sidebar-muted italic", children: "L\u00E4dt \u2026" });
361
+ }
362
+ return null;
363
+ }
364
+ // Top-Level-Section: SidebarGroup mit STATISCHEM Label (sidebar-07-Muster —
365
+ // "Platform"/"Projects" sind feste Überschriften, keine Toggles). Collapse
366
+ // gehört auf Items MIT children, nicht auf die Section selbst.
367
+ function NavSection({ node, collapsed, onToggle }) {
368
+ const displayLabel = useLabel(node);
369
+ const { q, matches } = useContext(NavFilterContext);
370
+ // Provider-Kinder (statisch leer bis geladen) halten die Section offen — ihr
371
+ // Match lässt sich hier nicht statisch prüfen; der Knoten selbst self-hidet.
372
+ if (q !== "" &&
373
+ !matches(node.label) &&
374
+ !node.children.some((c) => c.provider === true || subtreeMatches(c, matches))) {
375
+ return null;
376
+ }
377
+ return (_jsxs(SidebarGroup, { children: [_jsx(SidebarGroupLabel, { children: displayLabel }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: node.children.map((child) => (_jsx(NavMenuNode, { node: child, collapsed: collapsed, onToggle: onToggle }, child.qualifiedName))) }) })] }));
378
+ }
379
+ // Ein Menu-Eintrag (Top-Level-Item oder Section-Child). Drei Klick-Modi am
380
+ // EINEN Knoten: `screen` → Route-Link (KumikoLink), `target` → EditorPanel-
381
+ // Dispatch (useDispatchTarget), keins → Container/Provider-Knoten der nur
382
+ // auf-/zuklappt. Provider-Knoten laden ihre Children lazy + SSE-live. Der
383
+ // Collapse-Chevron sitzt als SidebarMenuAction daneben (separater Button,
384
+ // sonst <button> im <a> = invalides HTML); createAction/actions als
385
+ // absolute NodeActions links davon.
386
+ function NavMenuNode({ node, collapsed, onToggle }) {
387
+ const t = useTranslation();
388
+ const dispatch = useDispatchTarget();
389
+ const s = useNavNodeState(node, collapsed);
390
+ if (s.hidden)
391
+ return null;
392
+ const sub = s.isExpanded ? (_jsxs(SidebarMenuSub, { children: [_jsx(ProviderStatus, { loading: s.providerLoading, error: s.providerError }), s.childNodes.map((child) => (_jsx(NavSubNode, { node: child, collapsed: collapsed, onToggle: onToggle }, child.qualifiedName)))] })) : null;
393
+ const chevron = s.expandable ? (_jsx(SidebarMenuAction, { "aria-label": t(s.isExpanded ? "kumiko.nav.collapse" : "kumiko.nav.expand"), "aria-expanded": s.isExpanded, onClick: () => onToggle(node.qualifiedName), children: s.isExpanded ? _jsx(ChevronDown, {}) : _jsx(ChevronRight, {}) })) : null;
394
+ if (node.screen !== undefined) {
395
+ const screenId = lastSegment(node.screen);
396
+ return (_jsxs(SidebarMenuItem, { children: [_jsx(SidebarMenuButton, { asChild: true, isActive: s.active, tooltip: s.displayLabel, children: _jsxs(KumikoLink, { to: { ...(s.workspaceId !== undefined && { workspaceId: s.workspaceId }), screenId }, ...(s.ariaCurrent && { "aria-current": "page" }), children: [_jsx(NavLeadingIcon, { node: node, active: s.active, label: s.displayLabel }), _jsx("span", { className: "min-w-0 truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel }), _jsx(NavBadge, { node: node })] }) }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), chevron, sub] }));
397
+ }
398
+ if (node.target !== undefined) {
399
+ const target = node.target;
400
+ return (_jsxs(SidebarMenuItem, { children: [_jsxs(SidebarMenuButton, { isActive: s.active, tooltip: s.displayLabel, onClick: () => dispatch(target), children: [_jsx(NavLeadingIcon, { node: node, active: s.active, label: s.displayLabel }), _jsx("span", { className: "min-w-0 truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel }), _jsx(NavBadge, { node: node })] }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), chevron, sub] }));
401
+ }
402
+ return (_jsxs(SidebarMenuItem, { children: [_jsxs(SidebarMenuButton, { onClick: () => {
403
+ if (s.expandable)
404
+ onToggle(node.qualifiedName);
405
+ }, ...(s.expandable && { "aria-expanded": s.isExpanded }), children: [_jsx(NavLeadingIcon, { node: node, active: false, expanded: s.isExpanded, label: s.displayLabel }), _jsx("span", { className: "truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel }), s.expandable &&
406
+ (s.isExpanded ? (_jsx(ChevronDown, { className: "ml-auto" })) : (_jsx(ChevronRight, { className: "ml-auto" })))] }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), sub] }));
407
+ }
408
+ // Verschachtelte Einträge (innerhalb SidebarMenuSub). Gleiche drei Klick-Modi
409
+ // wie NavMenuNode, nur mit den Sub-Primitives. Folder-/Container-Knoten (kein
410
+ // screen/target) klappen auf — Provider-Children + tiefere Statics rekursiv.
411
+ // Non-Link-Klickziele brauchen asChild+<button> (SidebarMenuSubButton ist ein
412
+ // <a>); der Chevron sitzt als eigener absoluter Button (kein Button-im-<a>).
413
+ function NavSubNode({ node, collapsed, onToggle }) {
414
+ const t = useTranslation();
415
+ const dispatch = useDispatchTarget();
416
+ const s = useNavNodeState(node, collapsed);
417
+ if (s.hidden)
418
+ return null;
419
+ // Kinder in ein eigenes SidebarMenuSub (<ul>) — ein Sub-Item ist ein <li>,
420
+ // verschachtelte <li> ohne <ul> dazwischen sind invalides HTML (nested
421
+ // Content-Ordner: Marketing/ > Block). Der <ul> trägt zugleich die
422
+ // Einrück-/Border-Stufe pro Tiefe.
423
+ const deeper = s.isExpanded ? (_jsxs(SidebarMenuSub, { children: [_jsx(ProviderStatus, { loading: s.providerLoading, error: s.providerError }), s.childNodes.map((child) => (_jsx(NavSubNode, { node: child, collapsed: collapsed, onToggle: onToggle }, child.qualifiedName)))] })) : null;
424
+ const chevron = s.expandable ? (_jsx("button", { type: "button", "aria-label": t(s.isExpanded ? "kumiko.nav.collapse" : "kumiko.nav.expand"), "aria-expanded": s.isExpanded, onClick: (e) => {
425
+ e.stopPropagation();
426
+ onToggle(node.qualifiedName);
427
+ }, className: "absolute top-1 right-1 flex size-5 items-center justify-center rounded-md text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", children: s.isExpanded ? _jsx(ChevronDown, { className: "size-3.5" }) : _jsx(ChevronRight, { className: "size-3.5" }) })) : null;
428
+ if (node.screen !== undefined) {
429
+ const screenId = lastSegment(node.screen);
430
+ return (_jsxs(SidebarMenuSubItem, { children: [_jsx(SidebarMenuSubButton, { asChild: true, isActive: s.active, children: _jsxs(KumikoLink, { to: { ...(s.workspaceId !== undefined && { workspaceId: s.workspaceId }), screenId }, ...(s.ariaCurrent && { "aria-current": "page" }), children: [_jsx(NavLeadingIcon, { node: node, active: s.active, label: s.displayLabel }), _jsx("span", { className: "min-w-0 truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel }), _jsx(NavBadge, { node: node })] }) }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), chevron, deeper] }));
431
+ }
432
+ if (node.target !== undefined) {
433
+ const target = node.target;
434
+ return (_jsxs(SidebarMenuSubItem, { children: [_jsx(SidebarMenuSubButton, { asChild: true, isActive: s.active, children: _jsxs("button", { type: "button", onClick: () => dispatch(target), children: [_jsx(NavLeadingIcon, { node: node, active: s.active, label: s.displayLabel }), _jsx("span", { className: "min-w-0 truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel }), _jsx(NavBadge, { node: node })] }) }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), chevron, deeper] }));
435
+ }
436
+ return (_jsxs(SidebarMenuSubItem, { children: [_jsx(SidebarMenuSubButton, { asChild: true, children: _jsxs("button", { type: "button", onClick: () => {
437
+ if (s.expandable)
438
+ onToggle(node.qualifiedName);
439
+ }, children: [_jsx(NavLeadingIcon, { node: node, active: false, expanded: s.isExpanded, label: s.displayLabel }), _jsx("span", { className: "truncate group-data-[collapsible=icon]:hidden", title: s.displayLabel, children: s.displayLabel })] }) }), _jsx(NodeActions, { node: node, expandable: s.expandable, workspaceId: s.workspaceId }), chevron, deeper] }));
440
+ }
441
+ // Backwards-kompatibler Single-Feature-Builder. Convenience-Wrapper um
442
+ // buildNavRegistrySliceForApp — bestehende Tests die direkt mit
443
+ // FeatureSchema rufen brauchen keine Änderung.
444
+ export function buildNavRegistrySlice(schema, allowedNavQns) {
445
+ return buildNavRegistrySliceForApp(toAppSchema(schema), allowedNavQns);
446
+ }
447
+ // Multi-Feature-Variante. Iteriert alle Features, qualifiziert pro
448
+ // Feature mit dem eigenen featureName. Reihenfolge: Features in der
449
+ // AppSchema-Ordnung, navs in der vom Feature deklarierten Reihenfolge.
450
+ //
451
+ // Cross-Feature-Workspaces sind hier nativ unterstützt — `navMembers`
452
+ // referenzieren QNs, der Filter trifft die jeweils richtigen Einträge
453
+ // egal in welchem Feature sie deklariert sind.
454
+ export function buildNavRegistrySliceForApp(app, allowedNavQns) {
455
+ const screenAccessByQn = buildScreenAccessMap(app);
456
+ const qualified = [];
457
+ for (const feature of app.features) {
458
+ for (const n of feature.navs ?? []) {
459
+ const screen = n.screen !== undefined ? qualifyScreenId(feature.featureName, n.screen) : undefined;
460
+ // Nav entries without their own `access` inherit the referenced
461
+ // screen's access rule — otherwise a broader workspace/parent access
462
+ // lets a role see a nav entry whose target screen 403s them (#1099).
463
+ // An explicit `n.access` always wins; inheritance only fills the gap.
464
+ const inheritedAccess = n.access ?? (screen !== undefined ? screenAccessByQn.get(screen) : undefined);
465
+ qualified.push({
466
+ ...n,
467
+ id: qualifyNavId(feature.featureName, n.id),
468
+ ...(n.parent !== undefined && { parent: qualifyNavId(feature.featureName, n.parent) }),
469
+ ...(screen !== undefined && { screen }),
470
+ ...(inheritedAccess !== undefined && { access: inheritedAccess }),
471
+ });
472
+ }
473
+ }
474
+ const existing = dropNavsWithNonexistentParent(qualified);
475
+ // Workspace filter: drop nav entries whose qualified id isn't in the
476
+ // allow-set. A child whose parent gets dropped surfaces as a top-level
477
+ // entry — the workspace owner should list parents explicitly if they
478
+ // want the grouping preserved.
479
+ const filtered = allowedNavQns !== undefined ? existing.filter((n) => allowedNavQns.has(n.id)) : existing;
480
+ const allowedQnSet = new Set(filtered.map((n) => n.id));
481
+ const topLevel = [];
482
+ const byParentMap = new Map();
483
+ for (const nav of filtered) {
484
+ // Treat the entry as top-level when its parent isn't visible — keeps
485
+ // children visible after filtering, even if the workspace omits the
486
+ // parent group from its members list.
487
+ const hasVisibleParent = nav.parent !== undefined && allowedQnSet.has(nav.parent);
488
+ if (hasVisibleParent && nav.parent !== undefined) {
489
+ const list = byParentMap.get(nav.parent) ?? [];
490
+ list.push(nav);
491
+ byParentMap.set(nav.parent, list);
492
+ }
493
+ else {
494
+ topLevel.push(nav);
495
+ }
496
+ }
497
+ return {
498
+ topLevel,
499
+ byParent: (parent) => byParentMap.get(parent) ?? [],
500
+ };
501
+ }
502
+ // A parent absent from the (role-projected) schema is not a workspace
503
+ // filter result: the child must not leak to the top level. Transitive.
504
+ // Parents removed by an allowlist (filterAppSchemaNavsByAllowlist) are not
505
+ // absent here: that helper already lifted their children to the top level.
506
+ function dropNavsWithNonexistentParent(navs) {
507
+ let current = [...navs];
508
+ for (;;) {
509
+ const ids = new Set(current.map((n) => n.id));
510
+ const kept = current.filter((n) => n.parent === undefined || ids.has(n.parent));
511
+ if (kept.length === current.length)
512
+ return current;
513
+ current = kept;
514
+ }
515
+ }
516
+ // Exported (not part of the package's public index.ts surface) so other
517
+ // modules under layout/ — e.g. filter-app-schema-navs.ts — qualify nav ids
518
+ // the same way instead of keeping a second copy in sync.
519
+ export function qualifyNavId(feature, id) {
520
+ return id.includes(":nav:") ? id : `${feature}:nav:${id}`;
521
+ }
522
+ function qualifyScreenId(feature, id) {
523
+ return id.includes(":screen:") ? id : `${feature}:screen:${id}`;
524
+ }
525
+ function buildScreenAccessMap(app) {
526
+ const map = new Map();
527
+ for (const feature of app.features) {
528
+ for (const s of feature.screens) {
529
+ map.set(qualifyScreenId(feature.featureName, s.id), s.access);
530
+ }
531
+ }
532
+ return map;
533
+ }
534
+ // `lastSegment` lebt jetzt in @cosmicdrift/kumiko-renderer (./app/qn) — eine
535
+ // Quelle, beide Pakete teilen sie.
536
+ export { lastSegment };
@@ -0,0 +1,25 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ActionMenuItemSpec } from "../../index.ts";
3
+ type SlotElement = HTMLElement | null;
4
+ export type PageHeaderSlot = {
5
+ readonly statusElement: SlotElement;
6
+ readonly actionsElement: SlotElement;
7
+ readonly setStatusElement: (node: SlotElement) => void;
8
+ readonly setActionsElement: (node: SlotElement) => void;
9
+ readonly title: string | undefined;
10
+ readonly setTitle: (title: string | undefined) => void;
11
+ readonly recordTitle: string | undefined;
12
+ readonly setRecordTitle: (recordTitle: string | undefined) => void;
13
+ readonly breadcrumbHidden: boolean;
14
+ readonly setBreadcrumbHidden: (hidden: boolean) => void;
15
+ readonly breadcrumbScreenId: string | undefined;
16
+ readonly setBreadcrumbScreenId: (screenId: string | undefined) => void;
17
+ readonly overflowItems: readonly ActionMenuItemSpec[] | undefined;
18
+ readonly setOverflowItems: (items: readonly ActionMenuItemSpec[] | undefined) => void;
19
+ };
20
+ export declare function usePageHeaderSlot(): PageHeaderSlot | null;
21
+ /** Shells wrap ShellHeader and the content area in it. */
22
+ export declare function PageHeaderSlotProvider({ children }: {
23
+ readonly children: ReactNode;
24
+ }): ReactNode;
25
+ export {};
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Two portal targets (status, actions) plus a title override and a record title inside the
3
+ // ShellHeader, filled by the PageHeader primitive of whatever screen renders
4
+ // below it.
5
+ //
6
+ // Why a slot: the header sits above the content and is owned by the shell; a
7
+ // screen can only reach it through a portal. Same inversion as SidebarPanel.
8
+ import { createContext, useContext, useMemo, useState } from "react";
9
+ import { PageHeaderCompactProvider, PageHeaderSlotAvailableProvider, } from "../../index.js";
10
+ import { useIsNarrowViewport } from "../primitives/use-narrow-viewport.js";
11
+ const PageHeaderSlotContext = createContext(null);
12
+ export function usePageHeaderSlot() {
13
+ return useContext(PageHeaderSlotContext);
14
+ }
15
+ /** Shells wrap ShellHeader and the content area in it. */
16
+ export function PageHeaderSlotProvider({ children }) {
17
+ const [statusElement, setStatusElement] = useState(null);
18
+ const [actionsElement, setActionsElement] = useState(null);
19
+ const [title, setTitle] = useState(undefined);
20
+ const [recordTitle, setRecordTitle] = useState(undefined);
21
+ const [breadcrumbHidden, setBreadcrumbHidden] = useState(false);
22
+ const [breadcrumbScreenId, setBreadcrumbScreenId] = useState(undefined);
23
+ const [overflowItems, setOverflowItems] = useState(undefined);
24
+ const compact = useIsNarrowViewport();
25
+ const value = useMemo(() => ({
26
+ statusElement,
27
+ actionsElement,
28
+ setStatusElement,
29
+ setActionsElement,
30
+ title,
31
+ setTitle,
32
+ recordTitle,
33
+ setRecordTitle,
34
+ breadcrumbHidden,
35
+ setBreadcrumbHidden,
36
+ breadcrumbScreenId,
37
+ setBreadcrumbScreenId,
38
+ overflowItems,
39
+ setOverflowItems,
40
+ }), [
41
+ statusElement,
42
+ actionsElement,
43
+ title,
44
+ recordTitle,
45
+ breadcrumbHidden,
46
+ breadcrumbScreenId,
47
+ overflowItems,
48
+ ]);
49
+ return (_jsx(PageHeaderSlotContext.Provider, { value: value, children: _jsx(PageHeaderSlotAvailableProvider, { value: true, children: _jsx(PageHeaderCompactProvider, { value: compact, children: children }) }) }));
50
+ }
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from "react";
2
+ import { type MenuItemDef } from "../primitives/action-menu.tsx";
3
+ export type ProfileMenuItem = MenuItemDef;
4
+ export type ProfileMenuProps = {
5
+ readonly user: {
6
+ readonly id: string;
7
+ /** Display-Name oder Email — Quelle für Initials. */
8
+ readonly label: string;
9
+ };
10
+ readonly items: readonly ProfileMenuItem[];
11
+ readonly testId?: string;
12
+ };
13
+ export declare function ProfileMenu({ user, items, testId }: ProfileMenuProps): ReactNode;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ActionMenu } from "../primitives/action-menu.js";
3
+ import { Avatar } from "./avatar.js";
4
+ export function ProfileMenu({ user, items, testId }) {
5
+ return (_jsx(ActionMenu, { trigger: _jsx(Avatar, { id: user.id, label: user.label, size: "md" }), triggerLabel: `Open ${user.label} menu`, items: items, align: "end", testId: testId ?? "profile-menu-trigger" }));
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { ScreenDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
2
+ export type BreadcrumbCrumb = {
3
+ readonly label: string;
4
+ readonly screenId?: string;
5
+ };
6
+ export declare function screenTitleKey(screenShortId: string): string;
7
+ /** The short id of {screenId}'s parent list screen, resolved via the same
8
+ * logic as `resolveDetailBreadcrumb` — `undefined` when nothing resolves
9
+ * (unknown screen, or no explicit/heuristic parent). */
10
+ export declare function resolveParentScreenId(screens: readonly ScreenDefinition[], screenId: string): string | undefined;
11
+ export declare function resolveDetailBreadcrumb(screens: readonly ScreenDefinition[], detailScreenId: string, t: (key: string) => string): readonly BreadcrumbCrumb[] | undefined;