@burojs/ui-shadcn 0.5.0 → 0.7.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 (172) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-BWSAS7k2.js +143 -0
  3. package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
  4. package/dist/bulk-summary-CrEij5vq.cjs +143 -0
  5. package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
  6. package/dist/candidates-relation-picker-CS_JZ4Fd.js +31 -0
  7. package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
  8. package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
  9. package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
  10. package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
  13. package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +951 -605
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +618 -332
  33. package/dist/index.d.ts +618 -332
  34. package/dist/index.js +863 -522
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
  37. package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
  38. package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/label-66W1qbke.js.map +1 -1
  41. package/dist/label-w6JYf2cF.cjs.map +1 -1
  42. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
  43. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  44. package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
  45. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  46. package/dist/markdown/index.d.cts +2 -3
  47. package/dist/markdown/index.d.ts +2 -3
  48. package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
  49. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  50. package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  51. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  52. package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
  53. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  54. package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
  55. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  56. package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
  57. package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  58. package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  59. package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  60. package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
  61. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  62. package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  63. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  64. package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
  65. package/dist/related-rows--uP2iKQC.js.map +1 -0
  66. package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
  67. package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
  68. package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
  69. package/dist/relation-create-C43cdPQt.js.map +1 -0
  70. package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
  71. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  72. package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
  73. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  74. package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
  75. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  76. package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
  77. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  78. package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
  79. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  80. package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
  81. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  82. package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
  83. package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
  84. package/dist/relation-select-B8Fq4o-t.js +48 -0
  85. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  86. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  87. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  88. package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
  89. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  90. package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
  91. package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
  92. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  93. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  94. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  95. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  96. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  97. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  98. package/dist/success-notices-LDEgEkc4.js +41 -0
  99. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  100. package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
  101. package/dist/toaster-BbByOytP.cjs.map +1 -0
  102. package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
  103. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  104. package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  105. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  106. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  107. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  108. package/dist/use-theme-DFlpXils.cjs.map +1 -1
  109. package/dist/use-theme-dnE3Od3I.js.map +1 -1
  110. package/package.json +11 -11
  111. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  112. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  113. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  114. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  115. package/dist/candidates-gate-CX2PBu6l.js +0 -63
  116. package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
  117. package/dist/candidates-gate-DiFI52r7.cjs +0 -70
  118. package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
  119. package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
  120. package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
  121. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
  122. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
  123. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
  124. package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
  125. package/dist/dialog-B_rt9S1c.js.map +0 -1
  126. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  127. package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
  128. package/dist/filter-builder-DOnaXNfP.js.map +0 -1
  129. package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
  130. package/dist/flow-runtime-D8h99FSg.js.map +0 -1
  131. package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
  132. package/dist/flow-screen-CmbARSqg.js.map +0 -1
  133. package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
  134. package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
  135. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
  136. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
  137. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
  138. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
  139. package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
  140. package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
  141. package/dist/peek-filter-CnqHwTMU.cjs +0 -51
  142. package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
  143. package/dist/peek-filter-ErLfO153.js +0 -28
  144. package/dist/peek-filter-ErLfO153.js.map +0 -1
  145. package/dist/place-node--GdsP688.js.map +0 -1
  146. package/dist/place-node-SIp4XYQH.cjs.map +0 -1
  147. package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
  148. package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
  149. package/dist/related-rows-B4levHcR.js.map +0 -1
  150. package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
  151. package/dist/relation-create-BOaUe12G.cjs +0 -362
  152. package/dist/relation-create-BOaUe12G.cjs.map +0 -1
  153. package/dist/relation-create-Bg-eEJLs.js +0 -337
  154. package/dist/relation-create-Bg-eEJLs.js.map +0 -1
  155. package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
  156. package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
  157. package/dist/relation-picker-BAY77ivO.js.map +0 -1
  158. package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
  159. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
  160. package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
  161. package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
  162. package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
  163. package/dist/search-access-B7G5oR8n.cjs.map +0 -1
  164. package/dist/search-access-CURsWn-U.js.map +0 -1
  165. package/dist/shell-BPlk8hEa.js +0 -205
  166. package/dist/shell-BPlk8hEa.js.map +0 -1
  167. package/dist/shell-DxhlsPwm.cjs +0 -271
  168. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  169. package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
  170. package/dist/toaster-CfCRLgR-.js.map +0 -1
  171. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
  172. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,24 +1,23 @@
1
1
  import { c as scrollToDocAnchor, l as cn, n as useTheme, s as readDocHash, t as THEME_CHOICES } from "./use-theme-dnE3Od3I.js";
2
- import { a as DEFAULT_LANGUAGE_STORAGE_KEY, c as saveLanguage, d as useLinkComponent, f as Popover, g as PopoverTrigger, h as PopoverContent, i as DEFAULT_LANGUAGE, l as saveLocale, m as PopoverClose, n as toast, o as loadLanguage, p as PopoverAnchor, r as toFriendlyError, s as loadLocale, t as Toaster, u as LinkComponentProvider } from "./toaster-CfCRLgR-.js";
3
- import { A as hasIcon, B as DropdownMenuRadioItem, F as DropdownMenuGroup, G as DropdownMenuSubTrigger, H as DropdownMenuShortcut, I as DropdownMenuItem, J as TooltipContent, K as DropdownMenuTrigger, L as DropdownMenuLabel, M as DropdownMenu, N as DropdownMenuCheckboxItem, O as useLoaded, P as DropdownMenuContent, R as DropdownMenuPortal, U as DropdownMenuSub, V as DropdownMenuSeparator, W as DropdownMenuSubContent, X as TooltipTrigger, Y as TooltipProvider, Z as DocInfo, _ as FlowBoundary, a as modeForSurface, c as useListSlot, f as FlowActionButton, j as registerIcons, k as Icon, l as isExternalHref, n as ActionButton, o as canonicalResourceName, q as Tooltip, r as DisabledAction, s as listSlot, t as CheckConfirmDialog, u as openExternal, v as FlowLoadFailed, w as loadFlowScreen, z as DropdownMenuRadioGroup } from "./check-confirm-dialog-CkaVTPT9.js";
2
+ import { a as technicalText, c as LinkComponentProvider, d as PopoverAnchor, f as PopoverClose, l as useLinkComponent, m as PopoverTrigger, n as toast, p as PopoverContent, r as friendlyStringsFrom, s as toFriendlyError, t as Toaster, u as Popover } from "./toaster-Ct_YHGs6.js";
3
+ import { $ as Tooltip, A as OwnTitleHeading, B as DropdownMenuContent, F as Icon, G as DropdownMenuRadioGroup, H as DropdownMenuItem, I as hasIcon, J as DropdownMenuShortcut, K as DropdownMenuRadioItem, L as registerIcons, M as DialogPresentationShell, N as OwnTitleContext, P as SheetPresentationShell, Q as DropdownMenuTrigger, R as DropdownMenu, T as loadFlowScreen, U as DropdownMenuLabel, V as DropdownMenuGroup, W as DropdownMenuPortal, X as DropdownMenuSubContent, Y as DropdownMenuSub, Z as DropdownMenuSubTrigger, _ as reasonText, a as modeForSurface, at as SheetContent, c as useListSlot, ct as SheetHeader, dt as DocInfo, et as TooltipContent, f as FlowActionButton, it as SheetClose, k as useLoaded, l as isExternalHref, lt as SheetTitle, n as ActionButton, nt as TooltipTrigger, o as canonicalResourceName, ot as SheetDescription, q as DropdownMenuSeparator, r as DisabledAction, rt as Sheet, s as listSlot, st as SheetFooter, t as CheckConfirmDialog, tt as TooltipProvider, u as openExternal, ut as SheetTrigger, v as FlowBoundary, y as FlowLoadFailed, z as DropdownMenuCheckboxItem } from "./check-confirm-dialog-2j0gOd7x.js";
4
4
  import { n as buttonVariants, t as Button } from "./button-MWVeZaqj.js";
5
5
  import { t as Input } from "./input-CUPStzZO.js";
6
- import { a as DialogFooter, c as DialogTrigger, i as DialogDescription, n as DialogClose, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-B_rt9S1c.js";
7
- import { C as CommandLoading, S as CommandList, _ as CommandDialog, a as RelationManyDisplay, b as CommandInput, c as focusWithoutPeek, d as PeekHostContext, g as Command, i as RelationField, l as peekAnchorOpener, p as recordDisplayName, r as RelationDisplay, s as relationLinkClassName, u as PeekContext, v as CommandEmpty, w as CommandSeparator, x as CommandItem, y as CommandGroup } from "./search-access-CURsWn-U.js";
8
- import { a as SheetClose, c as SheetFooter, d as SheetTrigger, i as Sheet, l as SheetHeader, n as OwnTitleContext, o as SheetContent, r as SheetPresentationShell, s as SheetDescription, t as DialogPresentationShell, u as SheetTitle } from "./shell-BPlk8hEa.js";
6
+ import { a as DialogFooter, c as DialogTrigger, i as DialogDescription, n as DialogClose, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
7
+ import { C as CommandEmpty, D as CommandList, E as CommandItem, O as CommandLoading, S as CommandDialog, T as CommandInput, b as RecordState, c as RelationField, d as peekAnchorOpener, f as PeekContext, h as recordDisplayName, k as CommandSeparator, l as relationLinkClassName, p as PeekHostContext, s as RelationDisplay, u as focusWithoutPeek, v as InvalidRouteId, w as CommandGroup, x as Command, y as LoadError } from "./relation-create-C43cdPQt.js";
9
8
  import { t as Skeleton } from "./skeleton-U2noefj5.js";
10
- import { $ as Tabs, A as ListColumns, At as ShellProvider, C as ListFilters, Ct as AppFlowButton, D as ExportButton, Dt as ResolvedFieldDisplay, E as ListExport, Et as i18nPrefixToRef, F as ExportDialog, G as CursorLoadMore, I as DeclarativeForm, J as resolveUpdater, K as ListErrorState, L as ScreenTree, M as DataTableViewOptions, Mt as usePaletteState, N as ListBulkBar, Nt as useShell, O as ListCreate, Ot as lowerLegacyField, P as BulkActionToolbar, Q as Checkbox, S as ListFlows, T as ListSearchInput, Tt as FieldDisplay, U as ListBody, W as ResourceList, X as useBuroDataTable, Y as tanStackSortingToBuro, Z as DataTable, _t as ActionOverflow, b as SavedViewsDropdown, bt as downloadArtifact, ct as ShowPageHeader, dt as typography, et as TabsContent, f as CopyId, ft as isShowAvailable, h as RecordTitle, ht as recordRouteAvailable, j as ColumnsToggle, jt as defaultUserMapper, k as CreateButton, kt as toDisplayText, lt as DocumentTitleProvider, m as RecordActions, nt as TabsTrigger, o as resolveFrameView, ot as EditPageHeader, p as FormActions, q as buroSortingToTanStack, r as shadcnBlockImplementations, s as shadcnFrameViews, st as PageHeader, tt as TabsList, ut as useDocumentTitle, v as ListControls, w as FilterRegion, wt as Field, x as ListLive, xt as EnumBadge, y as ListSavedViews, yt as useExportRunWatcher, z as QuickScreenClaimContext } from "./place-node--GdsP688.js";
9
+ import { $ as CursorLoadMore, A as ListSearchInput, At as EnumBadge, B as ExportDialog, Bt as usePaletteState, C as ListControls, D as ListFlows, E as ListLive, Et as ActionOverflow, F as ListColumns, Ft as i18nPrefixToRef, H as ScreenTree, I as ColumnsToggle, It as FieldValueFrame, L as DataTableViewOptions, Lt as toDisplayText, M as ExportButton, Mt as AppFlowButton, N as ListCreate, Nt as Field, O as ListFilters, Ot as useExportRunWatcher, P as CreateButton, Pt as FieldDisplay, Q as ResourceList, R as ListBulkBar, Rt as ShellProvider, T as SavedViewsDropdown, V as DeclarativeForm, Vt as useShell, W as QuickScreenClaimContext, Y as useFormControl, Z as ListBody, _ as CopyId, _t as resolveResourceTitle, a as peekFieldDefinition, at as DataTable, b as RecordTitle, bt as typography, c as peekRow, ct as TabsContent, d as resolveFrameView, et as ListErrorState, f as shadcnFrameViews, gt as ShowPageHeader, ht as PageHeader, it as useBuroDataTable, j as ListExport, k as FilterRegion, kt as downloadArtifact, l as useRelationPicker, lt as TabsList, mt as EditPageHeader, nt as resolveUpdater, o as peekLeafAllowed, ot as Checkbox, r as shadcnBlockImplementations, rt as tanStackSortingToBuro, s as peekRecord, st as Tabs, tt as buroSortingToTanStack, ut as TabsTrigger, v as FormActions, vt as DocumentTitleProvider, w as ListSavedViews, wt as recordRouteAvailable, xt as isShowAvailable, y as RecordActions, yt as useDocumentTitle, z as BulkActionToolbar, zt as defaultUserMapper } from "./place-node-CfzQvh4Y.js";
11
10
  import { THEME_STORAGE_KEY } from "./layout/theme-storage-key.js";
12
- import { A as useLayoutResolver, C as useNavChromeBoundary, D as usePageAllowed, E as useNavVisible, M as canonicalNavKey, N as canonicalResourceKey, O as layoutSlot, S as NavChromeBoundaryProvider, T as useMatch, _ as useFormControl, a as recordErrorState, b as RecordState, c as readRecentRecords, d as recordTrailRoute, f as useTrailContext, g as RecordScreen, h as usePublishedRecordTrail, i as RecordPage, j as buildNavItems, k as useLayout, l as headerTrail, n as NavAreaPage, o as useRecordVisit, p as recordTrailKey, r as ResourcePageRouter, s as RECENT_LABEL_KEYS, u as recordTrail, v as PlannedBody, w as useHref, x as NavChrome, y as ResourceListPage } from "./resource-page-router-T4vS5gbJ.js";
11
+ import { A as TableRow, B as loadLocale, C as SelectValue, D as TableCell, E as TableCaption, M as badgeVariants, O as TableHead, P as ResolvedFieldDisplay, R as lowerLegacyField, S as SelectTrigger, T as TableBody, V as saveLocale, _ as SelectItem, a as PinnedFilterControls, b as SelectScrollUpButton, c as fromLocalDateTimeText, d as formatDate, f as formatDateTime, g as SelectGroup, h as SelectContent, i as FacetedFilterPicker, j as Badge, k as TableHeader, l as toLocalDateTimeText, m as Select, n as PresetFilterControls, o as FilterPanel, p as formatNumber, r as DataTableFacetedFilter, s as FilterValueControl, t as FilterBuilder, u as FilterBadges, v as SelectLabel, w as Table, x as SelectSeparator, y as SelectScrollDownButton, z as DEFAULT_LANGUAGE_STORAGE_KEY } from "./filter-builder-DaWHUZuV.js";
12
+ import { A as TabsLayout, C as usePageAllowed, D as useLayout, E as layoutSlot, M as canonicalNavKey, N as canonicalResourceKey$1, O as useLayoutResolver, S as useNavVisible, T as BUILTIN_LAYOUTS, _ as PlannedBody, a as useRecordVisit, b as useHref, c as headerTrail, d as useTrailContext, f as recordTrailKey, g as useResourceDimensionOptions, h as RecordScreen, i as RecordPage, j as SecondarySidebarLayout, k as useReportUnresolvedLayouts, l as recordTrail, m as usePublishedRecordTrail, n as NavAreaPage, o as RECENT_LABEL_KEYS, r as ResourcePageRouter, s as readRecentRecords, u as recordTrailRoute, v as ResourceListPage, w as useResourceVisible, x as useMatch, y as NavChromeMountedProvider } from "./resource-page-router-Diw6S6TK.js";
13
13
  import { t as Label } from "./label-66W1qbke.js";
14
- import { C as TableHeader, E as badgeVariants, S as TableHead, T as Badge, _ as SelectValue, a as FilterBadges, b as TableCaption, c as Select, d as SelectItem, f as SelectLabel, g as SelectTrigger, h as SelectSeparator, i as FilterValueControl, l as SelectContent, m as SelectScrollUpButton, n as PinnedFilterControls, o as formatDate, p as SelectScrollDownButton, r as FilterPanel, s as formatNumber, t as FilterBuilder, u as SelectGroup, v as Table, w as TableRow, x as TableCell, y as TableBody } from "./filter-builder-DOnaXNfP.js";
15
- import { n as RelationPicker, r as RelationPickerInput } from "./relation-picker-BAY77ivO.js";
16
- import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-2oxnA6IR.js";
17
- import { t as useDialogFilterState } from "./use-dialog-filter-state-BDcgS5Oz.js";
18
- import { i as peekRow, n as peekLeafAllowed, r as peekRecord, t as peekFieldDefinition } from "./peek-filter-ErLfO153.js";
19
- import { builtinFrames, compilePageScreen, compiledScreenFor, dtoFields, effectiveFlow, enumFallbackKey, enumNamespaceOf, evaluateVisible, fieldOptionKey, formatEntityRef, groupChain, hasListPage, hasRecordPage, hrefFor, humanizeKey, isCustomPageDef, isRelationField, isScreenFailure, navHrefFor, nodeKind, normalizeDataError, projectDto, recordEntries, relationAmbiguousError, resolveConceptLabel, resolveContentSlot, resolveDoc, resolveEnumLabel, resolveFieldLabel, resolveNavLabel, resolveResourceLabel, resolveResourceNavLabel, resolveStaticAvailability, resourceCreateHref, resourceShowHref, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, taskRunFraction, taskRunOutcome } from "@burojs/core";
20
- import { ComponentsContext, DispatcherProvider, I18nContext, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, ResourceContext, RouteSourceContext, ScreenStateProvider, formatBytes, retryRecordRead, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useCapabilityResolver, useChangeLocale, useCommandPalette, useCurrentUserId, useDebouncedValue, useDispatcherEffects, useDocPanel, useDocScopes, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useFieldAccess, useFileField, useFileUrls, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useIdentity, useLanguageStorageKey, useList, useListAccess, useLocale, useLogout, useNavigationGuardText, useOne, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRouterAdapter, useOptionalT, usePresentBlock, useProjectedRecord, useProjectedTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRouteNavigate, useRouterAdapter, useT, useTestIds, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
21
- import { IconAlertCircle, IconArrowLeft, IconArrowUpRight, IconBook2, IconCheck, IconChevronDown, IconChevronRight, IconCirclePlus, IconDeviceDesktop, IconDots, IconExternalLink, IconHelp, IconHelpHexagon, IconLayoutSidebar, IconLoader2, IconLogin, IconLogout, IconMaximize, IconMinimize, IconMoon, IconRefresh, IconRotate, IconSearch, IconSelector, IconSun, IconUpload, IconUser, IconX } from "@tabler/icons-react";
14
+ import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-CAScLPb2.js";
15
+ import { n as RelationPicker, r as RelationPickerInput } from "./relation-picker-D-tBBZW_.js";
16
+ import { t as useDialogFilterState } from "./use-dialog-filter-state-B_MqzHaT.js";
17
+ import { useGlobalShortcut, webPlatform } from "@burojs/react/web";
18
+ import { ENUM_CONTROLS, builtinFrames, columnName, compilePageScreen, compiledScreenFor, declaredField, declaredText, dtoFields, effectiveFlow, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, evaluateVisible, fieldOptionText, formatEntityRef, hasListPage, hasRecordPage, hrefFor, humanizeKey, isCustomPageDef, isEnumControl, isRelationField, isScreenFailure, linkOf, navHrefFor, nodeKind, normalizeDataError, normalizeEntityRef, projectDto, recordEntries, relationAmbiguousError, resolveConceptLabel, resolveContentSlot, resolveDoc, resolveEnumLabel, resolveFieldLabel, resolveNavLabel, resolveResourceLabel, resolveResourceNavLabel, resolveStaticAvailability, resourceCreateHref, resourceKey, resourceShowHref, routeResourceKey, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, taskRunFraction, taskRunOutcome } from "@burojs/core";
19
+ import { ComponentsContext, DispatcherProvider, I18nContext, NO_RECORD_SCOPE_FRAMES, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, RecordScopeChain, RouteSourceContext, ScreenStateProvider, formatBytes, optionFieldName, resolveErrorText, resolveOptionLabel, retryRecordRead, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useBoundEnum, useCapabilityResolver, useChangeLocale, useChromeT, useCommandPalette, useCurrentUserId, useDebouncedValue, useDispatcherEffects, useDocPanel, useDocScopes, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useFieldAccess, useFileField, useFileUrls, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useIdentity, useLanguageStorageKey, useLinkKeys, useList, useListAccess, useLocale, useLogout, useNavigationGuardText, useOne, useOptionCaps, useOptionalApp, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRouterAdapter, useOptionalT, usePlatformStorage, usePresentBlock, useProjectedRecord, useProjectedTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRelatedRecord, useRouteNavigate, useRouteRecordId, useRouterAdapter, useScope, useT, useTestIds, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
20
+ import { IconAlertCircle, IconArrowLeft, IconArrowUpRight, IconBook2, IconCheck, IconChevronDown, IconChevronRight, IconDeviceDesktop, IconDots, IconExternalLink, IconHelp, IconHelpHexagon, IconLayoutSidebar, IconLoader2, IconLogin, IconLogout, IconMaximize, IconMinimize, IconMoon, IconRefresh, IconRotate, IconSearch, IconSelector, IconSun, IconUpload, IconUser, IconX } from "@tabler/icons-react";
22
21
  import * as React from "react";
23
22
  import { Children, Fragment, Suspense, cloneElement, createContext, createElement, isValidElement, lazy, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
24
23
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -30,6 +29,7 @@ import * as SeparatorPrimitive from "@radix-ui/react-separator";
30
29
  import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
31
30
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
32
31
  import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
32
+ import { enumBindingKey } from "@burojs/core/internal";
33
33
  import { Controller, FormProvider, useForm, useFormContext } from "react-hook-form";
34
34
  import { zodResolver } from "@hookform/resolvers/zod";
35
35
  import { z } from "zod";
@@ -490,7 +490,7 @@ function ScopeSummary({ scope, summary }) {
490
490
  const caps = useI18nCaps();
491
491
  const t = useOptionalT();
492
492
  const links = (scope !== void 0 ? resolveDoc(caps, scope) : void 0)?.links ?? [];
493
- const newTabNote = t("docPanel.newTab", { defaultValue: "opens in a new tab" });
493
+ const newTabNote = t("docPanel.opensInNewTab", { defaultValue: "(opens in a new tab)" });
494
494
  if (summary === void 0 && links.length === 0) return null;
495
495
  return jsxs("div", {
496
496
  className: "mt-2 flex flex-col items-start gap-3",
@@ -1112,7 +1112,7 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
1112
1112
  function NavigationGuardDialog() {
1113
1113
  const prompt = useRegisterNavigationGuardDialog();
1114
1114
  const text = useNavigationGuardText();
1115
- const unsavedAction = useOptionalT()("navigation.unsavedAction");
1115
+ const unsavedAction = useOptionalT()("navigation.unsavedAction", { defaultValue: "An action on this screen has unsaved input" });
1116
1116
  const returnFocus = useRef(null);
1117
1117
  const wasOpen = useRef(false);
1118
1118
  const open = prompt !== null;
@@ -1135,7 +1135,7 @@ function NavigationGuardDialog() {
1135
1135
  event.preventDefault();
1136
1136
  element.focus();
1137
1137
  },
1138
- children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: text.title }), jsxs(AlertDialogDescription, { children: [text.body, prompt !== null && prompt.labels.length > 0 ? ` ${unsavedAction === "navigation.unsavedAction" ? "An action on this screen has unsaved input" : unsavedAction}: ${prompt.labels.join(", ")}.` : null] })] }), jsxs(AlertDialogFooter, { children: [jsx(AlertDialogCancel, {
1138
+ children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: text.title }), jsxs(AlertDialogDescription, { children: [text.body, prompt !== null && prompt.labels.length > 0 ? ` ${unsavedAction}: ${prompt.labels.join(", ")}.` : null] })] }), jsxs(AlertDialogFooter, { children: [jsx(AlertDialogCancel, {
1139
1139
  "data-testid": "nav-guard-stay",
1140
1140
  children: text.stay
1141
1141
  }), jsx(AlertDialogAction, {
@@ -1695,14 +1695,14 @@ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
1695
1695
  const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
1696
1696
  //#endregion
1697
1697
  //#region src/shell/use-nav-icon.tsx
1698
- const warned = new Set();
1698
+ const warned$1 = new Set();
1699
1699
  function resolveNavIcon(name, components) {
1700
1700
  if (name === void 0 || name === "") return void 0;
1701
1701
  const slot = components(name);
1702
1702
  if (typeof slot === "function" || typeof slot === "object" && slot !== null) return jsx(slot, { className: "h-4 w-4" });
1703
1703
  if (hasIcon(name)) return jsx(Icon, { name });
1704
- if (process.env.NODE_ENV !== "production" && !warned.has(name)) {
1705
- warned.add(name);
1704
+ if (process.env.NODE_ENV !== "production" && !warned$1.has(name)) {
1705
+ warned$1.add(name);
1706
1706
  console.warn(`[buro] unknown nav icon "${name}"`);
1707
1707
  }
1708
1708
  }
@@ -1711,6 +1711,53 @@ function useNavIcon() {
1711
1711
  return useCallback((name) => resolveNavIcon(name, (key) => components[key]), [components]);
1712
1712
  }
1713
1713
  //#endregion
1714
+ //#region src/layout/build-nav-items.ts
1715
+ function buildNavItems(args) {
1716
+ const { registry, caps, groupName, match, isVisible, entryFilter, icons } = args;
1717
+ const chain = match?.groupChain ?? [];
1718
+ const qualifiedMatch = match;
1719
+ const activeLeaf = qualifiedMatch == null ? void 0 : qualifiedMatch.kind === "group-index" ? canonicalNavKey({
1720
+ name: qualifiedMatch.target ?? qualifiedMatch.name,
1721
+ ref: qualifiedMatch.targetRef
1722
+ }) : canonicalNavKey(qualifiedMatch);
1723
+ const isEligible = (node) => isVisible(node) && (entryFilter ? entryFilter(node) : true);
1724
+ const out = [];
1725
+ for (const child of registry.groups.childrenOf(groupName)) {
1726
+ if (!isEligible(child)) continue;
1727
+ const kind = nodeKind(child);
1728
+ const childKey = canonicalNavKey(child);
1729
+ const href = navHrefFor(registry, childKey, { isVisible: isEligible });
1730
+ if (kind === "group") {
1731
+ const label = resolveNavLabel(caps, `groups.${childKey}`, declaredText("chrome", child, "label"));
1732
+ const active = chain.some((g) => canonicalNavKey(g) === childKey);
1733
+ out.push({
1734
+ kind: "group",
1735
+ name: childKey,
1736
+ label,
1737
+ href,
1738
+ icon: icons?.group?.(childKey),
1739
+ active
1740
+ });
1741
+ } else if (kind === "custom-page") out.push({
1742
+ kind: "custom-page",
1743
+ name: childKey,
1744
+ label: resolveNavLabel(caps, `pages.${childKey}`, declaredText("chrome", child, "label")),
1745
+ href,
1746
+ icon: icons?.page?.(childKey),
1747
+ active: activeLeaf === childKey
1748
+ });
1749
+ else out.push({
1750
+ kind: "resource",
1751
+ name: childKey,
1752
+ label: resolveResourceNavLabel(caps, childKey, child),
1753
+ href,
1754
+ icon: icons?.resource?.(childKey),
1755
+ active: activeLeaf === childKey
1756
+ });
1757
+ }
1758
+ return out;
1759
+ }
1760
+ //#endregion
1714
1761
  //#region src/layout/use-nav-tree.ts
1715
1762
  function isActiveHref(href, currentPath) {
1716
1763
  if (!currentPath) return false;
@@ -1724,8 +1771,7 @@ function useNavTree(opts = {}) {
1724
1771
  const passesEntryFilter = (entry) => entryFilter ? entryFilter(entry) : true;
1725
1772
  const app = useApp();
1726
1773
  const caps = useI18nCaps();
1727
- const visibilityCtx = useVisibilityCtx();
1728
- const isResourceVisible = (r) => evaluateVisible(r.visible, visibilityCtx);
1774
+ const isResourceVisible = useResourceVisible();
1729
1775
  const resolveLayout = useLayoutResolver();
1730
1776
  const match = useMatch();
1731
1777
  const isVisible = useNavVisible();
@@ -1735,6 +1781,7 @@ function useNavTree(opts = {}) {
1735
1781
  const allCustomPages = app.registry.customPages.list();
1736
1782
  const sections = app.registry.sections.list();
1737
1783
  const groups = app.registry.groups.rootNodes().filter((n) => nodeKind(n) === "group");
1784
+ useReportUnresolvedLayouts(app.registry.groups.list(), resolveLayout);
1738
1785
  const topLevelResources = allResources.filter((r) => hasListPage(r)).filter(isResourceVisible).filter(passesEntryFilter).filter((r) => !r.section && !r.group).map((r, idx) => ({
1739
1786
  r,
1740
1787
  idx
@@ -1744,11 +1791,11 @@ function useNavTree(opts = {}) {
1744
1791
  return ao !== bo ? ao - bo : a.idx - b.idx;
1745
1792
  }).map((x) => x.r);
1746
1793
  const topLevelCustomPages = allCustomPages.filter((p) => !p.section && !p.group).filter(pageAllowed).filter(passesEntryFilter);
1747
- const resourceHref = (def) => hrefFor(app.registry, canonicalResourceKey(def));
1748
- const resourceLabel = (def) => resolveResourceNavLabel(caps, canonicalResourceKey(def), def);
1749
- const pageLabel = (page) => resolveNavLabel(caps, `pages.${canonicalNavKey(page)}`, page.label);
1750
- const sectionLabel = (section) => resolveNavLabel(caps, `sections.${canonicalNavKey(section)}`, section.label);
1751
- const groupLabel = (group) => resolveNavLabel(caps, `groups.${canonicalNavKey(group)}`, group.label);
1794
+ const resourceHref = (def) => hrefFor(app.registry, canonicalResourceKey$1(def));
1795
+ const resourceLabel = (def) => resolveResourceNavLabel(caps, canonicalResourceKey$1(def), def);
1796
+ const pageLabel = (page) => resolveNavLabel(caps, `pages.${canonicalNavKey(page)}`, declaredText("chrome", page, "label"));
1797
+ const sectionLabel = (section) => resolveNavLabel(caps, `sections.${canonicalNavKey(section)}`, declaredText("chrome", section, "label"));
1798
+ const groupLabel = (group) => resolveNavLabel(caps, `groups.${canonicalNavKey(group)}`, declaredText("chrome", group, "label"));
1752
1799
  const pageEntry = (page) => {
1753
1800
  const href = page.path;
1754
1801
  return {
@@ -1764,7 +1811,7 @@ function useNavTree(opts = {}) {
1764
1811
  const href = resourceHref(def);
1765
1812
  return {
1766
1813
  kind: "resource",
1767
- key: canonicalResourceKey(def),
1814
+ key: canonicalResourceKey$1(def),
1768
1815
  label: resourceLabel(def),
1769
1816
  href,
1770
1817
  icon: def.icon,
@@ -1972,7 +2019,7 @@ function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection,
1972
2019
  })
1973
2020
  }) }, entry.key);
1974
2021
  const [top, ...blocks] = tree;
1975
- return jsxs(Fragment$1, { children: [jsxs(SidebarGroup, { children: [topLabel !== null && jsx(SidebarGroupLabel, { children: topLabel ?? t("nav.resources") }), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: top?.entries.map(menuItem) }) })] }), blocks.map((block) => {
2022
+ return jsxs(Fragment$1, { children: [top !== void 0 && top.entries.length > 0 && jsxs(SidebarGroup, { children: [topLabel !== null && jsx(SidebarGroupLabel, { children: topLabel ?? t("nav.resources") }), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: top.entries.map(menuItem) }) })] }), blocks.map((block) => {
1976
2023
  const key = block.key ?? "";
1977
2024
  if (block.label === null) return jsx(SidebarGroup, { children: jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) }) }, key);
1978
2025
  if (block.collapsible) return jsx(CollapsibleNavSection, {
@@ -2016,126 +2063,31 @@ function AppSidebar({ header, filters, nav, footer }) {
2016
2063
  });
2017
2064
  }
2018
2065
  //#endregion
2019
- //#region src/layout/layouts/secondary-sidebar-layout.tsx
2020
- function Row({ it }) {
2021
- const RouterLink = useLinkComponent();
2022
- return jsx("li", { children: jsxs(RouterLink, {
2023
- href: it.href,
2024
- "data-testid": `nav-secondary-item:${it.name}`,
2025
- "aria-current": it.active ? "page" : void 0,
2026
- className: cn("flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium outline-hidden ring-offset-background transition-colors", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "bg-secondary text-secondary-foreground" : "text-muted-foreground hover:bg-secondary/50 hover:text-foreground"),
2027
- children: [it.icon != null && jsx("span", {
2028
- className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
2029
- "aria-hidden": "true",
2030
- children: it.icon
2031
- }), jsx("span", {
2032
- className: "truncate",
2033
- title: it.label,
2034
- children: it.label
2035
- })]
2036
- }) });
2037
- }
2038
- function SecondarySidebarLayout({ group, groupKey, items, children }) {
2039
- const RouterLink = useLinkComponent();
2040
- const caps = useI18nCaps();
2041
- const id = groupKey ?? group.name;
2042
- const label = resolveNavLabel(caps, `groups.${id}`, group.label);
2043
- return jsxs("div", {
2044
- className: "md:-m-6 md:flex md:min-h-[calc(100%+3rem)]",
2045
- "data-testid": `nav-secondary:${id}`,
2046
- children: [
2047
- jsx("aside", {
2048
- className: "hidden w-64 shrink-0 border-r bg-background text-foreground md:block",
2049
- children: jsxs("div", {
2050
- className: "space-y-4 p-4",
2051
- children: [jsx("p", {
2052
- className: "px-3 text-base font-semibold tracking-tight",
2053
- children: label
2054
- }), jsx("nav", {
2055
- "aria-label": label,
2056
- children: jsx("ul", {
2057
- className: "space-y-0.5",
2058
- children: items.map((it) => jsx(Row, { it }, it.name))
2059
- })
2060
- })]
2061
- })
2062
- }),
2063
- jsx("nav", {
2064
- "aria-label": label,
2065
- "data-testid": `nav-secondary-select:${id}`,
2066
- className: "-mx-1 mb-4 flex snap-x snap-proximity gap-1.5 overflow-x-auto scroll-smooth px-1 pb-1 [-ms-overflow-style:none] [mask-image:linear-gradient(to_right,transparent,black_8px,black_calc(100%-8px),transparent)] [scrollbar-width:none] md:hidden [&::-webkit-scrollbar]:hidden",
2067
- children: items.map((it) => jsx(RouterLink, {
2068
- href: it.href,
2069
- "aria-current": it.active ? "page" : void 0,
2070
- className: cn("snap-start whitespace-nowrap rounded-full border px-3 py-1.5 text-sm outline-hidden ring-offset-background transition-colors duration-150", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "border-primary/40 bg-primary/10 font-medium text-foreground" : "border-border text-muted-foreground hover:bg-accent/60 hover:text-accent-foreground"),
2071
- children: it.label
2072
- }, it.name))
2073
- }),
2074
- jsx("div", {
2075
- className: "min-w-0 flex-1 md:overflow-x-hidden md:p-6",
2076
- children
2077
- })
2078
- ]
2079
- });
2080
- }
2081
- //#endregion
2082
- //#region src/layout/layouts/tabs-layout.tsx
2083
- function TabsLayout({ group, groupKey, items, children }) {
2084
- const RouterLink = useLinkComponent();
2085
- const caps = useI18nCaps();
2086
- const id = groupKey ?? group.name;
2087
- const label = resolveNavLabel(caps, `groups.${id}`, group.label);
2088
- return jsxs("div", {
2089
- className: "flex flex-col gap-4",
2090
- "data-testid": `nav-tabs:${id}`,
2091
- children: [jsx("nav", {
2092
- "aria-label": label,
2093
- className: "flex gap-1 overflow-x-auto scroll-smooth border-b border-border [-ms-overflow-style:none] [mask-image:linear-gradient(to_right,transparent,black_8px,black_calc(100%-8px),transparent)] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
2094
- children: items.map((it) => jsxs(RouterLink, {
2095
- href: it.href,
2096
- "aria-current": it.active ? "page" : void 0,
2097
- "data-testid": `nav-tab:${it.name}`,
2098
- className: cn("group/tab relative flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-t-sm px-3 py-2.5 text-sm outline-hidden ring-offset-background transition-colors duration-150", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"),
2099
- children: [
2100
- it.icon != null && jsx("span", {
2101
- className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
2102
- "aria-hidden": "true",
2103
- children: it.icon
2104
- }),
2105
- it.label,
2106
- jsx("span", {
2107
- "aria-hidden": "true",
2108
- className: cn("pointer-events-none absolute inset-x-2 -bottom-px h-0.5 origin-center scale-x-0 rounded-full bg-primary opacity-0 transition-all duration-300 ease-out", it.active ? "scale-x-100 opacity-100" : "group-hover/tab:scale-x-100 group-hover/tab:opacity-30")
2109
- })
2110
- ]
2111
- }, it.name))
2112
- }), jsx("div", {
2113
- className: "min-w-0",
2114
- children
2115
- })]
2116
- });
2117
- }
2118
- //#endregion
2119
- //#region src/layout/nav-area-layout.tsx
2066
+ //#region src/layout/nav-chrome.tsx
2120
2067
  const DETAIL_ACTIONS = new Set([
2121
2068
  "show",
2122
2069
  "edit",
2123
2070
  "create"
2124
2071
  ]);
2125
- function NavAreaLayout({ groupName, children, entryFilter, icons }) {
2072
+ function NavChrome({ children, entryFilter, icons }) {
2126
2073
  const app = useApp();
2127
2074
  const caps = useI18nCaps();
2128
2075
  const isVisible = useNavVisible();
2129
2076
  const match = useMatch();
2130
2077
  const resolveLayout = useLayoutResolver();
2131
- const group = app.registry.groups.get(groupName);
2132
- if (!group) return jsx(Fragment$1, { children });
2133
- const groupKey = canonicalNavKey(group);
2134
- const claimed = [groupKey, ...groupChain(app.registry, group).map(canonicalNavKey)];
2135
- const collapse = group.chromeOn === "list" && match?.kind === "resource" && DETAIL_ACTIONS.has(match.action);
2136
- const Layout = resolveLayout(group.layout);
2078
+ useReportUnresolvedLayouts(match?.groupChain ?? [], resolveLayout);
2079
+ if (!match) return jsx(NavChromeMountedProvider, {
2080
+ value: true,
2081
+ children
2082
+ });
2137
2083
  let content = children;
2138
- if (Layout && !collapse) {
2084
+ for (let i = match.groupChain.length - 1; i >= 0; i--) {
2085
+ const group = match.groupChain[i];
2086
+ if (!group) continue;
2087
+ const groupKey = canonicalNavKey(group);
2088
+ if (group.chromeOn === "list" && match.kind === "resource" && DETAIL_ACTIONS.has(match.action)) continue;
2089
+ const Layout = resolveLayout(group.layout);
2090
+ if (!Layout) continue;
2139
2091
  const items = buildNavItems({
2140
2092
  registry: app.registry,
2141
2093
  caps,
@@ -2149,11 +2101,11 @@ function NavAreaLayout({ groupName, children, entryFilter, icons }) {
2149
2101
  group,
2150
2102
  groupKey,
2151
2103
  items,
2152
- children
2104
+ children: content
2153
2105
  });
2154
2106
  }
2155
- return jsx(NavChromeBoundaryProvider, {
2156
- claim: claimed,
2107
+ return jsx(NavChromeMountedProvider, {
2108
+ value: true,
2157
2109
  children: content
2158
2110
  });
2159
2111
  }
@@ -2166,10 +2118,12 @@ function Chevron() {
2166
2118
  "aria-hidden": "true"
2167
2119
  });
2168
2120
  }
2169
- function Breadcrumbs(props) {
2121
+ function Breadcrumbs(input) {
2122
+ const props = input;
2170
2123
  const { leadingSegments, className, docScopes, "data-testid": testId } = props;
2171
2124
  const app = useApp();
2172
- const def = props.resource === void 0 ? void 0 : app.registry.get(props.resource);
2125
+ const resource = props.resource === void 0 ? void 0 : routeResourceKey(props.resource);
2126
+ const def = resource === void 0 ? void 0 : app.registry.get(resource);
2173
2127
  const tid = useTestIds();
2174
2128
  const t = useOptionalT();
2175
2129
  const RouterLink = useLinkComponent();
@@ -2214,7 +2168,7 @@ function Breadcrumbs(props) {
2214
2168
  });
2215
2169
  if (!def) return null;
2216
2170
  const { recordId, pageLabel, middleSegments } = props;
2217
- const resourceKey = canonicalResourceKey(def);
2171
+ const resourceKey = canonicalResourceKey$1(def);
2218
2172
  const listHref = hrefFor(app.registry, resourceKey);
2219
2173
  const resourceLabel = resolveResourceLabel(caps, resourceKey, "plural", def);
2220
2174
  const resourceIsCurrent = !pageLabel && !(middleSegments && middleSegments.length > 0);
@@ -2224,7 +2178,12 @@ function Breadcrumbs(props) {
2224
2178
  "data-testid": testId,
2225
2179
  children: [
2226
2180
  leading(resourceKey),
2227
- resourceIsCurrent ? current(tid.navBreadcrumb(resourceKey), resourceLabel) : jsx(RouterLink, {
2181
+ resourceIsCurrent ? current(tid.navBreadcrumb(resourceKey), resourceLabel) : !hasListPage(def) ? jsx("span", {
2182
+ "data-testid": tid.navBreadcrumb(resourceKey),
2183
+ className: "truncate",
2184
+ title: resourceLabel,
2185
+ children: resourceLabel
2186
+ }) : jsx(RouterLink, {
2228
2187
  href: listHref,
2229
2188
  "data-testid": tid.navBreadcrumb(resourceKey),
2230
2189
  className: cn(LINK_CLASS, "truncate"),
@@ -2300,15 +2259,15 @@ function RecordBreadcrumbs() {
2300
2259
  }
2301
2260
  //#endregion
2302
2261
  //#region src/layout/default-nav-layouts.ts
2303
- const defaultNavLayouts = {
2304
- [layoutSlot("secondary-sidebar")]: SecondarySidebarLayout,
2305
- [layoutSlot("tabs")]: TabsLayout
2306
- };
2262
+ const defaultNavLayouts = { ...BUILTIN_LAYOUTS };
2307
2263
  //#endregion
2308
2264
  //#region src/layout/context-picker.tsx
2309
- function ContextPicker({ sliceName, label, options, value, onChange, placeholder, disabled, className, hideLabel }) {
2265
+ const ALL = "\0all";
2266
+ function ContextPicker({ sliceName, label, options, value, onChange, placeholder, disabled, className, hideLabel, allOption }) {
2310
2267
  const tid = useTestIds();
2311
2268
  const t = useOptionalT();
2269
+ const chromeT = useChromeT();
2270
+ const allLabel = typeof allOption === "string" ? allOption : chromeT("scope.all", { defaultValue: "All" });
2312
2271
  const displayLabel = label ?? humanizeKey(sliceName);
2313
2272
  return jsxs("div", {
2314
2273
  className: cn("space-y-1", className),
@@ -2317,21 +2276,112 @@ function ContextPicker({ sliceName, label, options, value, onChange, placeholder
2317
2276
  className: hideLabel ? "sr-only" : "text-xs font-medium text-muted-foreground uppercase tracking-wide",
2318
2277
  children: displayLabel
2319
2278
  }), jsxs(Select, {
2320
- value: value ?? "",
2321
- onValueChange: onChange,
2279
+ value: value ?? (allOption === void 0 ? "" : ALL),
2280
+ onValueChange: (next) => {
2281
+ if (allOption === void 0) onChange(next);
2282
+ else onChange(next === ALL ? null : next);
2283
+ },
2322
2284
  disabled,
2323
2285
  children: [jsx(SelectTrigger, {
2324
2286
  id: `context-picker:${sliceName}`,
2325
2287
  "data-testid": tid.contextPicker(sliceName),
2326
2288
  className: cn("w-full", hideLabel ? "h-8 text-xs" : void 0),
2327
2289
  children: jsx(SelectValue, { placeholder: placeholder ?? t("field.select", { defaultValue: "Select…" }) })
2328
- }), jsx(SelectContent, { children: options.map((o) => jsx(SelectItem, {
2290
+ }), jsxs(SelectContent, { children: [allOption === void 0 ? null : jsx(SelectItem, {
2291
+ value: ALL,
2292
+ "data-testid": tid.contextPickerOption(sliceName, "all"),
2293
+ children: allLabel
2294
+ }), options.map((o) => jsx(SelectItem, {
2329
2295
  value: o.value,
2330
2296
  "data-testid": tid.contextPickerOption(sliceName, o.value),
2331
2297
  children: o.label
2332
- }, o.value)) })]
2298
+ }, o.value))] })]
2299
+ })]
2300
+ });
2301
+ }
2302
+ //#endregion
2303
+ //#region src/layout/scope-dimension-picker.tsx
2304
+ function ScopeDimensionPicker(props) {
2305
+ const scope = useScope();
2306
+ const definition = scope.definition?.dimensions[props.dimension];
2307
+ if (definition === void 0) throw new Error(`ScopeDimensionPicker: '${props.dimension}' is not a dimension of the app's defineScope(...)`);
2308
+ if (definition.select === "none") return null;
2309
+ const source = definition.values;
2310
+ if (source === void 0) throw new Error(`ScopeDimensionPicker: dimension '${props.dimension}' declares no values; add values: { resource } or a { value, label } list`);
2311
+ const allowed = scope.access[props.dimension]?.allowed;
2312
+ const permitted = Array.isArray(allowed) ? allowed : void 0;
2313
+ const common = {
2314
+ ...props,
2315
+ multiple: definition.select === "multiple",
2316
+ all: definition.all,
2317
+ selected: scope.selection[props.dimension] ?? null,
2318
+ select: (value) => scope.select(props.dimension, value)
2319
+ };
2320
+ if (Array.isArray(source)) {
2321
+ const options = source.filter((option) => permitted === void 0 || permitted.includes(option.value));
2322
+ return jsx(DimensionControl, {
2323
+ ...common,
2324
+ options
2325
+ });
2326
+ }
2327
+ const { resource, label } = source;
2328
+ return jsx(ResourceDimension, {
2329
+ ...common,
2330
+ resource,
2331
+ ...label ? { labelField: label } : {}
2332
+ });
2333
+ }
2334
+ function ResourceDimension({ resource, labelField, ...rest }) {
2335
+ const options = useResourceDimensionOptions(rest.dimension, resource, labelField);
2336
+ return jsx(DimensionControl, {
2337
+ ...rest,
2338
+ options
2339
+ });
2340
+ }
2341
+ function DimensionControl({ dimension, label, hideLabel, className, multiple, all, selected, select, options }) {
2342
+ const tid = useTestIds();
2343
+ const chromeT = useChromeT();
2344
+ const byText = useMemo(() => new Map(options.map((option) => [String(option.value), option.value])), [options]);
2345
+ const textOptions = useMemo(() => options.map((option) => ({
2346
+ value: String(option.value),
2347
+ label: option.label
2348
+ })), [options]);
2349
+ const picked = (Array.isArray(selected) ? selected : selected === null ? [] : [selected]).map(String);
2350
+ const text = label ?? humanizeKey(dimension);
2351
+ if (multiple) return jsxs("div", {
2352
+ className: cn("space-y-1", className),
2353
+ children: [jsx("span", {
2354
+ className: hideLabel ? "sr-only" : "text-xs font-medium text-muted-foreground uppercase tracking-wide",
2355
+ children: text
2356
+ }), jsx(FacetedFilterPicker, {
2357
+ title: text,
2358
+ options: textOptions,
2359
+ selected: picked,
2360
+ ...all ? { emptyLabel: chromeT("scope.all", { defaultValue: "All" }) } : { clearable: false },
2361
+ onChange: (next) => {
2362
+ if (next.length === 0 && !all) return;
2363
+ select(next.length === 0 ? null : next.map((value) => byText.get(value) ?? value));
2364
+ },
2365
+ getTestId: (role, value) => role === "trigger" ? tid.contextPicker(dimension) : role === "option" ? tid.contextPickerOption(dimension, value ?? "") : role === "clear" ? tid.contextPickerOption(dimension, "all") : void 0
2333
2366
  })]
2334
2367
  });
2368
+ const common = {
2369
+ sliceName: dimension,
2370
+ ...label === void 0 ? {} : { label },
2371
+ options: textOptions,
2372
+ value: picked[0] ?? null,
2373
+ ...hideLabel === void 0 ? {} : { hideLabel },
2374
+ ...className === void 0 ? {} : { className }
2375
+ };
2376
+ const choose = (value) => select(value === null ? null : byText.get(value) ?? value);
2377
+ return all ? jsx(ContextPicker, {
2378
+ ...common,
2379
+ allOption: true,
2380
+ onChange: choose
2381
+ }) : jsx(ContextPicker, {
2382
+ ...common,
2383
+ onChange: choose
2384
+ });
2335
2385
  }
2336
2386
  //#endregion
2337
2387
  //#region src/layout/language-switch.tsx
@@ -2522,7 +2572,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2522
2572
  const navigate = useRouteNavigate();
2523
2573
  const isVisible = useNavVisible();
2524
2574
  const resolveLayout = useLayoutResolver();
2525
- const isResourceVisible = (r) => evaluateVisible(r.visible, visibilityCtx);
2575
+ const isResourceVisible = useResourceVisible();
2526
2576
  const pageAllowed = usePageAllowed();
2527
2577
  const isPageVisible = (p) => evaluateVisible(p.visible, visibilityCtx) && pageAllowed(p);
2528
2578
  const { topLevelResources, topLevelPages, sectionGroups } = useMemo(() => {
@@ -2550,13 +2600,13 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2550
2600
  onSelect?.();
2551
2601
  navigate(href);
2552
2602
  };
2553
- const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey(def), "plural", def);
2554
- const pageLabel = (def) => resolveNavLabel(caps, `pages.${canonicalNavKey(def)}`, def.label);
2603
+ const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey$1(def), "plural", def);
2604
+ const pageLabel = (def) => resolveNavLabel(caps, `pages.${canonicalNavKey(def)}`, declaredText("chrome", def, "label"));
2555
2605
  const groups = [];
2556
2606
  if (topLevelResources.length > 0) groups.push(jsx(CommandGroup, {
2557
2607
  heading: t("commandPalette.resources", { defaultValue: "Resources" }),
2558
2608
  children: topLevelResources.map((def) => {
2559
- const resource = canonicalResourceKey(def);
2609
+ const resource = canonicalResourceKey$1(def);
2560
2610
  const label = resourceLabel(def);
2561
2611
  const href = hrefFor(app.registry, resource, { isVisible });
2562
2612
  return jsx(CommandItem, {
@@ -2583,7 +2633,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2583
2633
  }, "pages"));
2584
2634
  for (const { section, children } of sectionGroups) {
2585
2635
  const sectionKey = canonicalNavKey(section);
2586
- const sectionLabel = resolveNavLabel(caps, `sections.${sectionKey}`, section.label);
2636
+ const sectionLabel = resolveNavLabel(caps, `sections.${sectionKey}`, declaredText("chrome", section, "label"));
2587
2637
  groups.push(jsxs(CommandGroup, {
2588
2638
  heading: sectionLabel,
2589
2639
  children: [jsx(CommandItem, {
@@ -2605,7 +2655,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2605
2655
  children: label
2606
2656
  }, `section:${sectionKey}:page:${pageKey}`);
2607
2657
  }
2608
- const resource = canonicalResourceKey(child);
2658
+ const resource = canonicalResourceKey$1(child);
2609
2659
  const label = resourceLabel(child);
2610
2660
  const href = hrefFor(app.registry, resource, { isVisible });
2611
2661
  return jsx(CommandItem, {
@@ -2622,7 +2672,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2622
2672
  const items = [];
2623
2673
  if (!!resolveLayout(group.layout)) {
2624
2674
  const groupKey = canonicalNavKey(group);
2625
- const label = resolveNavLabel(caps, `groups.${groupKey}`, group.label);
2675
+ const label = resolveNavLabel(caps, `groups.${groupKey}`, declaredText("chrome", group, "label"));
2626
2676
  const href = navHrefFor(app.registry, groupKey, { isVisible });
2627
2677
  items.push(jsx(CommandItem, {
2628
2678
  value: `group:${idPath}:${label}`,
@@ -2657,7 +2707,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2657
2707
  const resource = child;
2658
2708
  if (!hasListPage(resource)) continue;
2659
2709
  if (!isResourceVisible(resource)) continue;
2660
- const resourceId = canonicalResourceKey(resource);
2710
+ const resourceId = canonicalResourceKey$1(resource);
2661
2711
  const label = resourceLabel(resource);
2662
2712
  const href = hrefFor(app.registry, resourceId, { isVisible });
2663
2713
  items.push(jsx(CommandItem, {
@@ -2676,7 +2726,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2676
2726
  const groupKey = canonicalNavKey(group);
2677
2727
  const items = collectGroupItems(group, groupKey, 0);
2678
2728
  if (items.length === 0) continue;
2679
- const heading = resolveNavLabel(caps, `groups.${groupKey}`, group.label);
2729
+ const heading = resolveNavLabel(caps, `groups.${groupKey}`, declaredText("chrome", group, "label"));
2680
2730
  groups.push(jsx(CommandGroup, {
2681
2731
  heading,
2682
2732
  children: items
@@ -2696,10 +2746,10 @@ function opensInQuick(registry, resource, mode) {
2696
2746
  }
2697
2747
  //#endregion
2698
2748
  //#region src/layout/command-palette-recent-group.tsx
2699
- function entryHref(registry, entry, visibilityCtx) {
2749
+ function entryHref(registry, entry, isResourceVisible) {
2700
2750
  const def = registry.get(entry.canonicalRef);
2701
2751
  if (def === void 0) return void 0;
2702
- if (!evaluateVisible(def.visible, visibilityCtx)) return void 0;
2752
+ if (!isResourceVisible(def)) return void 0;
2703
2753
  for (const action of ["show", "edit"]) if (recordRouteAvailable(registry, entry.canonicalRef, action)) return hrefFor(registry, entry.canonicalRef, {
2704
2754
  action,
2705
2755
  id: entry.id
@@ -2713,27 +2763,39 @@ function CommandPaletteRecentGroup({ onSelect, getTestId, heading, refreshKey, w
2713
2763
  const navigate = useRouteNavigate();
2714
2764
  const quick = useQuick();
2715
2765
  const userId = useCurrentUserId();
2716
- const visibilityCtx = useVisibilityCtx();
2766
+ const isResourceVisible = useResourceVisible();
2717
2767
  const resolvedHeading = heading ?? t("commandPalette.recent", { defaultValue: "Recent" });
2718
- const [hydrated, setHydrated] = useState(false);
2768
+ const store = usePlatformStorage().local;
2769
+ const [stored, setStored] = useState([]);
2719
2770
  useEffect(() => {
2720
- setHydrated(true);
2721
- }, []);
2722
- const recents = useMemo(() => {
2723
- if (!hydrated || userId === null) return [];
2724
- return readRecentRecords(userId).flatMap((entry) => {
2725
- const href = entryHref(app.registry, entry, visibilityCtx);
2726
- return href === void 0 ? [] : [{
2727
- entry,
2728
- href
2729
- }];
2730
- });
2771
+ if (userId === null) {
2772
+ setStored([]);
2773
+ return;
2774
+ }
2775
+ let current = true;
2776
+ const read = readRecentRecords(store, userId);
2777
+ if (Array.isArray(read)) setStored(read);
2778
+ else read.then((entries) => {
2779
+ if (current) setStored(entries);
2780
+ }, () => void 0);
2781
+ return () => {
2782
+ current = false;
2783
+ };
2731
2784
  }, [
2732
- hydrated,
2785
+ store,
2733
2786
  userId,
2734
- refreshKey,
2787
+ refreshKey
2788
+ ]);
2789
+ const recents = useMemo(() => stored.flatMap((entry) => {
2790
+ const href = entryHref(app.registry, entry, isResourceVisible);
2791
+ return href === void 0 ? [] : [{
2792
+ entry,
2793
+ href
2794
+ }];
2795
+ }), [
2796
+ stored,
2735
2797
  app.registry,
2736
- visibilityCtx
2798
+ isResourceVisible
2737
2799
  ]);
2738
2800
  if ((useCommandState((state) => state.search) ?? "").length > 0) return null;
2739
2801
  if (recents.length === 0) return null;
@@ -2798,18 +2860,19 @@ function CommandPaletteRecordsGroup({ enabled = true, pageSize = 5, minQueryLeng
2798
2860
  const t = useOptionalT();
2799
2861
  const navigate = useRouteNavigate();
2800
2862
  const quick = useQuick();
2863
+ const isResourceVisible = useResourceVisible();
2801
2864
  const resolvedHeading = heading ?? t("commandPalette.records", { defaultValue: "Records" });
2802
2865
  const liveQuery = useCommandState((state) => state.search) ?? "";
2803
2866
  const debouncedQuery = useDebouncedValue(liveQuery, debounceMs);
2804
2867
  const searchableResources = useMemo(() => {
2805
- return app.registry.list().filter((r) => isShowAvailable(app.registry, canonicalResourceKey(r)) || opensInQuick(app.registry, canonicalResourceKey(r), "show")).map((def) => {
2868
+ return app.registry.list().filter((r) => isShowAvailable(app.registry, canonicalResourceKey$1(r)) || opensInQuick(app.registry, canonicalResourceKey$1(r), "show")).filter(isResourceVisible).map((def) => {
2806
2869
  const displayField = resolveDisplayField(def);
2807
2870
  return displayField ? {
2808
2871
  def,
2809
2872
  displayField
2810
2873
  } : null;
2811
2874
  }).filter((entry) => entry !== null);
2812
- }, [app.registry]);
2875
+ }, [app.registry, isResourceVisible]);
2813
2876
  if (!(enabled && debouncedQuery.length >= minQueryLength && searchableResources.length > 0)) return null;
2814
2877
  const goTo = (href, resource, id) => {
2815
2878
  onSelect?.();
@@ -2828,12 +2891,12 @@ function CommandPaletteRecordsGroup({ enabled = true, pageSize = 5, minQueryLeng
2828
2891
  recordHref,
2829
2892
  registry: app.registry,
2830
2893
  getTestId
2831
- }, canonicalResourceKey(def)))
2894
+ }, canonicalResourceKey$1(def)))
2832
2895
  }, "records");
2833
2896
  }
2834
2897
  function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recordHref, registry, getTestId }) {
2835
2898
  const caps = useI18nCaps();
2836
- const resource = canonicalResourceKey(def);
2899
+ const resource = canonicalResourceKey$1(def);
2837
2900
  const access = useListAccess(resource);
2838
2901
  const titleVisible = access.status === "ready" && access.fieldVisible(displayField);
2839
2902
  const shouldFetch = query.length > 0 && titleVisible;
@@ -2848,6 +2911,7 @@ function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recor
2848
2911
  page: 1,
2849
2912
  pageSize
2850
2913
  },
2914
+ scope: "session",
2851
2915
  enabled: shouldFetch
2852
2916
  });
2853
2917
  const report = useCommandPaletteResults(`records:${resource}`);
@@ -2856,9 +2920,11 @@ function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recor
2856
2920
  report(rows.length);
2857
2921
  }, [report, rows.length]);
2858
2922
  if (rows.length === 0) return null;
2859
- const resourceLabel = resolveResourceLabel(caps, canonicalResourceKey(def), "single", def);
2923
+ const resourceLabel = resolveResourceLabel(caps, canonicalResourceKey$1(def), "single", def);
2924
+ const declaredIdField = def.data?.idField;
2925
+ const idField = typeof declaredIdField === "string" && declaredIdField.length > 0 ? declaredIdField : "id";
2860
2926
  return jsx(Fragment$1, { children: rows.map((row) => {
2861
- const idVal = row.id ?? row.event_id ?? row.job_id;
2927
+ const idVal = row[idField] ?? row.id ?? row.event_id ?? row.job_id;
2862
2928
  const id = idVal == null ? "" : String(idVal);
2863
2929
  if (!id) return null;
2864
2930
  const display = titleVisible ? row[displayField] : void 0;
@@ -3003,7 +3069,7 @@ function CommandPaletteActionsGroup({ actions, onSelect, getTestId, heading }) {
3003
3069
  function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, heading, createHref, prefix }) {
3004
3070
  const caps = useI18nCaps();
3005
3071
  const app = useApp();
3006
- const visibilityCtx = useVisibilityCtx();
3072
+ const isResourceVisible = useResourceVisible();
3007
3073
  const gate = useAvailabilityGate();
3008
3074
  const t = useOptionalT();
3009
3075
  const navigate = useRouteNavigate();
@@ -3011,11 +3077,11 @@ function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, headin
3011
3077
  const resolvedHeading = heading ?? t("commandPalette.create", { defaultValue: "Create" });
3012
3078
  const resolvedPrefix = prefix ?? t("commandPalette.createNew", { defaultValue: "Create new" });
3013
3079
  const visibleResources = useMemo(() => {
3014
- return app.registry.list().filter((r) => hasListPage(r)).filter((r) => gate(canonicalResourceKey(r), "create").state === "available").filter((r) => hasRecordPage(r, "create") || opensInQuick(app.registry, canonicalResourceKey(r), "create")).filter((r) => evaluateVisible(r.visible, visibilityCtx)).filter((r) => resourceFilter ? resourceFilter(r) : true);
3080
+ return app.registry.list().filter((r) => hasListPage(r)).filter((r) => gate(canonicalResourceKey$1(r), "create").state === "available").filter((r) => hasRecordPage(r, "create") || opensInQuick(app.registry, canonicalResourceKey$1(r), "create")).filter(isResourceVisible).filter((r) => resourceFilter ? resourceFilter(r) : true);
3015
3081
  }, [
3016
3082
  app,
3017
3083
  gate,
3018
- visibilityCtx,
3084
+ isResourceVisible,
3019
3085
  resourceFilter
3020
3086
  ]);
3021
3087
  if (visibleResources.length === 0) return null;
@@ -3029,13 +3095,13 @@ function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, headin
3029
3095
  return;
3030
3096
  }
3031
3097
  onSelect?.();
3032
- quick.create(canonicalResourceKey(def));
3098
+ quick.create(canonicalResourceKey$1(def));
3033
3099
  };
3034
- const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey(def), "single", def);
3100
+ const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey$1(def), "single", def);
3035
3101
  return jsx(CommandGroup, {
3036
3102
  heading: resolvedHeading,
3037
3103
  children: visibleResources.map((def) => {
3038
- const resource = canonicalResourceKey(def);
3104
+ const resource = canonicalResourceKey$1(def);
3039
3105
  const label = resourceLabel(def);
3040
3106
  const href = createHref?.(def) ?? resourceCreateHref(app.registry, def, resource);
3041
3107
  const value = `create:${resource}:${resolvedPrefix}:${label}`;
@@ -3097,7 +3163,7 @@ function AppActions() {
3097
3163
  variant: "outline",
3098
3164
  size: "sm",
3099
3165
  disabled: action.disabledReason !== void 0,
3100
- title: action.disabledReason,
3166
+ title: action.disabledReason === void 0 ? void 0 : reasonText(caps, action.disabledReason),
3101
3167
  "data-testid": `app-action:${action.name}`,
3102
3168
  children: action.disabledReason === void 0 ? jsx(RouterLink, {
3103
3169
  href,
@@ -3219,7 +3285,7 @@ function localizedSchema(t) {
3219
3285
  function LoginForm({ onSubmit, title, description, className }) {
3220
3286
  const tid = useTestIds();
3221
3287
  const t = useOptionalT();
3222
- const locale = useLocale();
3288
+ const caps = useI18nCaps();
3223
3289
  const resolvedTitle = title ?? t("auth.signIn", { defaultValue: "Sign in" });
3224
3290
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({
3225
3291
  resolver: zodResolver(localizedSchema(t)),
@@ -3234,9 +3300,11 @@ function LoginForm({ onSubmit, title, description, className }) {
3234
3300
  try {
3235
3301
  await onSubmit(values);
3236
3302
  } catch (e) {
3237
- const raw = e instanceof Error ? e.message : String(e);
3238
- const friendly = toFriendlyError(raw, "", locale);
3239
- setSubmitError(friendly.replaced ? `${friendly.title}. ${friendly.detail}` : credentialsMessage(raw, t));
3303
+ const dataCode = e !== null && typeof e === "object" && !(e instanceof Error) ? e.code : void 0;
3304
+ const raw = e instanceof Error ? e.message : technicalText(e) ?? (dataCode === void 0 ? String(e) : "");
3305
+ const friendly = toFriendlyError(raw, "", friendlyStringsFrom(t));
3306
+ const credentials = credentialsMessage(raw, t);
3307
+ setSubmitError(friendly.replaced ? `${friendly.title}. ${friendly.detail}` : typeof dataCode === "string" && credentials === raw ? resolveErrorText(e, caps) : credentials);
3240
3308
  }
3241
3309
  });
3242
3310
  return jsxs(Card, {
@@ -3361,9 +3429,108 @@ function AuthPage({ children, brand, footer, className }) {
3361
3429
  });
3362
3430
  }
3363
3431
  //#endregion
3432
+ //#region src/enum/enum-choice-group.tsx
3433
+ const NONE = "__none__";
3434
+ function isEmpty$1(value) {
3435
+ return value === null || value === void 0 || value === "";
3436
+ }
3437
+ function EnumChoiceGroup({ control, options, value, onChange, testid, name, id, required, disabled, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, className }) {
3438
+ const t = useOptionalT();
3439
+ const generatedName = React.useId();
3440
+ const groupName = name ?? generatedName;
3441
+ const empty = isEmpty$1(value);
3442
+ const noneLabel = t("field.none", { defaultValue: "None" });
3443
+ const pick = (next) => {
3444
+ if (!disabled) onChange(next);
3445
+ };
3446
+ const ariaProps = {
3447
+ "aria-invalid": ariaInvalid || void 0,
3448
+ "aria-describedby": ariaDescribedBy,
3449
+ ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {}
3450
+ };
3451
+ if (control === "segmented") {
3452
+ const segment = (key, label, active, select) => jsx("button", {
3453
+ type: "button",
3454
+ "aria-pressed": active,
3455
+ "data-state": active ? "active" : "inactive",
3456
+ "data-testid": `${testid}:option:${key}`,
3457
+ disabled,
3458
+ onClick: select,
3459
+ className: cn("inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all", "data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs", "disabled:pointer-events-none disabled:opacity-50", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"),
3460
+ children: label
3461
+ }, key);
3462
+ return jsxs("div", {
3463
+ role: "group",
3464
+ id,
3465
+ "aria-labelledby": ariaLabelledBy,
3466
+ "data-testid": testid,
3467
+ className: cn("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", className),
3468
+ ...ariaProps,
3469
+ children: [required ? null : segment(NONE, noneLabel, empty, () => pick(void 0)), options.map((option) => segment(option.value, jsxs(Fragment$1, { children: [option.icon ? jsx("span", {
3470
+ "data-testid": `${testid}:option:${option.value}:icon`,
3471
+ className: "contents",
3472
+ children: jsx(Icon, {
3473
+ name: option.icon,
3474
+ className: "size-3.5 shrink-0"
3475
+ })
3476
+ }) : null, option.label] }), value === option.value, () => pick(option.value)))]
3477
+ });
3478
+ }
3479
+ const cards = control === "cards";
3480
+ const choice = (key, option, label, checked, select) => {
3481
+ const inputId = `${id ?? groupName}:${key}`;
3482
+ return jsxs("label", {
3483
+ htmlFor: inputId,
3484
+ className: cn("flex items-start gap-2.5", cards && "cursor-pointer rounded-lg border px-3 py-2.5 transition-colors hover:bg-accent/50 has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring", cards && (checked ? "border-primary" : "border-input"), disabled && "cursor-not-allowed opacity-60"),
3485
+ children: [jsx("input", {
3486
+ type: "radio",
3487
+ id: inputId,
3488
+ name: groupName,
3489
+ value: key,
3490
+ checked,
3491
+ onChange: select,
3492
+ disabled,
3493
+ ...ariaProps,
3494
+ "data-testid": `${testid}:option:${key}`,
3495
+ className: cn("h-4 w-4 shrink-0 accent-primary", cards ? "mt-0.5" : "mt-1")
3496
+ }), jsxs("span", {
3497
+ className: "flex min-w-0 flex-col gap-0.5",
3498
+ children: [jsxs("span", {
3499
+ className: cn("flex items-center gap-1.5", cards && "font-medium"),
3500
+ children: [option?.icon ? jsx("span", {
3501
+ "data-testid": `${testid}:option:${key}:icon`,
3502
+ className: "contents",
3503
+ children: jsx(Icon, {
3504
+ name: option.icon,
3505
+ className: "size-4 shrink-0"
3506
+ })
3507
+ }) : null, jsx("span", { children: label })]
3508
+ }), option?.summary === void 0 || option.summary === "" ? null : jsx("span", {
3509
+ "data-testid": `${testid}:option:${key}:doc`,
3510
+ className: "text-sm text-muted-foreground",
3511
+ children: option.summary
3512
+ })]
3513
+ })]
3514
+ }, key);
3515
+ };
3516
+ return jsxs("div", {
3517
+ role: "radiogroup",
3518
+ id,
3519
+ "aria-labelledby": ariaLabelledBy,
3520
+ "data-testid": testid,
3521
+ "aria-required": required,
3522
+ className: cn("flex flex-col", cards ? "gap-2" : "gap-1.5", className),
3523
+ children: [required ? null : choice(NONE, void 0, noneLabel, empty, () => pick(void 0)), options.map((option) => choice(option.value, option, option.label, value === option.value, () => pick(option.value)))]
3524
+ });
3525
+ }
3526
+ //#endregion
3364
3527
  //#region src/enum/enum-picker.tsx
3365
3528
  const ENUM_PICKER_CLEAR_SENTINEL = "__buro_clear__";
3366
- function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id, testid, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, className, clearable = false, clearLabel = "All" }) {
3529
+ function withSummary(summary) {
3530
+ return summary === void 0 ? {} : { summary };
3531
+ }
3532
+ function EnumPicker({ enumName: enumInput, value, onChange, control = "select", name, id, testid, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, className, clearable = false, clearLabel = "All" }) {
3533
+ const enumName = enumBindingKey(enumInput) ?? "";
3367
3534
  const app = useApp();
3368
3535
  const t = useOptionalT();
3369
3536
  const def = app.registry.enums.get(enumName);
@@ -3394,63 +3561,43 @@ function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id,
3394
3561
  children: jsx(SelectValue, { placeholder: t("field.loading", { defaultValue: "Loading…" }) })
3395
3562
  }), jsx(SelectContent, {})]
3396
3563
  });
3397
- if (error || !values) return jsxs("span", {
3398
- "data-testid": `${tid}:error`,
3564
+ if (error || !values) return jsxs(LoadError, {
3565
+ surface: "picker",
3566
+ error,
3567
+ testId: `${tid}:error`,
3399
3568
  className: "text-muted-foreground",
3400
- title: error?.message,
3569
+ fallback: {
3570
+ key: "relation.failed",
3571
+ default: "Failed to load options."
3572
+ },
3401
3573
  children: [
3402
3574
  "(failed to load enum ‘",
3403
3575
  enumName,
3404
3576
  "’)"
3405
3577
  ]
3406
3578
  });
3407
- if (renderAs === "radio") return jsx("div", {
3408
- role: "radiogroup",
3409
- "data-testid": tid,
3410
- className: cn("flex flex-col gap-2", className),
3411
- "aria-required": required,
3412
- children: values.map((entry) => {
3413
- const inputId = `${id ?? tid}:${entry.value}`;
3414
- return jsxs("label", {
3415
- htmlFor: inputId,
3416
- className: "flex items-center gap-2",
3417
- children: [jsx("input", {
3418
- type: "radio",
3419
- id: inputId,
3420
- name: name ?? tid,
3421
- value: entry.value,
3422
- checked: value === entry.value,
3423
- onChange: (e) => onChange(e.target.value),
3424
- disabled,
3425
- "aria-invalid": ariaInvalid || void 0,
3426
- "aria-describedby": ariaDescribedBy,
3427
- ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3428
- "data-testid": `${tid}:option:${entry.value}`,
3429
- className: "h-4 w-4"
3430
- }), jsx("span", { children: resolveEnumLabel(caps, ns, entry.value, entry.label) })]
3431
- }, entry.value);
3432
- })
3433
- });
3434
- if (renderAs === "segmented") return jsx("div", {
3435
- role: "group",
3436
- "data-testid": tid,
3437
- className: cn("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", className),
3438
- "aria-invalid": ariaInvalid || void 0,
3439
- "aria-describedby": ariaDescribedBy,
3440
- ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3441
- children: values.map((entry) => {
3442
- const isActive = value === entry.value;
3443
- return jsx("button", {
3444
- type: "button",
3445
- "aria-pressed": isActive,
3446
- "data-state": isActive ? "active" : "inactive",
3447
- "data-testid": `${tid}:option:${entry.value}`,
3448
- disabled,
3449
- onClick: () => onChange(entry.value),
3450
- className: cn("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all", "data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs", "disabled:pointer-events-none disabled:opacity-50", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"),
3451
- children: resolveEnumLabel(caps, ns, entry.value, entry.label)
3452
- }, entry.value);
3453
- })
3579
+ const labelOf = (entry) => resolveEnumLabel(caps, ns, entry.value, enumValueText(def, entry.value) ?? entry.label);
3580
+ const summaryOf = (entry) => resolveDoc(caps, `enum/${ns}/${entry.value}`)?.summary || void 0;
3581
+ if (control !== "select") return jsx(EnumChoiceGroup, {
3582
+ control,
3583
+ options: values.map((entry) => ({
3584
+ value: entry.value,
3585
+ label: labelOf(entry),
3586
+ ...entry.icon === void 0 ? {} : { icon: entry.icon },
3587
+ ...withSummary(summaryOf(entry))
3588
+ })),
3589
+ value,
3590
+ onChange,
3591
+ testid: tid,
3592
+ name,
3593
+ id,
3594
+ required,
3595
+ disabled,
3596
+ ariaInvalid,
3597
+ ariaDescribedBy,
3598
+ ariaErrorMessage,
3599
+ ariaLabelledBy,
3600
+ className
3454
3601
  });
3455
3602
  const selectValue = value == null ? clearable ? ENUM_PICKER_CLEAR_SENTINEL : "" : value;
3456
3603
  const handleValueChange = (next) => {
@@ -3480,36 +3627,49 @@ function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id,
3480
3627
  "data-testid": `${tid}:option:__clear__`,
3481
3628
  children: clearLabel
3482
3629
  }), values.map((entry) => {
3483
- const optionLabel = resolveEnumLabel(caps, ns, entry.value, entry.label);
3484
- const optionDoc = resolveDoc(caps, `enum/${ns}/${entry.value}`)?.summary;
3630
+ const optionLabel = labelOf(entry);
3631
+ const optionDoc = summaryOf(entry);
3485
3632
  return jsx(SelectItem, {
3486
3633
  value: entry.value,
3487
3634
  "data-testid": `${tid}:option:${entry.value}`,
3488
- children: optionDoc === void 0 ? optionLabel : jsxs("span", {
3489
- className: "flex flex-col gap-0.5",
3490
- children: [jsx("span", { children: optionLabel }), jsx("span", {
3491
- className: "text-xs leading-snug text-muted-foreground",
3492
- "data-testid": `${tid}:option:${entry.value}:doc`,
3493
- children: optionDoc
3494
- })]
3495
- })
3635
+ description: optionDoc === void 0 ? void 0 : jsx("span", {
3636
+ "data-testid": `${tid}:option:${entry.value}:doc`,
3637
+ children: optionDoc
3638
+ }),
3639
+ children: optionLabel
3496
3640
  }, entry.value);
3497
3641
  })] })]
3498
3642
  });
3499
3643
  }
3500
3644
  //#endregion
3645
+ //#region src/enum/resolve-enum-control.ts
3646
+ const warned = new Set();
3647
+ function resolveEnumControl(fieldControl, enumDef, where) {
3648
+ if (isEnumControl(fieldControl)) return fieldControl;
3649
+ if (fieldControl !== void 0) {
3650
+ const shown = typeof fieldControl === "string" ? `'${fieldControl}'` : String(fieldControl);
3651
+ const key = `${where}\u0000${shown}`;
3652
+ if (!warned.has(key)) {
3653
+ warned.add(key);
3654
+ console.warn(`[buro/ui-shadcn] ${where}: unknown enum control ${shown}; expected one of ${ENUM_CONTROLS.map((control) => `'${control}'`).join(", ")}. Using the enum's control, else 'select'.`);
3655
+ }
3656
+ }
3657
+ return enumDef?.control ?? "select";
3658
+ }
3659
+ //#endregion
3501
3660
  //#region src/forms/issue-severity.ts
3502
3661
  function isErrorIssue(issue) {
3503
3662
  return issue.severity === "error";
3504
3663
  }
3505
3664
  //#endregion
3506
3665
  //#region src/fields/boolean.tsx
3507
- function BooleanDisplay({ value }) {
3666
+ function BooleanDisplay({ value, text }) {
3508
3667
  const t = useOptionalT();
3509
3668
  if (value == null) return jsx("span", {
3510
3669
  className: "text-muted-foreground",
3511
3670
  children: "—"
3512
3671
  });
3672
+ if (text !== void 0) return jsx("span", { children: text });
3513
3673
  return jsx("span", { children: value ? t("boolean.yes", { defaultValue: "Yes" }) : t("boolean.no", { defaultValue: "No" }) });
3514
3674
  }
3515
3675
  function BooleanInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
@@ -3528,17 +3688,32 @@ function BooleanInput({ name, id, value, onChange, testid, disabled, required, a
3528
3688
  }
3529
3689
  //#endregion
3530
3690
  //#region src/fields/date.tsx
3531
- function DateDisplay({ value }) {
3691
+ function DateDisplay({ value, text }) {
3692
+ return jsx(TemporalDisplay, {
3693
+ value,
3694
+ text,
3695
+ format: formatDate
3696
+ });
3697
+ }
3698
+ function DateTimeDisplay({ value, text }) {
3699
+ return jsx(TemporalDisplay, {
3700
+ value,
3701
+ text,
3702
+ format: formatDateTime
3703
+ });
3704
+ }
3705
+ function TemporalDisplay({ value, text, format }) {
3532
3706
  const locale = useOptionalLocale();
3533
3707
  if (value == null || value === "") return jsx("span", {
3534
3708
  className: "text-muted-foreground",
3535
3709
  children: "—"
3536
3710
  });
3711
+ if (text !== void 0) return jsx("span", { children: text });
3537
3712
  const d = new Date(String(value));
3538
3713
  if (Number.isNaN(d.getTime())) return jsx("span", { children: String(value) });
3539
3714
  return jsx("span", {
3540
3715
  className: "whitespace-nowrap",
3541
- children: formatDate(d, locale)
3716
+ children: format(d, locale)
3542
3717
  });
3543
3718
  }
3544
3719
  function toIsoDate(v) {
@@ -3549,13 +3724,29 @@ function toIsoDate(v) {
3549
3724
  if (Number.isNaN(d.getTime())) return "";
3550
3725
  return d.toISOString().slice(0, 10);
3551
3726
  }
3552
- function DateInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
3727
+ function DateInput(props) {
3728
+ return jsx(TemporalInput, {
3729
+ ...props,
3730
+ type: "date",
3731
+ toText: toIsoDate,
3732
+ fromText: (text) => text || null
3733
+ });
3734
+ }
3735
+ function DateTimeInput(props) {
3736
+ return jsx(TemporalInput, {
3737
+ ...props,
3738
+ type: "datetime-local",
3739
+ toText: toLocalDateTimeText,
3740
+ fromText: (text) => fromLocalDateTimeText(text) ?? null
3741
+ });
3742
+ }
3743
+ function TemporalInput({ type, toText, fromText, name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
3553
3744
  return jsx(Input, {
3554
3745
  name,
3555
3746
  id,
3556
- type: "date",
3557
- value: toIsoDate(value),
3558
- onChange: (e) => onChange(e.target.value || null),
3747
+ type,
3748
+ value: toText(value),
3749
+ onChange: (e) => onChange(fromText(e.target.value)),
3559
3750
  "data-testid": testid,
3560
3751
  disabled,
3561
3752
  required,
@@ -3567,23 +3758,15 @@ function DateInput({ name, id, value, onChange, testid, disabled, required, aria
3567
3758
  }
3568
3759
  //#endregion
3569
3760
  //#region src/fields/enum.tsx
3570
- function optionLabel(caps, field, value, inline) {
3571
- const ns = enumNamespaceOf(field);
3572
- if (ns) return resolveEnumLabel(caps, ns, value, inline);
3573
- return field ? resolveContentSlot(caps, [fieldOptionKey(field.name, value)], { default: inline }) : inline;
3574
- }
3575
- function useOptionCaps(resource) {
3576
- return useI18nCaps(useContext(ResourceContext).resource ?? resource);
3577
- }
3578
3761
  function EnumDisplay({ value, options, field, resource }) {
3579
3762
  const caps = useOptionCaps(resource);
3763
+ const enumDef = useBoundEnum(field);
3580
3764
  if (value == null || value === "") return jsx("span", {
3581
3765
  className: "text-muted-foreground",
3582
3766
  children: "—"
3583
3767
  });
3584
3768
  const opt = options?.find((o) => o.value === String(value));
3585
- const inline = typeof opt?.label === "string" ? opt.label : String(value);
3586
- const label = optionLabel(caps, field, String(value), inline);
3769
+ const label = resolveOptionLabel(caps, field, String(value), opt, enumDef);
3587
3770
  return jsxs("span", {
3588
3771
  className: "inline-flex items-center gap-1",
3589
3772
  children: [opt?.icon ? jsx(Icon, {
@@ -3592,9 +3775,36 @@ function EnumDisplay({ value, options, field, resource }) {
3592
3775
  }) : null, label]
3593
3776
  });
3594
3777
  }
3595
- function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, resource }) {
3778
+ function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, resource }) {
3596
3779
  const t = useOptionalT();
3597
3780
  const caps = useOptionCaps(resource);
3781
+ const enumDef = useBoundEnum(field);
3782
+ const ns = enumNamespaceOf(field);
3783
+ const control = resolveEnumControl(field?.ui?.control, enumDef, `field ${field?.name ?? name}`);
3784
+ const resolved = (options ?? []).map((o) => {
3785
+ const summary = ns === void 0 ? void 0 : resolveDoc(caps, `enum/${ns}/${o.value}`)?.summary;
3786
+ return {
3787
+ value: o.value,
3788
+ label: resolveOptionLabel(caps, field, o.value, o, enumDef),
3789
+ ...o.icon === void 0 ? {} : { icon: o.icon },
3790
+ ...summary === void 0 ? {} : { summary }
3791
+ };
3792
+ });
3793
+ if (control !== "select") return jsx(EnumChoiceGroup, {
3794
+ control,
3795
+ options: resolved,
3796
+ value,
3797
+ onChange,
3798
+ testid: testid ?? name,
3799
+ name,
3800
+ id,
3801
+ required,
3802
+ disabled,
3803
+ ariaInvalid,
3804
+ ariaDescribedBy,
3805
+ ariaErrorMessage,
3806
+ ariaLabelledBy
3807
+ });
3598
3808
  return jsxs(Select, {
3599
3809
  value: value ?? "",
3600
3810
  onValueChange: onChange,
@@ -3609,14 +3819,19 @@ function EnumInput({ name, id, value, onChange, testid, options, field, placehol
3609
3819
  "aria-describedby": ariaDescribedBy,
3610
3820
  ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3611
3821
  children: jsx(SelectValue, { placeholder: placeholder ?? t("field.select", { defaultValue: "Select…" }) })
3612
- }), jsx(SelectContent, { children: options?.map((o) => jsx(SelectItem, {
3822
+ }), jsx(SelectContent, { children: resolved.map((o) => jsx(SelectItem, {
3613
3823
  value: o.value,
3824
+ "data-testid": testid === void 0 ? void 0 : `${testid}:option:${o.value}`,
3825
+ description: o.summary === void 0 ? void 0 : jsx("span", {
3826
+ "data-testid": testid === void 0 ? void 0 : `${testid}:option:${o.value}:doc`,
3827
+ children: o.summary
3828
+ }),
3614
3829
  children: jsxs("span", {
3615
3830
  className: "inline-flex items-center gap-1.5",
3616
3831
  children: [o.icon ? jsx(Icon, {
3617
3832
  name: o.icon,
3618
3833
  className: "size-3.5 shrink-0 text-muted-foreground"
3619
- }) : null, optionLabel(caps, field, o.value, typeof o.label === "string" ? o.label : o.value)]
3834
+ }) : null, o.label]
3620
3835
  })
3621
3836
  }, o.value)) })]
3622
3837
  });
@@ -3989,6 +4204,7 @@ function JsonDisplay({ value, presentation, testid }) {
3989
4204
  });
3990
4205
  }
3991
4206
  function JsonInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
4207
+ const t = useOptionalT();
3992
4208
  const seed = React.useMemo(() => {
3993
4209
  if (value == null || value === "") return "";
3994
4210
  return toDisplayText(value).text;
@@ -4032,13 +4248,13 @@ function JsonInput({ name, id, value, onChange, testid, disabled, required, aria
4032
4248
  }), invalid && jsx("p", {
4033
4249
  className: "text-[13px] leading-snug text-destructive",
4034
4250
  "data-testid": `${testid}-json-error`,
4035
- children: "Некорректный JSON — изменения не будут сохранены, пока он не станет валидным."
4251
+ children: t("field.jsonInvalid", { defaultValue: "Invalid JSON. Changes will not be saved until it is valid." })
4036
4252
  })]
4037
4253
  });
4038
4254
  }
4039
4255
  //#endregion
4040
4256
  //#region src/fields/lazy-relation-many.tsx
4041
- const RelationManyPickerInput = lazy(() => import("./relation-many-picker-gdAOD-qK.js").then((module) => ({ default: module.RelationManyPickerInput })));
4257
+ const RelationManyPickerInput = lazy(() => import("./relation-many-picker-BJhaEqxz.js").then((module) => ({ default: module.RelationManyPickerInput })));
4042
4258
  function LazyRelationManyInput(props) {
4043
4259
  return jsx(Suspense, {
4044
4260
  fallback: jsx(Skeleton, {
@@ -4051,8 +4267,8 @@ function LazyRelationManyInput(props) {
4051
4267
  }
4052
4268
  //#endregion
4053
4269
  //#region src/fields/lazy-rich-text.tsx
4054
- const RichTextDisplay = lazy(() => import("./rich-text-DFerywgz.js").then((module) => ({ default: module.RichTextDisplay })));
4055
- const RichTextInput = lazy(() => import("./rich-text-DFerywgz.js").then((module) => ({ default: module.RichTextInput })));
4270
+ const RichTextDisplay = lazy(() => import("./rich-text-RcPBf6hr.js").then((module) => ({ default: module.RichTextDisplay })));
4271
+ const RichTextInput = lazy(() => import("./rich-text-RcPBf6hr.js").then((module) => ({ default: module.RichTextInput })));
4056
4272
  function RichTextDisplayLoading() {
4057
4273
  const t = useOptionalT();
4058
4274
  return jsx("div", {
@@ -4109,12 +4325,13 @@ function isIdentifier(field) {
4109
4325
  const n = field?.name;
4110
4326
  return n === "id" || typeof n === "string" && n.endsWith("_id");
4111
4327
  }
4112
- function NumberDisplay({ value, field }) {
4328
+ function NumberDisplay({ value, field, text }) {
4113
4329
  const locale = useOptionalLocale();
4114
4330
  if (value == null || value === "") return jsx("span", {
4115
4331
  className: "text-muted-foreground",
4116
4332
  children: "—"
4117
4333
  });
4334
+ if (text !== void 0) return jsx("span", { children: text });
4118
4335
  let raw = value;
4119
4336
  if (typeof raw === "string") raw = unquote(raw);
4120
4337
  const n = typeof raw === "number" ? raw : Number(raw);
@@ -4149,6 +4366,196 @@ function NumberInput({ name, id, value, onChange, testid, placeholder, disabled,
4149
4366
  });
4150
4367
  }
4151
4368
  //#endregion
4369
+ //#region src/fields/link-keys-display.tsx
4370
+ const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4371
+ function qualify(name) {
4372
+ try {
4373
+ const ref = normalizeEntityRef(name);
4374
+ return ref.namespace === "default" ? ref.id : `${ref.namespace}:${ref.id}`;
4375
+ } catch {
4376
+ return;
4377
+ }
4378
+ }
4379
+ function idFieldOf(def) {
4380
+ const declared = (def?.data)?.idField;
4381
+ return typeof declared === "string" && declared.length > 0 ? declared : "id";
4382
+ }
4383
+ function byOf(field) {
4384
+ const by = isRecord$1(field) && isRecord$1(field.meta) ? field.meta.by : void 0;
4385
+ return typeof by === "string" && by.length > 0 ? by : void 0;
4386
+ }
4387
+ const chipClassName = "inline-flex items-center rounded-md border border-border bg-muted/40 px-1.5 py-0.5 text-xs";
4388
+ function LinkKeysDisplay(props) {
4389
+ const { field, record } = props;
4390
+ const owner = props.owner ?? props.resource ?? "";
4391
+ const name = field?.name ?? "";
4392
+ const app = useApp();
4393
+ const t = useOptionalT();
4394
+ const tid = useTestIds();
4395
+ const ownerDef = owner === "" ? void 0 : app.registry.get(owner);
4396
+ const declaration = ownerDef === void 0 ? void 0 : declaredField(ownerDef, name);
4397
+ const link = isRecord$1(declaration) ? linkOf(declaration) : void 0;
4398
+ const junctionName = link === void 0 ? void 0 : qualify(link.junction);
4399
+ const junctionDef = junctionName === void 0 ? void 0 : app.registry.get(junctionName);
4400
+ const junctionField = (key) => junctionDef === void 0 ? void 0 : declaredField(junctionDef, key);
4401
+ const ownerField = link === void 0 ? idFieldOf(ownerDef) : byOf(junctionField(link.sourceKey)) ?? idFieldOf(ownerDef);
4402
+ const targetBy = link === void 0 ? void 0 : byOf(junctionField(link.targetKey));
4403
+ const ownerKey = record?.[ownerField];
4404
+ const { keys, complete, isLoading, error } = useLinkKeys(owner, name, ownerKey);
4405
+ const [missed, setMissed] = useState(() => new Set());
4406
+ const onMiss = useCallback((key, isMiss) => {
4407
+ setMissed((current) => {
4408
+ if (current.has(key) === isMiss) return current;
4409
+ const next = new Set(current);
4410
+ if (isMiss) next.add(key);
4411
+ else next.delete(key);
4412
+ return next;
4413
+ });
4414
+ }, []);
4415
+ const target = (isRecord$1(declaration) && isRecord$1(declaration.meta) ? declaration.meta.to : void 0) ?? field?.meta?.to;
4416
+ const chipField = useMemo(() => ({
4417
+ name,
4418
+ meta: {
4419
+ ...target === void 0 ? {} : { to: target },
4420
+ ...targetBy === void 0 ? {} : { by: targetBy }
4421
+ },
4422
+ ...field?.ui === void 0 ? {} : { ui: field.ui }
4423
+ }), [
4424
+ name,
4425
+ target,
4426
+ field?.ui,
4427
+ targetBy
4428
+ ]);
4429
+ const chipsId = tid.linkChips(owner, name);
4430
+ if (error) return jsx(LoadError, {
4431
+ surface: "relation",
4432
+ error,
4433
+ ...typeof target === "string" ? { resource: target } : {},
4434
+ testId: tid.linkError(owner, name),
4435
+ className: "text-destructive text-xs",
4436
+ attributes: { role: "status" },
4437
+ fallback: {
4438
+ key: "field.link.failed",
4439
+ default: "Links could not be loaded"
4440
+ }
4441
+ });
4442
+ if (keys === void 0) {
4443
+ if (!isLoading) return jsx("span", {
4444
+ "data-testid": "relation-many-display:empty",
4445
+ className: "text-muted-foreground",
4446
+ children: "—"
4447
+ });
4448
+ return jsx("span", {
4449
+ "data-testid": `${chipsId}:loading`,
4450
+ className: "inline-block h-5 w-20 animate-pulse rounded bg-muted",
4451
+ "aria-busy": "true"
4452
+ });
4453
+ }
4454
+ if (!complete) return jsx("span", {
4455
+ "data-testid": chipsId,
4456
+ className: "inline-flex flex-wrap gap-1",
4457
+ children: jsx("span", {
4458
+ "data-testid": tid.linkIncomplete(owner, name),
4459
+ className: "text-muted-foreground",
4460
+ children: t("field.link.incomplete", {
4461
+ count: keys.length,
4462
+ defaultValue: "More than {{count}} links"
4463
+ })
4464
+ })
4465
+ });
4466
+ if (keys.length === 0) return jsx("span", {
4467
+ "data-testid": "relation-many-display:empty",
4468
+ className: "text-muted-foreground",
4469
+ children: "—"
4470
+ });
4471
+ const hidden = keys.filter((key) => missed.has(String(key))).length;
4472
+ return jsxs("span", {
4473
+ "data-testid": chipsId,
4474
+ className: "inline-flex flex-wrap gap-1",
4475
+ children: [keys.map((key) => jsx(LinkChip, {
4476
+ value: key,
4477
+ field: chipField,
4478
+ onMiss
4479
+ }, String(key))), hidden > 0 ? jsx("span", {
4480
+ "data-testid": tid.linkHidden(owner, name),
4481
+ className: `${chipClassName} text-muted-foreground`,
4482
+ children: t("field.link.hidden", {
4483
+ count: hidden,
4484
+ defaultValue: "{{count}} not visible"
4485
+ })
4486
+ }) : null]
4487
+ });
4488
+ }
4489
+ function LinkChip({ value, field, onMiss }) {
4490
+ const gate = useEntryGate();
4491
+ const meta = field.meta;
4492
+ let to;
4493
+ try {
4494
+ to = meta?.to === void 0 ? void 0 : resourceKey(meta.to);
4495
+ } catch {
4496
+ to = void 0;
4497
+ }
4498
+ const answer = to === void 0 ? void 0 : gate(to, "show");
4499
+ if (!(to !== void 0 && answer !== void 0 && answer.state !== "undecided" && answer.state !== "hidden" && answer.state !== "disabled")) return jsx("span", {
4500
+ className: chipClassName,
4501
+ children: jsx(RelationDisplay, {
4502
+ value,
4503
+ field
4504
+ })
4505
+ });
4506
+ return jsx(ReadChip, {
4507
+ to,
4508
+ value,
4509
+ field,
4510
+ onMiss
4511
+ });
4512
+ }
4513
+ function ReadChip({ to, value, field, onMiss }) {
4514
+ const app = useApp();
4515
+ const by = field.meta?.by;
4516
+ const { record, isLoading, error, ambiguous } = useRelatedRecord(to, value, typeof by === "string" ? { by } : void 0);
4517
+ const miss = app.registry.get(to) !== void 0 && !isLoading && !error && !ambiguous && !record;
4518
+ const key = String(value);
4519
+ useEffect(() => {
4520
+ onMiss(key, miss);
4521
+ }, [
4522
+ onMiss,
4523
+ key,
4524
+ miss
4525
+ ]);
4526
+ useEffect(() => () => onMiss(key, false), [onMiss, key]);
4527
+ if (miss) return null;
4528
+ return jsx("span", {
4529
+ className: chipClassName,
4530
+ children: jsx(RelationDisplay, {
4531
+ value,
4532
+ field
4533
+ })
4534
+ });
4535
+ }
4536
+ function RelationManyDisplay(props) {
4537
+ const app = useApp();
4538
+ const owner = props.owner ?? props.resource ?? "";
4539
+ const ownerDef = owner === "" ? void 0 : app.registry.get(owner);
4540
+ const declared = ownerDef === void 0 || props.field?.name === void 0 ? void 0 : declaredField(ownerDef, props.field.name);
4541
+ if (isRecord$1(declared) && linkOf(declared) !== void 0) return jsx(LinkKeysDisplay, { ...props });
4542
+ const { value, field } = props;
4543
+ const keys = Array.isArray(value) ? value : [];
4544
+ if (keys.length === 0) return jsx("span", {
4545
+ "data-testid": "relation-many-display:empty",
4546
+ className: "text-muted-foreground",
4547
+ children: "—"
4548
+ });
4549
+ return jsx("span", {
4550
+ className: "inline-flex flex-wrap gap-x-2",
4551
+ "data-testid": "relation-many-display",
4552
+ children: keys.map((key, index) => jsx(RelationDisplay, {
4553
+ value: key,
4554
+ field
4555
+ }, `${String(key)}:${index}`))
4556
+ });
4557
+ }
4558
+ //#endregion
4152
4559
  //#region src/fields/relation-meta-schema.ts
4153
4560
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4154
4561
  const relationMetaSchema = Object.freeze({ "~standard": Object.freeze({
@@ -4160,7 +4567,7 @@ const relationMetaSchema = Object.freeze({ "~standard": Object.freeze({
4160
4567
  path: []
4161
4568
  }] };
4162
4569
  const issues = [];
4163
- const { to, by, resolve, searchField, candidates } = value;
4570
+ const { to, by, resolve, searchField, candidates, scope } = value;
4164
4571
  if (to !== void 0 && !(typeof to === "string" && to.length > 0) && !(isRecord(to) && (typeof to.id === "string" || isRecord(to.ref)))) issues.push({
4165
4572
  message: "must be a resource name or reference",
4166
4573
  path: ["to"]
@@ -4181,16 +4588,21 @@ const relationMetaSchema = Object.freeze({ "~standard": Object.freeze({
4181
4588
  message: "must be an array of conditions",
4182
4589
  path: ["candidates"]
4183
4590
  });
4591
+ if (scope !== void 0 && scope !== "context" && scope !== "session" && scope !== "none") issues.push({
4592
+ message: "must be 'context', 'session' or 'none'",
4593
+ path: ["scope"]
4594
+ });
4184
4595
  return issues.length > 0 ? { issues } : { value };
4185
4596
  }
4186
4597
  }) });
4187
4598
  //#endregion
4188
4599
  //#region src/fields/text.tsx
4189
- function TextDisplay({ value }) {
4600
+ function TextDisplay({ value, text: formatted }) {
4190
4601
  if (value == null || value === "") return jsx("span", {
4191
4602
  className: "text-muted-foreground",
4192
4603
  children: "—"
4193
4604
  });
4605
+ if (formatted !== void 0) return jsx("span", { children: formatted });
4194
4606
  const { text, isStructured } = toDisplayText(value);
4195
4607
  return isStructured ? jsx("span", {
4196
4608
  className: "font-mono text-xs break-all",
@@ -4219,23 +4631,26 @@ function TextInput({ name, id, value, onChange, testid, placeholder, disabled, r
4219
4631
  }
4220
4632
  //#endregion
4221
4633
  //#region src/fields/textarea.tsx
4222
- function TextareaDisplay({ value }) {
4634
+ function TextareaDisplay({ value, text: formatted }) {
4223
4635
  if (value == null || value === "") return jsx("span", {
4224
4636
  className: "text-muted-foreground",
4225
4637
  children: "—"
4226
4638
  });
4227
- const { text, isStructured } = toDisplayText(value);
4639
+ const { text, isStructured } = formatted === void 0 ? toDisplayText(value) : {
4640
+ text: formatted,
4641
+ isStructured: false
4642
+ };
4228
4643
  return jsx("pre", {
4229
4644
  className: isStructured ? "whitespace-pre-wrap break-all font-mono text-xs" : "whitespace-pre-wrap font-sans text-sm",
4230
4645
  children: text
4231
4646
  });
4232
4647
  }
4233
- function CodeDisplay({ value, presentation, testid }) {
4648
+ function CodeDisplay({ value, text: formatted, presentation, testid }) {
4234
4649
  if (value == null || value === "") return jsx("span", {
4235
4650
  className: "text-muted-foreground",
4236
4651
  children: "—"
4237
4652
  });
4238
- const { text } = toDisplayText(value);
4653
+ const text = formatted ?? toDisplayText(value).text;
4239
4654
  return jsx("pre", {
4240
4655
  "data-testid": testid,
4241
4656
  className: presentation === "plain" ? "overflow-x-auto whitespace-pre font-mono text-xs leading-relaxed" : "overflow-x-auto whitespace-pre rounded-md border bg-muted/40 p-3 font-mono text-xs leading-relaxed",
@@ -4279,30 +4694,39 @@ function TextareaInput({ name, id, value, onChange, testid, placeholder, disable
4279
4694
  //#endregion
4280
4695
  //#region src/fields/default-renderers.ts
4281
4696
  function legacyField(props) {
4282
- const name = typeof props.path.at(-1) === "string" ? props.path.at(-1) : "field";
4697
+ const name = optionFieldName(props.path);
4698
+ const texts = props.field.texts;
4283
4699
  return Object.freeze({
4284
4700
  name,
4285
4701
  meta: props.meta,
4286
4702
  ui: props.ui,
4287
- codec: props.field.codec
4703
+ ...texts === void 0 ? {} : { texts }
4288
4704
  });
4289
4705
  }
4290
- function legacyOptions(options) {
4706
+ function legacyOptions(options, field) {
4291
4707
  if (options.length === 0) return void 0;
4292
- return options.map((option) => Object.freeze({
4293
- value: String(option.value ?? ""),
4294
- label: option.label,
4295
- ...typeof option.meta?.icon === "string" ? { icon: option.meta.icon } : {}
4296
- }));
4708
+ return options.map((option) => {
4709
+ const value = String(option.value ?? "");
4710
+ return Object.freeze({
4711
+ value,
4712
+ label: fieldOptionText(field, value) ?? value,
4713
+ ...typeof option.meta?.icon === "string" ? { icon: option.meta.icon } : {}
4714
+ });
4715
+ });
4716
+ }
4717
+ function registryKey(ref) {
4718
+ return ref.namespace === "default" ? ref.id : formatEntityRef(ref);
4297
4719
  }
4298
4720
  function display(renderer) {
4299
4721
  return function ShadcnDisplay(props) {
4300
4722
  return createElement(renderer, {
4301
4723
  value: props.value,
4724
+ ...props.text === void 0 ? {} : { text: props.text },
4302
4725
  multiple: props.multiple,
4303
4726
  field: legacyField(props),
4304
- options: legacyOptions(props.options),
4727
+ options: legacyOptions(props.options, props.field),
4305
4728
  resource: props.resource.id,
4729
+ owner: registryKey(props.resource),
4306
4730
  record: props.record,
4307
4731
  presentation: props.presentation,
4308
4732
  testid: props.testIds.display
@@ -4327,7 +4751,7 @@ function input(renderer) {
4327
4751
  multiple: props.multiple,
4328
4752
  onChange: props.onChange ?? (() => void 0),
4329
4753
  testid: props.testIds.input,
4330
- options: legacyOptions(props.options),
4754
+ options: legacyOptions(props.options, props.field),
4331
4755
  placeholder: typeof props.ui.placeholder === "string" ? props.ui.placeholder : void 0,
4332
4756
  disabled: props.disabled,
4333
4757
  required: props.field.required === true,
@@ -4335,12 +4759,14 @@ function input(renderer) {
4335
4759
  ariaInvalid: props.issues.some(isErrorIssue),
4336
4760
  ariaDescribedBy: props.accessibility.description,
4337
4761
  ariaErrorMessage: props.accessibility.error,
4762
+ ariaLabelledBy: props.accessibility.label,
4338
4763
  field: legacyField(props),
4339
4764
  resource: props.resource.id,
4340
4765
  record: props.record,
4341
4766
  relationSeeds: props.relationSeeds,
4342
4767
  fixedFilters: props.fixedFilters,
4343
- metaSources: props.metaSources
4768
+ metaSources: props.metaSources,
4769
+ link: props.link
4344
4770
  });
4345
4771
  };
4346
4772
  }
@@ -4388,9 +4814,27 @@ function scalarOrCollection(renderer, layout) {
4388
4814
  });
4389
4815
  }
4390
4816
  function enumName(ui) {
4391
- const value = ui.enum;
4392
- if (typeof value === "string" && value.length > 0) return value;
4393
- if (value !== null && typeof value === "object" && typeof value.id === "string") return value.id;
4817
+ return enumBindingKey(ui.enum);
4818
+ }
4819
+ function RegistryEnumInput(props) {
4820
+ const app = useOptionalApp();
4821
+ const control = resolveEnumControl(props.ui.control, app?.registry.enums.get(props.enumName), `field ${typeof props.path.at(-1) === "string" ? String(props.path.at(-1)) : props.enumName}`);
4822
+ return createElement(EnumPicker, {
4823
+ enumName: props.enumName,
4824
+ control,
4825
+ value: props.value == null ? void 0 : String(props.value),
4826
+ onChange: props.onChange ?? (() => void 0),
4827
+ name: props.accessibility.input,
4828
+ id: props.accessibility.input,
4829
+ testid: props.testIds.input,
4830
+ placeholder: typeof props.ui.placeholder === "string" ? props.ui.placeholder : void 0,
4831
+ disabled: props.disabled,
4832
+ required: props.field.required === true,
4833
+ ariaInvalid: props.issues.some(isErrorIssue),
4834
+ ariaDescribedBy: props.accessibility.description,
4835
+ ariaErrorMessage: props.accessibility.error,
4836
+ ariaLabelledBy: props.accessibility.label
4837
+ });
4394
4838
  }
4395
4839
  function enumAware(renderer, layout) {
4396
4840
  const FallbackInput = input(renderer.Input);
@@ -4400,19 +4844,9 @@ function enumAware(renderer, layout) {
4400
4844
  return Object.freeze({
4401
4845
  Input: (props) => {
4402
4846
  const name = enumName(props.ui);
4403
- if (name !== void 0) return createElement(EnumPicker, {
4404
- enumName: name,
4405
- value: props.value == null ? void 0 : String(props.value),
4406
- onChange: props.onChange ?? (() => void 0),
4407
- name: props.accessibility.input,
4408
- id: props.accessibility.input,
4409
- testid: props.testIds.input,
4410
- placeholder: typeof props.ui.placeholder === "string" ? props.ui.placeholder : void 0,
4411
- disabled: props.disabled,
4412
- required: props.field.required === true,
4413
- ariaInvalid: props.issues.some(isErrorIssue),
4414
- ariaDescribedBy: props.accessibility.description,
4415
- ariaErrorMessage: props.accessibility.error
4847
+ if (name !== void 0) return createElement(RegistryEnumInput, {
4848
+ ...props,
4849
+ enumName: name
4416
4850
  });
4417
4851
  if (props.options.length > 0) return createElement(OptionedInput, props);
4418
4852
  return createElement(FallbackInput, props);
@@ -4447,6 +4881,10 @@ const date = {
4447
4881
  Display: DateDisplay,
4448
4882
  Input: DateInput
4449
4883
  };
4884
+ const dateTime = {
4885
+ Display: DateTimeDisplay,
4886
+ Input: DateTimeInput
4887
+ };
4450
4888
  const en = {
4451
4889
  Display: EnumDisplay,
4452
4890
  Input: EnumInput
@@ -4482,9 +4920,9 @@ const shadcnFieldImplementations = Object.freeze({
4482
4920
  number: scalar(num),
4483
4921
  boolean: scalar(bool),
4484
4922
  date: scalar(date),
4485
- dateTime: scalar(date),
4486
- "date-time": scalar(date),
4487
- "default:date-time": scalar(date),
4923
+ dateTime: scalar(dateTime),
4924
+ "date-time": scalar(dateTime),
4925
+ "default:date-time": scalar(dateTime),
4488
4926
  enum: enumAware(en),
4489
4927
  json: scalar({
4490
4928
  Display: JsonDisplay,
@@ -4526,7 +4964,8 @@ function DisplayBody(props) {
4526
4964
  }
4527
4965
  //#endregion
4528
4966
  //#region src/enum/enum-display.tsx
4529
- function EnumDisplay$1({ enumName, value, variant = "badge", testid, className }) {
4967
+ function EnumDisplay$1({ enumName: enumInput, value, variant = "badge", testid, className }) {
4968
+ const enumName = enumBindingKey(enumInput) ?? "";
4530
4969
  const app = useApp();
4531
4970
  const caps = useI18nCaps();
4532
4971
  const def = app.registry.enums.get(enumName);
@@ -4556,7 +4995,7 @@ function EnumDisplay$1({ enumName, value, variant = "badge", testid, className }
4556
4995
  children: value
4557
4996
  });
4558
4997
  const entry = values?.find((v) => v.value === value);
4559
- const label = entry === void 0 && def?.fallback ? resolveContentSlot(caps, [enumFallbackKey(ns)], { default: def.fallback.label }) ?? value : resolveEnumLabel(caps, ns, value, entry?.label);
4998
+ const label = entry === void 0 && (def?.fallback !== void 0 || enumFallbackText(def) !== void 0) ? resolveContentSlot(caps, [enumFallbackKey(ns)], { default: enumFallbackText(def) }) ?? value : resolveEnumLabel(caps, ns, value, enumValueText(def, value) ?? entry?.label);
4560
4999
  const icon = entry?.icon;
4561
5000
  if (variant === "icon-label") return jsxs("span", {
4562
5001
  "data-testid": id,
@@ -4574,159 +5013,6 @@ function EnumDisplay$1({ enumName, value, variant = "badge", testid, className }
4574
5013
  });
4575
5014
  }
4576
5015
  //#endregion
4577
- //#region src/data-table/data-table-faceted-filter.tsx
4578
- function DataTableFacetedFilter({ column, title, options = [], loadOptions, selectedLabelFor, searchPlaceholder, emptyText, getTestId, className }) {
4579
- const t = useOptionalT();
4580
- const filterValue = column.getFilterValue();
4581
- const selected = useMemo(() => {
4582
- if (Array.isArray(filterValue)) return new Set(filterValue.map(String));
4583
- return new Set();
4584
- }, [filterValue]);
4585
- const [search, setSearch] = useState("");
4586
- const [asyncOptions, setAsyncOptions] = useState([]);
4587
- const [loading, setLoading] = useState(false);
4588
- const debounceRef = useRef(null);
4589
- const loadOptionsRef = useRef(loadOptions);
4590
- useEffect(() => {
4591
- loadOptionsRef.current = loadOptions;
4592
- });
4593
- useEffect(() => {
4594
- if (!loadOptionsRef.current) return;
4595
- setLoading(true);
4596
- if (debounceRef.current !== null) clearTimeout(debounceRef.current);
4597
- let cancelled = false;
4598
- debounceRef.current = setTimeout(() => {
4599
- const loader = loadOptionsRef.current;
4600
- if (!loader) return;
4601
- loader(search).then((results) => {
4602
- if (!cancelled) {
4603
- setAsyncOptions(results);
4604
- setLoading(false);
4605
- }
4606
- }).catch(() => {
4607
- if (!cancelled) setLoading(false);
4608
- });
4609
- }, 250);
4610
- return () => {
4611
- cancelled = true;
4612
- if (debounceRef.current !== null) clearTimeout(debounceRef.current);
4613
- };
4614
- }, [search]);
4615
- const effectiveOptions = loadOptions ? asyncOptions : options;
4616
- const labelFor = (value) => {
4617
- const opt = effectiveOptions.find((o) => o.value === value);
4618
- if (opt) return opt.label;
4619
- return selectedLabelFor?.(value) ?? value;
4620
- };
4621
- const toggle = (value) => {
4622
- const next = new Set(selected);
4623
- if (next.has(value)) next.delete(value);
4624
- else next.add(value);
4625
- column.setFilterValue(next.size === 0 ? void 0 : Array.from(next));
4626
- };
4627
- const selectedValues = Array.from(selected);
4628
- return jsxs(Popover, { children: [jsx(PopoverTrigger, {
4629
- asChild: true,
4630
- children: jsxs(Button, {
4631
- type: "button",
4632
- variant: "outline",
4633
- size: "sm",
4634
- "data-testid": getTestId?.("trigger"),
4635
- className: cn("h-8 border-dashed", className),
4636
- children: [
4637
- jsx(IconCirclePlus, {
4638
- className: "mr-2 h-4 w-4",
4639
- "aria-hidden": "true"
4640
- }),
4641
- title,
4642
- selected.size > 0 && jsxs(Fragment$1, { children: [
4643
- jsx("span", {
4644
- className: "mx-2 h-4 w-px bg-border",
4645
- "aria-hidden": "true"
4646
- }),
4647
- jsx(Badge, {
4648
- variant: "secondary",
4649
- className: "rounded-sm px-1 font-normal lg:hidden",
4650
- children: selected.size
4651
- }),
4652
- jsx("div", {
4653
- className: "hidden gap-1 lg:flex",
4654
- children: selected.size > 2 ? jsx(Badge, {
4655
- variant: "secondary",
4656
- className: "rounded-sm px-1 font-normal",
4657
- children: t("table.selectedCount", {
4658
- count: selected.size,
4659
- defaultValue: "{{count}} selected"
4660
- })
4661
- }) : selectedValues.map((value) => jsx(Badge, {
4662
- variant: "secondary",
4663
- className: "rounded-sm px-1 font-normal",
4664
- children: labelFor(value)
4665
- }, value))
4666
- })
4667
- ] })
4668
- ]
4669
- })
4670
- }), jsx(PopoverContent, {
4671
- className: "w-[220px] p-0",
4672
- align: "start",
4673
- children: jsxs(Command, {
4674
- shouldFilter: !loadOptions,
4675
- children: [
4676
- jsx(CommandInput, {
4677
- placeholder: searchPlaceholder ?? title,
4678
- "data-testid": getTestId?.("search"),
4679
- ...loadOptions ? {
4680
- value: search,
4681
- onValueChange: setSearch
4682
- } : {}
4683
- }),
4684
- jsxs(CommandList, { children: [
4685
- loading && jsx("div", {
4686
- className: "py-6 text-center text-sm text-muted-foreground",
4687
- children: t("list.loadingShort", { defaultValue: "Loading…" })
4688
- }),
4689
- jsx(CommandEmpty, { children: emptyText ?? t("relation.noResults", { defaultValue: "No results." }) }),
4690
- jsx(CommandGroup, { children: effectiveOptions.map((opt) => {
4691
- const isSelected = selected.has(opt.value);
4692
- const Icon = opt.icon;
4693
- return jsxs(CommandItem, {
4694
- value: opt.label,
4695
- "data-testid": getTestId?.("option", opt.value),
4696
- role: "option",
4697
- "aria-selected": isSelected,
4698
- "aria-checked": isSelected,
4699
- onSelect: () => toggle(opt.value),
4700
- children: [
4701
- jsx("span", {
4702
- className: cn("mr-2 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border", isSelected ? "border-primary bg-primary text-primary-foreground" : "border-input opacity-50"),
4703
- "aria-hidden": "true",
4704
- children: isSelected && jsx(IconCheck, { className: "h-3.5 w-3.5" })
4705
- }),
4706
- Icon && jsx(Icon, {
4707
- className: "mr-2 h-4 w-4 text-muted-foreground",
4708
- "aria-hidden": "true"
4709
- }),
4710
- jsx("span", {
4711
- className: "flex-1 text-left",
4712
- children: opt.label
4713
- })
4714
- ]
4715
- }, opt.value);
4716
- }) })
4717
- ] }),
4718
- selected.size > 0 && jsx("button", {
4719
- type: "button",
4720
- "data-testid": getTestId?.("clear"),
4721
- onClick: () => column.setFilterValue(void 0),
4722
- className: "flex w-full items-center justify-center border-t px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
4723
- children: t("filters.clearAll", { defaultValue: "Clear filters" })
4724
- })
4725
- ]
4726
- })
4727
- })] });
4728
- }
4729
- //#endregion
4730
5016
  //#region src/data-table/data-table-text-filter.tsx
4731
5017
  function DataTableTextFilter({ column, placeholder, testid, className }) {
4732
5018
  const external = column.getFilterValue() ?? "";
@@ -4876,14 +5162,18 @@ const OPERATOR_LABELS = {
4876
5162
  null: "is null",
4877
5163
  nnull: "is not null",
4878
5164
  startswith: "starts with",
4879
- endswith: "ends with"
5165
+ endswith: "ends with",
5166
+ has: "has",
5167
+ hasAny: "has any of",
5168
+ hasAll: "has all of",
5169
+ hasNone: "has none of"
4880
5170
  };
4881
- function formatOperatorLabel(op, t) {
4882
- return t ? t(`filters.operators.${op}`, { defaultValue: OPERATOR_LABELS[op] }) : OPERATOR_LABELS[op];
5171
+ function formatOperatorLabel(op, chromeT) {
5172
+ return chromeT ? chromeT(`filters.operators.${op}`, { defaultValue: OPERATOR_LABELS[op] }) : OPERATOR_LABELS[op];
4883
5173
  }
4884
5174
  function useOperatorLabel() {
4885
- const t = useOptionalT();
4886
- return (op) => formatOperatorLabel(op, t);
5175
+ const chromeT = useChromeT();
5176
+ return (op) => formatOperatorLabel(op, chromeT);
4887
5177
  }
4888
5178
  function isEmpty(v) {
4889
5179
  return v === void 0 || v === null || v === "";
@@ -4977,7 +5267,7 @@ function DataTableOperatorFilter({ column, operators, defaultOperator, valueKind
4977
5267
  //#region src/data-table/data-table-row-actions.tsx
4978
5268
  function DataTableRowActions({ row, actions, getTestId, className }) {
4979
5269
  const [open, setOpen] = useState(false);
4980
- const t = useOptionalT();
5270
+ const chromeT = useChromeT();
4981
5271
  if (actions.length === 0) return null;
4982
5272
  return jsxs(Popover, {
4983
5273
  open,
@@ -4995,7 +5285,7 @@ function DataTableRowActions({ row, actions, getTestId, className }) {
4995
5285
  "aria-hidden": "true"
4996
5286
  }), jsx("span", {
4997
5287
  className: "sr-only",
4998
- children: t("actions.openMenu", { defaultValue: "Open row actions menu" })
5288
+ children: chromeT("actions.openMenu", { defaultValue: "Open row actions menu" })
4999
5289
  })]
5000
5290
  })
5001
5291
  }), jsx(PopoverContent, {
@@ -5116,7 +5406,8 @@ function BlockScreen({ block, props, resource, record, label }) {
5116
5406
  children: jsx(PlannedBody, {
5117
5407
  screen: compiled,
5118
5408
  name: "modal",
5119
- label: label ?? ""
5409
+ label: label ?? "",
5410
+ ...needsRow && row !== null ? { record: row } : {}
5120
5411
  })
5121
5412
  })
5122
5413
  });
@@ -5125,13 +5416,20 @@ function relation(field) {
5125
5416
  if (field === null || typeof field !== "object") return false;
5126
5417
  return isRelationField(field);
5127
5418
  }
5419
+ const resolveOf = (bag) => bag !== null && typeof bag === "object" ? bag.resolve : void 0;
5420
+ function lazyToOne(field) {
5421
+ const entry = field;
5422
+ if (entry.kind === "relationMany" || entry.type === "array") return false;
5423
+ return (resolveOf(entry.meta) ?? resolveOf(entry.opts)) === "lazy";
5424
+ }
5128
5425
  function peekFields(def, columns, capabilityFor, fieldVisible = () => true) {
5129
5426
  const record = new Set(dtoFields(def, "record"));
5130
5427
  const out = [];
5131
5428
  for (const name of columns) {
5132
5429
  if (out.length === 5) break;
5133
5430
  if (name === def.displayField || out.includes(name) || !record.has(name)) continue;
5134
- if (!peekLeafAllowed(def, name, capabilityFor, fieldVisible) || relation(peekFieldDefinition(def, name))) continue;
5431
+ const field = peekFieldDefinition(def, name);
5432
+ if (!peekLeafAllowed(def, name, capabilityFor, fieldVisible) || relation(field) && !lazyToOne(field)) continue;
5135
5433
  out.push(name);
5136
5434
  }
5137
5435
  return Object.freeze(out);
@@ -5198,6 +5496,7 @@ function PeekByKey({ target, by }) {
5198
5496
  page: 1,
5199
5497
  pageSize: 2
5200
5498
  },
5499
+ scope: "none",
5201
5500
  enabled: by.key !== "",
5202
5501
  retry: false
5203
5502
  });
@@ -5213,7 +5512,7 @@ function PeekByKey({ target, by }) {
5213
5512
  } else if (query.error != null) read = failure(query.error, () => void query.refetch());
5214
5513
  else read = { state: "pending" };
5215
5514
  useEffect(() => {
5216
- if (read.state === "ambiguous" && process.env.NODE_ENV !== "production") console.error(`[burojs] ${relationAmbiguousError(target, by.field, by.key).message}`);
5515
+ if (read.state === "ambiguous" && process.env.NODE_ENV !== "production") console.error(`[burojs] ${technicalText(relationAmbiguousError(target, by.field, by.key))}`);
5217
5516
  }, [
5218
5517
  read.state,
5219
5518
  target,
@@ -5229,6 +5528,7 @@ function PeekByKey({ target, by }) {
5229
5528
  function PeekBody({ target, peekKey, read }) {
5230
5529
  const tid = useTestIds();
5231
5530
  const t = useOptionalT();
5531
+ const chromeT = useChromeT();
5232
5532
  const testid = tid.peek(target, peekKey);
5233
5533
  if (read.state === "pending") return jsx("div", {
5234
5534
  "data-testid": testid,
@@ -5256,7 +5556,7 @@ function PeekBody({ target, peekKey, read }) {
5256
5556
  variant: "outline",
5257
5557
  "data-testid": `${testid}:retry`,
5258
5558
  onClick: read.retry,
5259
- children: t("actions.retry", { defaultValue: "Retry" })
5559
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
5260
5560
  })]
5261
5561
  });
5262
5562
  if (read.state === "unavailable") return jsx("p", {
@@ -5484,7 +5784,7 @@ function DefaultPeekBody({ target, testid, row, open }) {
5484
5784
  const columns = useMemo(() => {
5485
5785
  if (def === void 0) return [];
5486
5786
  const listed = def.views?.list?.columns;
5487
- return peekFields(def, listed ?? Object.keys(def.fields), capabilityFor, (name) => access[name]?.view === "visible");
5787
+ return peekFields(def, listed?.map(columnName) ?? Object.keys(def.fields), capabilityFor, (name) => access[name]?.view === "visible");
5488
5788
  }, [
5489
5789
  def,
5490
5790
  capabilityFor,
@@ -5521,16 +5821,24 @@ function DefaultPeekBody({ target, testid, row, open }) {
5521
5821
  className: "flex min-w-0 items-baseline gap-2 text-sm",
5522
5822
  children: [jsx("dt", {
5523
5823
  className: "w-24 shrink-0 truncate text-xs text-muted-foreground",
5524
- children: resolveFieldLabel(caps, name, field?.label)
5824
+ children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
5525
5825
  }), jsx("dd", {
5526
5826
  className: "min-w-0 break-words",
5527
- children: jsx(ResolvedFieldDisplay, {
5528
- resource: ref,
5529
- name,
5530
- field,
5827
+ children: jsx(FieldValueFrame, {
5828
+ field: {
5829
+ ...field,
5830
+ name
5831
+ },
5531
5832
  value,
5532
- record,
5533
- fallback: toDisplayText(value).text
5833
+ surface: "display",
5834
+ children: jsx(ResolvedFieldDisplay, {
5835
+ resource: ref,
5836
+ name,
5837
+ field,
5838
+ value,
5839
+ record,
5840
+ fallback: toDisplayText(value).text
5841
+ })
5534
5842
  })
5535
5843
  })]
5536
5844
  }, name);
@@ -5542,21 +5850,36 @@ function DefaultPeekBody({ target, testid, row, open }) {
5542
5850
  //#region src/quick/quick-body.tsx
5543
5851
  const EMPTY_ROUTE = Object.freeze({});
5544
5852
  function QuickBody({ entry }) {
5853
+ return jsx(RecordScopeChain, {
5854
+ frames: entry.scopeFrames ?? NO_RECORD_SCOPE_FRAMES,
5855
+ children: jsx(QuickBodyOf, { entry })
5856
+ });
5857
+ }
5858
+ function QuickBodyOf({ entry }) {
5545
5859
  if (entry.mode === "create") return jsx(QuickScreenOf, {
5546
5860
  entry,
5547
5861
  route: EMPTY_ROUTE
5548
5862
  });
5549
5863
  if (entry.recordId === void 0 || entry.recordId === "") return jsx(RecordState, { state: "not-found" });
5550
- return jsx(QuickRead, {
5864
+ return jsx(QuickReadRoute, {
5551
5865
  entry,
5552
5866
  recordId: entry.recordId
5553
5867
  });
5554
5868
  }
5555
- function QuickRead({ entry, recordId }) {
5869
+ function QuickReadRoute({ entry, recordId }) {
5870
+ const typed = useRouteRecordId(entry.resource, recordId);
5871
+ if (typed === void 0) return jsx(InvalidRouteId, { resource: entry.resource });
5872
+ return jsx(QuickRead, {
5873
+ entry,
5874
+ recordId,
5875
+ typedId: typed
5876
+ });
5877
+ }
5878
+ function QuickRead({ entry, recordId, typedId }) {
5556
5879
  const def = useApp().registry.get(entry.resource);
5557
5880
  const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
5558
5881
  const query = useOne(entry.resource, {
5559
- id: recordId,
5882
+ id: typedId,
5560
5883
  retry: retryRecordRead,
5561
5884
  ...projection === void 0 ? {} : { projection }
5562
5885
  });
@@ -5567,20 +5890,20 @@ function QuickRead({ entry, recordId }) {
5567
5890
  return jsx(QuickLoaded, {
5568
5891
  entry,
5569
5892
  recordId,
5893
+ typedId,
5570
5894
  row,
5571
5895
  isCurrentData: !query.isPlaceholderData
5572
5896
  });
5573
5897
  }
5574
- if (query.error != null) {
5575
- const state = recordErrorState(query.error);
5576
- return jsx(RecordState, {
5577
- state,
5578
- ...state === "error" ? { onRetry: () => void query.refetch() } : {}
5579
- });
5580
- }
5898
+ if (query.error != null) return jsx(LoadError, {
5899
+ surface: "record",
5900
+ resource: entry.resource,
5901
+ error: query.error,
5902
+ retry: () => void query.refetch()
5903
+ });
5581
5904
  return jsx(RecordState, { state: "loading" });
5582
5905
  }
5583
- function QuickLoaded({ entry, recordId, row, isCurrentData }) {
5906
+ function QuickLoaded({ entry, recordId, typedId, row, isCurrentData }) {
5584
5907
  const gate = useEntryGate();
5585
5908
  const quick = useQuickEntry();
5586
5909
  const answer = gate(entry.resource, entry.mode === "edit" ? "edit" : "show", row);
@@ -5600,16 +5923,16 @@ function QuickLoaded({ entry, recordId, row, isCurrentData }) {
5600
5923
  if (!open) return jsx(RecordState, { state: "not-found" });
5601
5924
  return jsx(QuickScreenOf, {
5602
5925
  entry,
5603
- route
5926
+ route,
5927
+ recordId: typedId
5604
5928
  });
5605
5929
  }
5606
5930
  function canonicalOrNull(name) {
5607
- if (typeof name !== "string") return null;
5608
5931
  try {
5609
- return canonicalResourceName(name);
5610
- } catch {
5611
- return null;
5612
- }
5932
+ if (typeof name === "string") return canonicalResourceName(name);
5933
+ if (name !== null && typeof name === "object") return routeResourceKey(name);
5934
+ } catch {}
5935
+ return null;
5613
5936
  }
5614
5937
  function absorbedRedirect(to, entry) {
5615
5938
  if (typeof to !== "object" || to === null) return null;
@@ -5623,7 +5946,7 @@ function closeEntry(quick, dismiss) {
5623
5946
  if (quick.stack.entries.length > 1) quick.stack.pop();
5624
5947
  else dismiss(quick.presentationId);
5625
5948
  }
5626
- function QuickScreenOf({ entry, route }) {
5949
+ function QuickScreenOf({ entry, route, recordId }) {
5627
5950
  const quick = useQuickEntry();
5628
5951
  const instance = useUIInstance();
5629
5952
  const runtime = useUIPresentationRuntime();
@@ -5675,7 +5998,7 @@ function QuickScreenOf({ entry, route }) {
5675
5998
  children: jsx(RecordScreen, {
5676
5999
  resource: entry.resource,
5677
6000
  mode: entry.mode,
5678
- ...entry.recordId === void 0 ? {} : { recordId: entry.recordId },
6001
+ ...recordId === void 0 ? {} : { recordId },
5679
6002
  ...entry.defaults === void 0 ? {} : { defaults: entry.defaults },
5680
6003
  ...entry.input === void 0 ? {} : { input: entry.input },
5681
6004
  ...callbacks
@@ -5697,7 +6020,7 @@ function useEntryTitle(entry) {
5697
6020
  const caps = useI18nCaps(entry.resource);
5698
6021
  const t = useOptionalT();
5699
6022
  if (entry.mode !== "create") return entry.title ?? "";
5700
- return t("pageHeader.createTitle", { params: { label: resolveResourceLabel(caps, entry.resource, "single", app.registry.get(entry.resource)) } });
6023
+ return resolveResourceTitle(caps, t, entry.resource, "createTitle", { label: resolveResourceLabel(caps, entry.resource, "single", app.registry.get(entry.resource)) });
5701
6024
  }
5702
6025
  const HEADING_CLASS = "min-w-0 flex-1 break-words text-lg font-semibold tracking-tight focus-visible:outline-hidden";
5703
6026
  function QuickHeader({ entries }) {
@@ -5954,11 +6277,25 @@ SheetPresentationHost.optionsSchema = Object.freeze({
5954
6277
  //#region src/ui-presentations/index.tsx
5955
6278
  function FlowScreen(props) {
5956
6279
  const loaded = useLoaded(loadFlowScreen);
5957
- if (loaded.error !== void 0) return jsx(FlowLoadFailed, { onRetry: loaded.retry });
5958
- if (loaded.module === void 0) return null;
6280
+ if (loaded.error !== void 0) return jsx(FlowLoadFailed, {
6281
+ label: props.label,
6282
+ onRetry: loaded.retry
6283
+ });
6284
+ if (loaded.module === void 0) return jsxs("div", {
6285
+ "data-buro-flow-step": "loading",
6286
+ "aria-busy": "true",
6287
+ className: "flex flex-col gap-4",
6288
+ children: [jsx(OwnTitleHeading, { label: props.label }), jsx(IconLoader2, {
6289
+ className: "h-5 w-5 animate-spin self-center",
6290
+ "aria-hidden": "true"
6291
+ })]
6292
+ });
5959
6293
  const Screen = loaded.module.FlowScreen;
5960
6294
  return jsx(FlowBoundary, {
5961
- fallback: (_error, reset) => jsx(FlowLoadFailed, { onRetry: reset }),
6295
+ fallback: (_error, reset) => jsx(FlowLoadFailed, {
6296
+ label: props.label,
6297
+ onRetry: reset
6298
+ }),
5962
6299
  children: jsx(Screen, { ...props })
5963
6300
  });
5964
6301
  }
@@ -6233,9 +6570,13 @@ function DefaultCommandPalette({ open, onOpenChange, actions, children }) {
6233
6570
  }
6234
6571
  //#endregion
6235
6572
  //#region src/shell/app-shell.tsx
6236
- function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, children }) {
6573
+ function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, navChrome, children }) {
6237
6574
  const enabled = palette !== false;
6238
- const { open, setOpen } = useCommandPalette({ disableShortcut: !enabled });
6575
+ const { open, setOpen, toggle } = useCommandPalette();
6576
+ useGlobalShortcut({
6577
+ key: "k",
6578
+ mod: true
6579
+ }, toggle, { enabled });
6239
6580
  const paletteState = useMemo(() => ({
6240
6581
  open,
6241
6582
  setOpen
@@ -6253,7 +6594,7 @@ function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel,
6253
6594
  sidebar: fill(sidebar, () => jsx(AppSidebar, {})),
6254
6595
  header: fill(header, () => jsx(AppHeader, {})),
6255
6596
  ...docPanel !== void 0 ? { docPanel } : {},
6256
- children
6597
+ children: navChrome === false ? children : jsx(NavChrome, { children })
6257
6598
  }), paletteNode]
6258
6599
  });
6259
6600
  }
@@ -6361,6 +6702,6 @@ function ScopeSwitcher({ options, value, onChange, label, extra, placeholder })
6361
6702
  const PACKAGE_NAME = "@burojs/ui-shadcn";
6362
6703
  const PACKAGE_VERSION = "0.0.0";
6363
6704
  //#endregion
6364
- export { ActionButton, ActionOverflow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AppActions, AppFlowButton, AppHeader, AppLayout, AppShell, AppSidebar, AuthPage, Avatar, AvatarFallback, AvatarImage, Badge, BooleanDisplay, BooleanInput, BrandMark, Breadcrumbs, BulkActionToolbar, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CheckConfirmDialog, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnsToggle, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandPalette, CommandPaletteActionsGroup, CommandPaletteCreateGroup, CommandPaletteDocsGroup, CommandPaletteNavigationGroups, CommandPaletteRecentGroup, CommandPaletteRecordsGroup, CommandPaletteTrigger, CommandSeparator, ContextPicker, CopyId, CreateButton, CursorLoadMore, DEFAULT_LANGUAGE, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, DataTableEnumFilter, DataTableFacetedFilter, DataTableNumRangeFilter, DataTableOperatorFilter, DataTableRowActions, DataTableTextFilter, DataTableToolbar, DataTableViewOptions, DateDisplay, DateInput, DeclarativeDisplay, DeclarativeForm, DefaultCommandPalette, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTrigger, DisabledAction, DocBrowser, DocInfo, DocPanel, DocTrailHelp, DocumentTitleProvider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, EnumDisplay, EnumInput, EnumPicker, EnumDisplay$1 as EnumValueDisplay, ExportButton, ExportDialog, Field, FieldDisplay, FileDisplay, FileField, FileInput, FilterBadges, FilterBuilder, FilterPanel, FilterRegion, FilterValueControl, FlowActionButton, Form, FormActions, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HeaderBreadcrumbs, HelpButton, Icon, Input, Label, LanguageSubmenu, LanguageSwitch, LinkComponentProvider, ListBody, ListBulkBar, ListColumns, ListControls, ListCreate, ListErrorState, ListExport, ListFilters, ListFlows, ListLive, ListSavedViews, ListSearchInput, LoginForm, NavAreaLayout, NavAreaPage, NavChrome, NavChromeBoundaryProvider, NavMain, NavigationGuardDialog, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, PageHeader, PinnedFilterControls, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPresentationHost, PopoverTrigger, Progress, RecordActions, RecordBreadcrumbs, RecordPage, RecordScreen, RecordTitle, RefreshButton, RelationDisplay, RelationField, RelationManyDisplay, RelationPicker, LazyRelationPickerDialog as RelationPickerDialog, RelationPickerInput, ResourceList, ResourceListPage, ResourcePageRouter, SavedViewsDropdown, ScopeSwitcher, SecondarySidebarLayout, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTrigger, ShellProvider, ShowPageHeader, Sidebar, SidebarBrand, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, THEME_CHOICES, THEME_STORAGE_KEY, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsLayout, TabsList, TabsTrigger, TaskRunPanel, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, ThemeSubmenu, ThemeToggle, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserMenu, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, defaultNavLayouts, defaultUserMapper, downloadArtifact, formatOperatorLabel, hasIcon, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLanguage, loadLocale, localeLabel, modeForSurface, recordDisplayName, registerIcons, relationLinkClassName, resolveNavIcon, resolveUpdater, saveLanguage, saveLocale, shadcnBlockImplementations, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentUser, useDialogFilterState, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLanguagePaletteActions, useLanguageSwitch, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavChromeBoundary, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useShell, useSidebar, useTheme, useThemePaletteActions, wrapperEnabled };
6705
+ export { ActionButton, ActionOverflow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AppActions, AppFlowButton, AppHeader, AppLayout, AppShell, AppSidebar, AuthPage, Avatar, AvatarFallback, AvatarImage, Badge, BooleanDisplay, BooleanInput, BrandMark, Breadcrumbs, BulkActionToolbar, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CheckConfirmDialog, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnsToggle, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandPalette, CommandPaletteActionsGroup, CommandPaletteCreateGroup, CommandPaletteDocsGroup, CommandPaletteNavigationGroups, CommandPaletteRecentGroup, CommandPaletteRecordsGroup, CommandPaletteTrigger, CommandSeparator, ContextPicker, CopyId, CreateButton, CursorLoadMore, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, DataTableEnumFilter, DataTableFacetedFilter, DataTableNumRangeFilter, DataTableOperatorFilter, DataTableRowActions, DataTableTextFilter, DataTableToolbar, DataTableViewOptions, DateDisplay, DateInput, DeclarativeDisplay, DeclarativeForm, DefaultCommandPalette, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTrigger, DisabledAction, DocBrowser, DocInfo, DocPanel, DocTrailHelp, DocumentTitleProvider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, EnumDisplay, EnumInput, EnumPicker, EnumDisplay$1 as EnumValueDisplay, ExportButton, ExportDialog, FacetedFilterPicker, Field, FieldDisplay, FileDisplay, FileField, FileInput, FilterBadges, FilterBuilder, FilterPanel, FilterRegion, FilterValueControl, FlowActionButton, Form, FormActions, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HeaderBreadcrumbs, HelpButton, Icon, Input, Label, LanguageSubmenu, LanguageSwitch, LinkComponentProvider, LinkKeysDisplay, ListBody, ListBulkBar, ListColumns, ListControls, ListCreate, ListErrorState, ListExport, ListFilters, ListFlows, ListLive, ListSavedViews, ListSearchInput, LoginForm, NavAreaPage, NavChrome, NavMain, NavigationGuardDialog, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, PageHeader, PinnedFilterControls, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPresentationHost, PopoverTrigger, PresetFilterControls, Progress, RecordActions, RecordBreadcrumbs, RecordPage, RecordScreen, RecordTitle, RefreshButton, RelationDisplay, RelationField, RelationManyDisplay, RelationPicker, LazyRelationPickerDialog as RelationPickerDialog, RelationPickerInput, ResourceList, ResourceListPage, ResourcePageRouter, SavedViewsDropdown, ScopeDimensionPicker, ScopeSwitcher, SecondarySidebarLayout, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTrigger, ShellProvider, ShowPageHeader, Sidebar, SidebarBrand, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, THEME_CHOICES, THEME_STORAGE_KEY, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsLayout, TabsList, TabsTrigger, TaskRunPanel, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, ThemeSubmenu, ThemeToggle, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserMenu, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, defaultNavLayouts, defaultUserMapper, downloadArtifact, formatOperatorLabel, hasIcon, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLocale, localeLabel, modeForSurface, recordDisplayName, registerIcons, relationLinkClassName, resolveNavIcon, resolveUpdater, saveLocale, shadcnBlockImplementations, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentUser, useDialogFilterState, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLanguagePaletteActions, useLanguageSwitch, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useRelationPicker, useShell, useSidebar, useTheme, useThemePaletteActions, webPlatform, wrapperEnabled };
6365
6706
 
6366
6707
  //# sourceMappingURL=index.js.map