@burojs/ui-shadcn 0.6.0 → 0.8.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 (158) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-2HLgz7Il.cjs +169 -0
  3. package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
  4. package/dist/bulk-summary-D6A0ua21.js +169 -0
  5. package/dist/bulk-summary-D6A0ua21.js.map +1 -0
  6. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-C47mF260.cjs} +4 -4
  7. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
  8. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CGXItMN0.js} +3 -3
  9. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
  10. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-BT04Mieo.cjs} +475 -107
  11. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
  12. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-CtiRzOAt.js} +391 -108
  13. package/dist/check-confirm-dialog-CtiRzOAt.js.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-nXqtM_h4.cjs → filter-builder-ByGhxLf_.cjs} +1021 -154
  19. package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-z3Axzf9Y.js} +929 -140
  21. package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BVFjMU5-.cjs} +64 -29
  23. package/dist/flow-runtime-BVFjMU5-.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DrUWyTS0.js} +65 -30
  25. package/dist/flow-runtime-DrUWyTS0.js.map +1 -0
  26. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-3fk6J33y.js} +130 -87
  27. package/dist/flow-screen-3fk6J33y.js.map +1 -0
  28. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-6y5E_HT6.cjs} +130 -87
  29. package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
  30. package/dist/index.cjs +1186 -950
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +631 -322
  33. package/dist/index.d.ts +631 -322
  34. package/dist/index.js +1100 -870
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-C4KmZIYE.cjs} +5 -5
  37. package/dist/inline-flow-host-C4KmZIYE.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-YnnvWjdU.js} +5 -5
  39. package/dist/inline-flow-host-YnnvWjdU.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-DN4bvQfg.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-CtEfqAvU.js} +35 -23
  45. package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DEjAxHe3.cjs} +42 -30
  47. package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
  48. package/dist/{place-node-NraNYKPx.js → place-node-BdxT-30h.js} +1980 -1041
  49. package/dist/place-node-BdxT-30h.js.map +1 -0
  50. package/dist/{place-node-C5SmIAYU.cjs → place-node-Bhl8B3fF.cjs} +2038 -1087
  51. package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
  52. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-Bc_vfCE_.cjs} +9 -4
  53. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
  54. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BvkD5OcC.js} +9 -4
  55. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
  56. package/dist/related-list-timeline-DCrwN9oO.js +167 -0
  57. package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
  58. package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
  59. package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
  60. package/dist/{related-rows-ea43qhhy.cjs → related-rows-BW9Jr07T.cjs} +27 -16
  61. package/dist/related-rows-BW9Jr07T.cjs.map +1 -0
  62. package/dist/{related-rows-o0_t-Yce.js → related-rows-CPE-9FFr.js} +25 -14
  63. package/dist/related-rows-CPE-9FFr.js.map +1 -0
  64. package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
  65. package/dist/relation-create-C43cdPQt.js.map +1 -0
  66. package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
  67. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  68. package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BlrGRwMV.js} +210 -104
  69. package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-Br-zBHxT.cjs} +218 -112
  71. package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
  72. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-ADjxI_TI.cjs} +36 -73
  73. package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
  74. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-CyKliBkN.js} +134 -36
  75. package/dist/relation-picker-dialog-CyKliBkN.js.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-DimVQF9Y.cjs} +133 -35
  77. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +1 -0
  78. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-x7P5HKie.js} +30 -61
  79. package/dist/relation-picker-x7P5HKie.js.map +1 -0
  80. package/dist/relation-select-B8Fq4o-t.js +48 -0
  81. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  82. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  83. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  84. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-BRpUnhay.js} +634 -264
  85. package/dist/resource-page-router-BRpUnhay.js.map +1 -0
  86. package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-qEd2xb55.cjs} +694 -312
  87. package/dist/resource-page-router-qEd2xb55.cjs.map +1 -0
  88. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  89. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  90. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  91. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  92. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  93. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  94. package/dist/success-notices-LDEgEkc4.js +41 -0
  95. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  96. package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
  97. package/dist/toaster-BbByOytP.cjs.map +1 -0
  98. package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
  99. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  100. package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  101. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  102. package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  103. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  104. package/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
  105. package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
  106. package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
  107. package/dist/use-record-subtitle-gIaCGgbI.js.map +1 -0
  108. package/package.json +11 -11
  109. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  110. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  111. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  112. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  113. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  114. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  115. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  116. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  117. package/dist/dialog-B_rt9S1c.js.map +0 -1
  118. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  119. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  120. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  121. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  122. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  123. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  124. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  125. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  126. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  127. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  128. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  129. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  130. package/dist/place-node-NraNYKPx.js.map +0 -1
  131. package/dist/related-list-timeline-CCzl71aA.cjs +0 -142
  132. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  133. package/dist/related-list-timeline-yrPsjA3F.js +0 -142
  134. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  135. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  136. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  137. package/dist/relation-create-IA40t1hi.cjs +0 -364
  138. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  139. package/dist/relation-create-h6F9B4C4.js +0 -339
  140. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  141. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  142. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  143. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  144. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  145. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  146. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  147. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  148. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  149. package/dist/search-access-CPPmfuvy.js.map +0 -1
  150. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  151. package/dist/shell-BPlk8hEa.js +0 -205
  152. package/dist/shell-BPlk8hEa.js.map +0 -1
  153. package/dist/shell-DxhlsPwm.cjs +0 -271
  154. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  155. package/dist/toaster-C02XbB9r.js.map +0 -1
  156. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  157. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  158. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,23 +1,24 @@
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 LinkComponentProvider, c as PopoverAnchor, d as PopoverTrigger, i as toFriendlyError, l as PopoverClose, n as toast, o as useLinkComponent, r as friendlyStringsFrom, s as Popover, t as Toaster, u as PopoverContent } from "./toaster-C02XbB9r.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-bRT8lt0d.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-CtiRzOAt.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 CommandSeparator, S as CommandLoading, _ as CommandEmpty, b as CommandItem, c as peekAnchorOpener, f as recordDisplayName, g as CommandDialog, h as Command, i as RelationField, l as PeekContext, o as relationLinkClassName, r as RelationDisplay, s as focusWithoutPeek, u as PeekHostContext, v as CommandGroup, x as CommandList, y as CommandInput } from "./search-access-CPPmfuvy.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 tanStackSortingToBuro, A as ListExport, At as i18nPrefixToRef, B as DeclarativeForm, C as ListSavedViews, Ct as useExportRunWatcher, D as ListFilters, Dt as AppFlowButton, E as ListFlows, F as ColumnsToggle, Ft as defaultUserMapper, I as DataTableViewOptions, It as usePaletteState, J as ResourceList, L as ListBulkBar, Lt as useShell, M as ListCreate, Mt as lowerLegacyField, N as CreateButton, Nt as toDisplayText, O as FilterRegion, Ot as Field, P as ListColumns, Pt as ShellProvider, Q as resolveUpdater, R as BulkActionToolbar, S as ListControls, T as ListLive, Tt as EnumBadge, U as QuickScreenClaimContext, V as ScreenTree, X as ListErrorState, Y as CursorLoadMore, Z as buroSortingToTanStack, _ as FormActions, a as peekFieldDefinition, at as TabsList, c as peekRow, d as shadcnFrameViews, dt as PageHeader, et as useBuroDataTable, ft as ShowPageHeader, g as CopyId, gt as isShowAvailable, ht as typography, it as TabsContent, j as ExportButton, jt as ResolvedFieldDisplay, k as ListSearchInput, kt as FieldDisplay, mt as useDocumentTitle, nt as Checkbox, o as peekLeafAllowed, ot as TabsTrigger, pt as DocumentTitleProvider, q as ListBody, r as shadcnBlockImplementations, rt as Tabs, s as peekRecord, tt as DataTable, u as resolveFrameView, ut as EditPageHeader, v as RecordActions, w as SavedViewsDropdown, wt as downloadArtifact, xt as ActionOverflow, y as RecordTitle, yt as recordRouteAvailable, z as ExportDialog } from "./place-node-NraNYKPx.js";
9
+ import { $ as CursorLoadMore, A as ListSearchInput, At as AppFlowButton, B as ExportDialog, C as ListControls, D as ListFlows, Dt as EnumSettlingRow, E as ListLive, Et as EnumBadge, F as ListColumns, Ft as toDisplayText, H as ScreenTree, I as ColumnsToggle, It as ShellProvider, L as DataTableViewOptions, Lt as defaultUserMapper, M as ExportButton, Mt as FieldDisplay, N as ListCreate, Nt as i18nPrefixToRef, O as ListFilters, Ot as unknownEnumValueLabel, P as CreateButton, Pt as FieldValueFrame, Q as ResourceList, R as ListBulkBar, Rt as usePaletteState, St as ActionOverflow, T as SavedViewsDropdown, Tt as downloadArtifact, V as DeclarativeForm, W as QuickScreenClaimContext, Y as useFormControl, Z as ListBody, _ as CopyId, _t as ShowPageHeader, a as peekFieldDefinition, at as DataTable, b as RecordTitle, bt as useDocumentTitle, c as peekRow, ct as TabsContent, d as resolveFrameView, et as ListErrorState, f as shadcnFrameViews, gt as PageHeader, ht as EditPageHeader, it as useBuroDataTable, j as ListExport, jt as Field, k as FilterRegion, l as useRelationPicker, lt as TabsList, 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 resolveResourceTitle, w as ListSavedViews, wt as useExportRunWatcher, xt as typography, y as RecordActions, yt as DocumentTitleProvider, z as BulkActionToolbar, zt as useShell } from "./place-node-BdxT-30h.js";
11
10
  import { THEME_STORAGE_KEY } from "./layout/theme-storage-key.js";
12
- import { C as TableHeader, D as DEFAULT_LANGUAGE_STORAGE_KEY, E as badgeVariants, O as loadLocale, 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, k as saveLocale, 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-Bhn-dhZV.js";
13
- 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-3RUf9d1j.js";
11
+ import { A as TableHeader, B as DEFAULT_LANGUAGE_STORAGE_KEY, C as SelectTrigger, D as TableCaption, E as TableBody, F as ResolvedFieldDisplay, H as saveLocale, M as Badge, N as badgeVariants, O as TableCell, S as SelectSeparator, T as Table, V as loadLocale, _ as SelectGroup, a as PinnedFilterControls, b as SelectScrollDownButton, c as fromLocalDateTimeText, d as useFieldTimeZone, f as FilterBadges, g as SelectContent, h as Select, i as FacetedFilterPicker, j as TableRow, k as TableHead, l as toLocalDateTimeText, m as localeTag, n as PresetFilterControls, o as FilterPanel, p as formatNumber, r as DataTableFacetedFilter, s as FilterValueControl, t as FilterBuilder, u as dateDisplayParams, v as SelectItem, w as SelectValue, x as SelectScrollUpButton, y as SelectLabel, z as lowerLegacyField } from "./filter-builder-z3Axzf9Y.js";
12
+ import { A as useLayoutResolver, C as useMatch, D as BUILTIN_LAYOUTS, E as useResourceVisible, F as canonicalResourceKey$1, M as TabsLayout, N as SecondarySidebarLayout, O as layoutSlot, P as canonicalNavKey, S as useHref, T as usePageAllowed, _ as RecordScreen, a as useRecordVisit, b as ResourceListPage, c as headerTrail, d as recordTrailRoute, f as useTrailContext, g as usePublishedRecordTrail, i as RecordPage, j as useReportUnresolvedLayouts, k as useLayout, l as originTrail, m as recordTrailKey, n as NavAreaPage, o as RECENT_LABEL_KEYS, p as isShowAvailable, r as ResourcePageRouter, s as readRecentRecords, u as recordTrail, v as useResourceDimensionOptions, w as useNavVisible, x as NavChromeMountedProvider, y as PlannedBody } from "./resource-page-router-BRpUnhay.js";
14
13
  import { t as Label } from "./label-66W1qbke.js";
15
- import { i as useRelationSelect, n as RelationPicker, r as RelationPickerInput } from "./relation-picker-Z0ZJJ5uS.js";
16
- import { r as LazyRelationPickerDialog, t as useCandidateSelection } from "./candidates-gate-Bb70LPAy.js";
17
- import { t as useDialogFilterState } from "./use-dialog-filter-state-C--oMAZG.js";
18
- import { builtinFrames, compilePageScreen, compiledScreenFor, declaredField, declaredText, dtoFields, effectiveFlow, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, evaluateVisible, fieldOptionText, formatEntityRef, groupChain, hasListPage, hasRecordPage, hrefFor, humanizeKey, isCustomPageDef, isRelationField, isScreenFailure, linkOf, navHrefFor, nodeKind, normalizeDataError, normalizeEntityRef, projectDto, recordEntries, relationAmbiguousError, resolveConceptLabel, resolveContentSlot, resolveDoc, resolveEnumLabel, resolveFieldLabel, resolveFieldOptionLabel, resolveNavLabel, resolveResourceLabel, resolveResourceNavLabel, resolveStaticAvailability, resourceCreateHref, resourceKey, resourceShowHref, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, taskRunFraction, taskRunOutcome } from "@burojs/core";
19
- import { ComponentsContext, DispatcherProvider, I18nContext, NO_RECORD_SCOPE_FRAMES, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, RecordScopeChain, ResourceContext, RouteSourceContext, ScreenStateProvider, formatBytes, retryRecordRead, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useCapabilityResolver, useChangeLocale, useCommandPalette, useCurrentUserId, useDataRuntime, 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, useOptionalApp, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, usePresentBlock, useProjectedRecord, useProjectedTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordScopeFrames, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRelatedRecord, useRouteNavigate, useRouterAdapter, useT, useTestIds, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
20
- 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-DN4bvQfg.js";
15
+ import { n as useRecordSubtitle, r as useRowSubtitle } from "./use-record-subtitle-gIaCGgbI.js";
16
+ import { n as RelationPicker, r as RelationPickerInput } from "./relation-picker-x7P5HKie.js";
17
+ import { t as useDialogFilterState } from "./use-dialog-filter-state-B_MqzHaT.js";
18
+ import { useGlobalShortcut, webPlatform } from "@burojs/react/web";
19
+ import { ENUM_CONTROLS, builtinFrames, canonicalInstant, columnName, compilePageScreen, compiledScreenFor, declaredField, declaredText, dtoFields, effectiveFlow, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, epochMsToInstant, evaluateVisible, fieldOptionText, formatEntityRef, formatTimeValue, hasListPage, hasRecordPage, hrefFor, humanizeKey, isCalendarDate, 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, zoneShortName } from "@burojs/core";
20
+ import { ComponentsContext, DispatcherProvider, I18nContext, NO_RECORD_SCOPE_FRAMES, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, RecordScopeChain, RouteSourceContext, ScreenStateProvider, formatBytes, optionFieldName, recordRouteAvailable, resolveErrorText, resolveOptionLabel, retryRecordRead, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useAvailableEnumValues, useBoundEnum, useCapabilityResolver, useChangeLocale, useChromeT, useCommandPalette, useCurrentUserId, useDebouncedValue, useDispatcherEffects, useDocPanel, useDocScopes, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useFieldAccess, useFieldMetaSources, useFileField, useFileUrls, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useIdentity, useLanguageStorageKey, useLinkKeys, useList, useListAccess, useLocale, useLogout, useNavigationGuardText, useNavigationOrigin, useOne, useOptionCaps, useOptionalApp, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRouterAdapter, useOptionalT, useOriginIntent, usePlatformStorage, usePresentBlock, useProjectedRecord, useProjectedTitle, usePublishOriginTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRelatedRecord, useRouteNavigate, useRouteRecordId, useRouterAdapter, useScope, useT, useTestIds, useTimeZone, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
21
+ import { IconAlertCircle, IconArrowDown, IconArrowLeft, IconArrowUp, IconArrowUpRight, IconBook2, IconCheck, IconChevronDown, IconChevronRight, IconDeviceDesktop, IconDots, IconExternalLink, IconHelp, IconHelpHexagon, IconLayoutSidebar, IconLoader2, IconLogin, IconLogout, IconMaximize, IconMinimize, IconMoon, IconPlus, IconRefresh, IconRotate, IconSearch, IconSelector, IconSun, IconTrash, IconUpload, IconUser, IconX } from "@tabler/icons-react";
21
22
  import * as React from "react";
22
23
  import { Children, Fragment, Suspense, cloneElement, createContext, createElement, isValidElement, lazy, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
23
24
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -29,6 +30,7 @@ import * as SeparatorPrimitive from "@radix-ui/react-separator";
29
30
  import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
30
31
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
31
32
  import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
33
+ import { enumBindingKey, enumParamsOf, narrowEntriesOf } from "@burojs/core/internal";
32
34
  import { Controller, FormProvider, useForm, useFormContext } from "react-hook-form";
33
35
  import { zodResolver } from "@hookform/resolvers/zod";
34
36
  import { z } from "zod";
@@ -489,7 +491,7 @@ function ScopeSummary({ scope, summary }) {
489
491
  const caps = useI18nCaps();
490
492
  const t = useOptionalT();
491
493
  const links = (scope !== void 0 ? resolveDoc(caps, scope) : void 0)?.links ?? [];
492
- const newTabNote = t("docPanel.newTab", { defaultValue: "opens in a new tab" });
494
+ const newTabNote = t("docPanel.opensInNewTab", { defaultValue: "(opens in a new tab)" });
493
495
  if (summary === void 0 && links.length === 0) return null;
494
496
  return jsxs("div", {
495
497
  className: "mt-2 flex flex-col items-start gap-3",
@@ -1111,7 +1113,7 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
1111
1113
  function NavigationGuardDialog() {
1112
1114
  const prompt = useRegisterNavigationGuardDialog();
1113
1115
  const text = useNavigationGuardText();
1114
- const unsavedAction = useOptionalT()("navigation.unsavedAction");
1116
+ const unsavedAction = useOptionalT()("navigation.unsavedAction", { defaultValue: "An action on this screen has unsaved input" });
1115
1117
  const returnFocus = useRef(null);
1116
1118
  const wasOpen = useRef(false);
1117
1119
  const open = prompt !== null;
@@ -1134,7 +1136,7 @@ function NavigationGuardDialog() {
1134
1136
  event.preventDefault();
1135
1137
  element.focus();
1136
1138
  },
1137
- 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, {
1139
+ 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, {
1138
1140
  "data-testid": "nav-guard-stay",
1139
1141
  children: text.stay
1140
1142
  }), jsx(AlertDialogAction, {
@@ -1694,14 +1696,14 @@ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
1694
1696
  const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
1695
1697
  //#endregion
1696
1698
  //#region src/shell/use-nav-icon.tsx
1697
- const warned = new Set();
1699
+ const warned$1 = new Set();
1698
1700
  function resolveNavIcon(name, components) {
1699
1701
  if (name === void 0 || name === "") return void 0;
1700
1702
  const slot = components(name);
1701
1703
  if (typeof slot === "function" || typeof slot === "object" && slot !== null) return jsx(slot, { className: "h-4 w-4" });
1702
1704
  if (hasIcon(name)) return jsx(Icon, { name });
1703
- if (process.env.NODE_ENV !== "production" && !warned.has(name)) {
1704
- warned.add(name);
1705
+ if (process.env.NODE_ENV !== "production" && !warned$1.has(name)) {
1706
+ warned$1.add(name);
1705
1707
  console.warn(`[buro] unknown nav icon "${name}"`);
1706
1708
  }
1707
1709
  }
@@ -1710,6 +1712,53 @@ function useNavIcon() {
1710
1712
  return useCallback((name) => resolveNavIcon(name, (key) => components[key]), [components]);
1711
1713
  }
1712
1714
  //#endregion
1715
+ //#region src/layout/build-nav-items.ts
1716
+ function buildNavItems(args) {
1717
+ const { registry, caps, groupName, match, isVisible, entryFilter, icons } = args;
1718
+ const chain = match?.groupChain ?? [];
1719
+ const qualifiedMatch = match;
1720
+ const activeLeaf = qualifiedMatch == null ? void 0 : qualifiedMatch.kind === "group-index" ? canonicalNavKey({
1721
+ name: qualifiedMatch.target ?? qualifiedMatch.name,
1722
+ ref: qualifiedMatch.targetRef
1723
+ }) : canonicalNavKey(qualifiedMatch);
1724
+ const isEligible = (node) => isVisible(node) && (entryFilter ? entryFilter(node) : true);
1725
+ const out = [];
1726
+ for (const child of registry.groups.childrenOf(groupName)) {
1727
+ if (!isEligible(child)) continue;
1728
+ const kind = nodeKind(child);
1729
+ const childKey = canonicalNavKey(child);
1730
+ const href = navHrefFor(registry, childKey, { isVisible: isEligible });
1731
+ if (kind === "group") {
1732
+ const label = resolveNavLabel(caps, `groups.${childKey}`, declaredText("chrome", child, "label"));
1733
+ const active = chain.some((g) => canonicalNavKey(g) === childKey);
1734
+ out.push({
1735
+ kind: "group",
1736
+ name: childKey,
1737
+ label,
1738
+ href,
1739
+ icon: icons?.group?.(childKey),
1740
+ active
1741
+ });
1742
+ } else if (kind === "custom-page") out.push({
1743
+ kind: "custom-page",
1744
+ name: childKey,
1745
+ label: resolveNavLabel(caps, `pages.${childKey}`, declaredText("chrome", child, "label")),
1746
+ href,
1747
+ icon: icons?.page?.(childKey),
1748
+ active: activeLeaf === childKey
1749
+ });
1750
+ else out.push({
1751
+ kind: "resource",
1752
+ name: childKey,
1753
+ label: resolveResourceNavLabel(caps, childKey, child),
1754
+ href,
1755
+ icon: icons?.resource?.(childKey),
1756
+ active: activeLeaf === childKey
1757
+ });
1758
+ }
1759
+ return out;
1760
+ }
1761
+ //#endregion
1713
1762
  //#region src/layout/use-nav-tree.ts
1714
1763
  function isActiveHref(href, currentPath) {
1715
1764
  if (!currentPath) return false;
@@ -1723,8 +1772,7 @@ function useNavTree(opts = {}) {
1723
1772
  const passesEntryFilter = (entry) => entryFilter ? entryFilter(entry) : true;
1724
1773
  const app = useApp();
1725
1774
  const caps = useI18nCaps();
1726
- const visibilityCtx = useVisibilityCtx();
1727
- const isResourceVisible = (r) => evaluateVisible(r.visible, visibilityCtx);
1775
+ const isResourceVisible = useResourceVisible();
1728
1776
  const resolveLayout = useLayoutResolver();
1729
1777
  const match = useMatch();
1730
1778
  const isVisible = useNavVisible();
@@ -1734,6 +1782,7 @@ function useNavTree(opts = {}) {
1734
1782
  const allCustomPages = app.registry.customPages.list();
1735
1783
  const sections = app.registry.sections.list();
1736
1784
  const groups = app.registry.groups.rootNodes().filter((n) => nodeKind(n) === "group");
1785
+ useReportUnresolvedLayouts(app.registry.groups.list(), resolveLayout);
1737
1786
  const topLevelResources = allResources.filter((r) => hasListPage(r)).filter(isResourceVisible).filter(passesEntryFilter).filter((r) => !r.section && !r.group).map((r, idx) => ({
1738
1787
  r,
1739
1788
  idx
@@ -1743,8 +1792,8 @@ function useNavTree(opts = {}) {
1743
1792
  return ao !== bo ? ao - bo : a.idx - b.idx;
1744
1793
  }).map((x) => x.r);
1745
1794
  const topLevelCustomPages = allCustomPages.filter((p) => !p.section && !p.group).filter(pageAllowed).filter(passesEntryFilter);
1746
- const resourceHref = (def) => hrefFor(app.registry, canonicalResourceKey(def));
1747
- const resourceLabel = (def) => resolveResourceNavLabel(caps, canonicalResourceKey(def), def);
1795
+ const resourceHref = (def) => hrefFor(app.registry, canonicalResourceKey$1(def));
1796
+ const resourceLabel = (def) => resolveResourceNavLabel(caps, canonicalResourceKey$1(def), def);
1748
1797
  const pageLabel = (page) => resolveNavLabel(caps, `pages.${canonicalNavKey(page)}`, declaredText("chrome", page, "label"));
1749
1798
  const sectionLabel = (section) => resolveNavLabel(caps, `sections.${canonicalNavKey(section)}`, declaredText("chrome", section, "label"));
1750
1799
  const groupLabel = (group) => resolveNavLabel(caps, `groups.${canonicalNavKey(group)}`, declaredText("chrome", group, "label"));
@@ -1763,7 +1812,7 @@ function useNavTree(opts = {}) {
1763
1812
  const href = resourceHref(def);
1764
1813
  return {
1765
1814
  kind: "resource",
1766
- key: canonicalResourceKey(def),
1815
+ key: canonicalResourceKey$1(def),
1767
1816
  label: resourceLabel(def),
1768
1817
  href,
1769
1818
  icon: def.icon,
@@ -1926,6 +1975,10 @@ function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection,
1926
1975
  const t = useT();
1927
1976
  const RouterLink = useLinkComponent();
1928
1977
  const navIcon = useNavIcon();
1978
+ const intent = useOriginIntent();
1979
+ const jump = (entry) => () => {
1980
+ if (entry.href !== void 0) intent(entry.href, "jump");
1981
+ };
1929
1982
  const ownOr = (own, name) => own !== void 0 ? own : navIcon(name);
1930
1983
  const groupAsSection = (key) => app.registry.sections.get(key) ?? app.registry.groups.get(key);
1931
1984
  const sectionIcon = (key, name) => {
@@ -1952,6 +2005,7 @@ function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection,
1952
2005
  href: entry.href ?? "",
1953
2006
  "aria-current": entry.active ? "page" : void 0,
1954
2007
  "data-testid": entryTestId(entry),
2008
+ onClick: jump(entry),
1955
2009
  children: [icon != null && jsx("span", {
1956
2010
  className: "inline-flex h-4 w-4 items-center justify-center",
1957
2011
  "aria-hidden": "true",
@@ -1967,11 +2021,12 @@ function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection,
1967
2021
  href: entry.href ?? "",
1968
2022
  "aria-current": entry.active ? "page" : void 0,
1969
2023
  "data-testid": entryTestId(entry),
2024
+ onClick: jump(entry),
1970
2025
  children: jsx("span", { children: entry.label })
1971
2026
  })
1972
2027
  }) }, entry.key);
1973
2028
  const [top, ...blocks] = tree;
1974
- 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) => {
2029
+ 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) => {
1975
2030
  const key = block.key ?? "";
1976
2031
  if (block.label === null) return jsx(SidebarGroup, { children: jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) }) }, key);
1977
2032
  if (block.collapsible) return jsx(CollapsibleNavSection, {
@@ -2015,126 +2070,31 @@ function AppSidebar({ header, filters, nav, footer }) {
2015
2070
  });
2016
2071
  }
2017
2072
  //#endregion
2018
- //#region src/layout/layouts/secondary-sidebar-layout.tsx
2019
- function Row({ it }) {
2020
- const RouterLink = useLinkComponent();
2021
- return jsx("li", { children: jsxs(RouterLink, {
2022
- href: it.href,
2023
- "data-testid": `nav-secondary-item:${it.name}`,
2024
- "aria-current": it.active ? "page" : void 0,
2025
- 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"),
2026
- children: [it.icon != null && jsx("span", {
2027
- className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
2028
- "aria-hidden": "true",
2029
- children: it.icon
2030
- }), jsx("span", {
2031
- className: "truncate",
2032
- title: it.label,
2033
- children: it.label
2034
- })]
2035
- }) });
2036
- }
2037
- function SecondarySidebarLayout({ group, groupKey, items, children }) {
2038
- const RouterLink = useLinkComponent();
2039
- const caps = useI18nCaps();
2040
- const id = groupKey ?? group.name;
2041
- const label = resolveNavLabel(caps, `groups.${id}`, declaredText("chrome", group, "label"));
2042
- return jsxs("div", {
2043
- className: "md:-m-6 md:flex md:min-h-[calc(100%+3rem)]",
2044
- "data-testid": `nav-secondary:${id}`,
2045
- children: [
2046
- jsx("aside", {
2047
- className: "hidden w-64 shrink-0 border-r bg-background text-foreground md:block",
2048
- children: jsxs("div", {
2049
- className: "space-y-4 p-4",
2050
- children: [jsx("p", {
2051
- className: "px-3 text-base font-semibold tracking-tight",
2052
- children: label
2053
- }), jsx("nav", {
2054
- "aria-label": label,
2055
- children: jsx("ul", {
2056
- className: "space-y-0.5",
2057
- children: items.map((it) => jsx(Row, { it }, it.name))
2058
- })
2059
- })]
2060
- })
2061
- }),
2062
- jsx("nav", {
2063
- "aria-label": label,
2064
- "data-testid": `nav-secondary-select:${id}`,
2065
- 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",
2066
- children: items.map((it) => jsx(RouterLink, {
2067
- href: it.href,
2068
- "aria-current": it.active ? "page" : void 0,
2069
- 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"),
2070
- children: it.label
2071
- }, it.name))
2072
- }),
2073
- jsx("div", {
2074
- className: "min-w-0 flex-1 md:overflow-x-hidden md:p-6",
2075
- children
2076
- })
2077
- ]
2078
- });
2079
- }
2080
- //#endregion
2081
- //#region src/layout/layouts/tabs-layout.tsx
2082
- function TabsLayout({ group, groupKey, items, children }) {
2083
- const RouterLink = useLinkComponent();
2084
- const caps = useI18nCaps();
2085
- const id = groupKey ?? group.name;
2086
- const label = resolveNavLabel(caps, `groups.${id}`, declaredText("chrome", group, "label"));
2087
- return jsxs("div", {
2088
- className: "flex flex-col gap-4",
2089
- "data-testid": `nav-tabs:${id}`,
2090
- children: [jsx("nav", {
2091
- "aria-label": label,
2092
- 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",
2093
- children: items.map((it) => jsxs(RouterLink, {
2094
- href: it.href,
2095
- "aria-current": it.active ? "page" : void 0,
2096
- "data-testid": `nav-tab:${it.name}`,
2097
- 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"),
2098
- children: [
2099
- it.icon != null && jsx("span", {
2100
- className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
2101
- "aria-hidden": "true",
2102
- children: it.icon
2103
- }),
2104
- it.label,
2105
- jsx("span", {
2106
- "aria-hidden": "true",
2107
- 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")
2108
- })
2109
- ]
2110
- }, it.name))
2111
- }), jsx("div", {
2112
- className: "min-w-0",
2113
- children
2114
- })]
2115
- });
2116
- }
2117
- //#endregion
2118
- //#region src/layout/nav-area-layout.tsx
2073
+ //#region src/layout/nav-chrome.tsx
2119
2074
  const DETAIL_ACTIONS = new Set([
2120
2075
  "show",
2121
2076
  "edit",
2122
2077
  "create"
2123
2078
  ]);
2124
- function NavAreaLayout({ groupName, children, entryFilter, icons }) {
2079
+ function NavChrome({ children, entryFilter, icons }) {
2125
2080
  const app = useApp();
2126
2081
  const caps = useI18nCaps();
2127
2082
  const isVisible = useNavVisible();
2128
2083
  const match = useMatch();
2129
2084
  const resolveLayout = useLayoutResolver();
2130
- const group = app.registry.groups.get(groupName);
2131
- if (!group) return jsx(Fragment$1, { children });
2132
- const groupKey = canonicalNavKey(group);
2133
- const claimed = [groupKey, ...groupChain(app.registry, group).map(canonicalNavKey)];
2134
- const collapse = group.chromeOn === "list" && match?.kind === "resource" && DETAIL_ACTIONS.has(match.action);
2135
- const Layout = resolveLayout(group.layout);
2085
+ useReportUnresolvedLayouts(match?.groupChain ?? [], resolveLayout);
2086
+ if (!match) return jsx(NavChromeMountedProvider, {
2087
+ value: true,
2088
+ children
2089
+ });
2136
2090
  let content = children;
2137
- if (Layout && !collapse) {
2091
+ for (let i = match.groupChain.length - 1; i >= 0; i--) {
2092
+ const group = match.groupChain[i];
2093
+ if (!group) continue;
2094
+ const groupKey = canonicalNavKey(group);
2095
+ if (group.chromeOn === "list" && match.kind === "resource" && DETAIL_ACTIONS.has(match.action)) continue;
2096
+ const Layout = resolveLayout(group.layout);
2097
+ if (!Layout) continue;
2138
2098
  const items = buildNavItems({
2139
2099
  registry: app.registry,
2140
2100
  caps,
@@ -2148,11 +2108,11 @@ function NavAreaLayout({ groupName, children, entryFilter, icons }) {
2148
2108
  group,
2149
2109
  groupKey,
2150
2110
  items,
2151
- children
2111
+ children: content
2152
2112
  });
2153
2113
  }
2154
- return jsx(NavChromeBoundaryProvider, {
2155
- claim: claimed,
2114
+ return jsx(NavChromeMountedProvider, {
2115
+ value: true,
2156
2116
  children: content
2157
2117
  });
2158
2118
  }
@@ -2165,19 +2125,27 @@ function Chevron() {
2165
2125
  "aria-hidden": "true"
2166
2126
  });
2167
2127
  }
2168
- function Breadcrumbs(props) {
2128
+ function Breadcrumbs(input) {
2129
+ const props = input;
2169
2130
  const { leadingSegments, className, docScopes, "data-testid": testId } = props;
2170
2131
  const app = useApp();
2171
- const def = props.resource === void 0 ? void 0 : app.registry.get(props.resource);
2132
+ const resource = props.resource === void 0 ? void 0 : routeResourceKey(props.resource);
2133
+ const def = resource === void 0 ? void 0 : app.registry.get(resource);
2172
2134
  const tid = useTestIds();
2173
2135
  const t = useOptionalT();
2174
2136
  const RouterLink = useLinkComponent();
2175
2137
  const caps = useI18nCaps(def?.name);
2138
+ const intent = useOriginIntent();
2139
+ const onJump = (seg) => {
2140
+ const href = seg.href;
2141
+ return seg.jump === true && href !== void 0 ? () => intent(href, "jump") : void 0;
2142
+ };
2176
2143
  const trailScopes = useDocTrailScopes(def?.name);
2177
2144
  const leading = (name) => leadingSegments?.map((seg, i) => jsxs("span", {
2178
2145
  className: "flex min-w-0 items-center gap-1",
2179
2146
  children: [seg.href ? jsx(RouterLink, {
2180
2147
  href: seg.href,
2148
+ onClick: onJump(seg),
2181
2149
  "data-testid": `${tid.navBreadcrumb(name)}:lead:${i}`,
2182
2150
  className: cn(LINK_CLASS, "truncate"),
2183
2151
  title: seg.label,
@@ -2212,9 +2180,34 @@ function Breadcrumbs(props) {
2212
2180
  ]
2213
2181
  });
2214
2182
  if (!def) return null;
2215
- const { recordId, pageLabel, middleSegments } = props;
2216
- const resourceKey = canonicalResourceKey(def);
2217
- const listHref = hrefFor(app.registry, resourceKey);
2183
+ const { recordId, pageLabel, middleSegments, rootSegments } = props;
2184
+ const resourceKey = canonicalResourceKey$1(def);
2185
+ if (rootSegments !== void 0 && rootSegments.length > 0) return jsxs("nav", {
2186
+ className: navClass,
2187
+ "aria-label": ariaLabel,
2188
+ "data-testid": testId,
2189
+ children: [
2190
+ rootSegments.map((seg, i) => jsxs("span", {
2191
+ className: "flex min-w-0 items-center gap-1",
2192
+ children: [i > 0 && jsx(Chevron, {}), seg.href ? jsx(RouterLink, {
2193
+ href: seg.href,
2194
+ onClick: onJump(seg),
2195
+ "data-testid": `${tid.navBreadcrumb(resourceKey)}:root:${i}`,
2196
+ className: cn(LINK_CLASS, "truncate"),
2197
+ title: seg.label,
2198
+ children: seg.label
2199
+ }) : jsx("span", {
2200
+ "data-testid": `${tid.navBreadcrumb(resourceKey)}:root:${i}`,
2201
+ className: "truncate",
2202
+ title: seg.label,
2203
+ children: seg.label
2204
+ })]
2205
+ }, `root:${i}:${seg.label}`)),
2206
+ pageLabel && jsxs(Fragment$1, { children: [jsx(Chevron, {}), current(tid.navBreadcrumb(resourceKey, recordId), pageLabel)] }),
2207
+ help
2208
+ ]
2209
+ });
2210
+ const listHref = props.resourceHref ?? hrefFor(app.registry, resourceKey);
2218
2211
  const resourceLabel = resolveResourceLabel(caps, resourceKey, "plural", def);
2219
2212
  const resourceIsCurrent = !pageLabel && !(middleSegments && middleSegments.length > 0);
2220
2213
  return jsxs("nav", {
@@ -2223,7 +2216,12 @@ function Breadcrumbs(props) {
2223
2216
  "data-testid": testId,
2224
2217
  children: [
2225
2218
  leading(resourceKey),
2226
- resourceIsCurrent ? current(tid.navBreadcrumb(resourceKey), resourceLabel) : jsx(RouterLink, {
2219
+ resourceIsCurrent ? current(tid.navBreadcrumb(resourceKey), resourceLabel) : !hasListPage(def) ? jsx("span", {
2220
+ "data-testid": tid.navBreadcrumb(resourceKey),
2221
+ className: "truncate",
2222
+ title: resourceLabel,
2223
+ children: resourceLabel
2224
+ }) : jsx(RouterLink, {
2227
2225
  href: listHref,
2228
2226
  "data-testid": tid.navBreadcrumb(resourceKey),
2229
2227
  className: cn(LINK_CLASS, "truncate"),
@@ -2234,6 +2232,7 @@ function Breadcrumbs(props) {
2234
2232
  className: "flex min-w-0 items-center gap-1",
2235
2233
  children: [jsx(Chevron, {}), seg.href ? jsx(RouterLink, {
2236
2234
  href: seg.href,
2235
+ onClick: onJump(seg),
2237
2236
  "data-testid": `${tid.navBreadcrumb(resourceKey)}:mid:${i}`,
2238
2237
  className: cn(LINK_CLASS, "truncate"),
2239
2238
  title: seg.label,
@@ -2251,6 +2250,12 @@ function Breadcrumbs(props) {
2251
2250
  }
2252
2251
  //#endregion
2253
2252
  //#region src/layout/record-breadcrumbs.tsx
2253
+ function viewOrigin(ctx, trail, resource) {
2254
+ if (trail === void 0 || trail.resource === void 0 || trail.segments.length > 0) return void 0;
2255
+ const target = ctx.registry.get(trail.resource)?.routesTo?.show;
2256
+ if (target === void 0 || ctx.registry.get(target) !== ctx.registry.get(resource)) return void 0;
2257
+ return trail.resource;
2258
+ }
2254
2259
  function RecordBreadcrumbs() {
2255
2260
  const ctx = useTrailContext();
2256
2261
  const match = useMatch();
@@ -2258,9 +2263,17 @@ function RecordBreadcrumbs() {
2258
2263
  const route = recordTrailRoute(ctx.registry, match);
2259
2264
  const published = usePublishedRecordTrail(route === null ? "" : recordTrailKey(route.resource, route.id, route.flow === void 0 ? route.action : `flow:${route.flow}`));
2260
2265
  const idTitle = useRecordTitle(route?.resource, route?.id);
2266
+ usePublishOriginTitle(published?.title || void 0);
2267
+ const origin = useNavigationOrigin();
2268
+ const rooted = origin === null ? void 0 : originTrail(ctx, origin);
2269
+ const view = route === null ? void 0 : viewOrigin(ctx, rooted, route.resource);
2270
+ const viewScopes = useDocTrailScopes(view);
2271
+ const ownScopes = useDocTrailScopes(view === void 0 ? void 0 : route?.resource);
2272
+ const docScopes = view === void 0 ? {} : { docScopes: [...new Set([...viewScopes, ...ownScopes])] };
2261
2273
  if (route === null || match === null) return null;
2262
2274
  const { resource, id, groupChain } = route;
2263
2275
  const title = published?.title ?? idTitle;
2276
+ const withOrigin = rooted === void 0 ? {} : { origin: rooted };
2264
2277
  if (route.flow !== void 0) {
2265
2278
  if (effectiveFlow(snapshot, match.ref, route.flow) === void 0) return null;
2266
2279
  const flowLabel = published?.flowLabel;
@@ -2270,8 +2283,10 @@ function RecordBreadcrumbs() {
2270
2283
  action: "show",
2271
2284
  id,
2272
2285
  title: idTitle,
2273
- groupChain
2286
+ groupChain,
2287
+ ...withOrigin
2274
2288
  }),
2289
+ ...docScopes,
2275
2290
  "data-testid": "record-breadcrumbs"
2276
2291
  });
2277
2292
  return jsx(Breadcrumbs, {
@@ -2280,8 +2295,10 @@ function RecordBreadcrumbs() {
2280
2295
  action: "edit",
2281
2296
  id,
2282
2297
  title,
2283
- groupChain
2298
+ groupChain,
2299
+ ...withOrigin
2284
2300
  }),
2301
+ ...docScopes,
2285
2302
  pageLabel: flowLabel,
2286
2303
  "data-testid": "record-breadcrumbs"
2287
2304
  });
@@ -2292,22 +2309,24 @@ function RecordBreadcrumbs() {
2292
2309
  action: route.action,
2293
2310
  id,
2294
2311
  title,
2295
- groupChain
2312
+ groupChain,
2313
+ ...withOrigin
2296
2314
  }),
2315
+ ...docScopes,
2297
2316
  "data-testid": "record-breadcrumbs"
2298
2317
  });
2299
2318
  }
2300
2319
  //#endregion
2301
2320
  //#region src/layout/default-nav-layouts.ts
2302
- const defaultNavLayouts = {
2303
- [layoutSlot("secondary-sidebar")]: SecondarySidebarLayout,
2304
- [layoutSlot("tabs")]: TabsLayout
2305
- };
2321
+ const defaultNavLayouts = { ...BUILTIN_LAYOUTS };
2306
2322
  //#endregion
2307
2323
  //#region src/layout/context-picker.tsx
2308
- function ContextPicker({ sliceName, label, options, value, onChange, placeholder, disabled, className, hideLabel }) {
2324
+ const ALL = "\0all";
2325
+ function ContextPicker({ sliceName, label, options, value, onChange, placeholder, disabled, className, hideLabel, allOption }) {
2309
2326
  const tid = useTestIds();
2310
2327
  const t = useOptionalT();
2328
+ const chromeT = useChromeT();
2329
+ const allLabel = typeof allOption === "string" ? allOption : chromeT("scope.all", { defaultValue: "All" });
2311
2330
  const displayLabel = label ?? humanizeKey(sliceName);
2312
2331
  return jsxs("div", {
2313
2332
  className: cn("space-y-1", className),
@@ -2316,21 +2335,112 @@ function ContextPicker({ sliceName, label, options, value, onChange, placeholder
2316
2335
  className: hideLabel ? "sr-only" : "text-xs font-medium text-muted-foreground uppercase tracking-wide",
2317
2336
  children: displayLabel
2318
2337
  }), jsxs(Select, {
2319
- value: value ?? "",
2320
- onValueChange: onChange,
2338
+ value: value ?? (allOption === void 0 ? "" : ALL),
2339
+ onValueChange: (next) => {
2340
+ if (allOption === void 0) onChange(next);
2341
+ else onChange(next === ALL ? null : next);
2342
+ },
2321
2343
  disabled,
2322
2344
  children: [jsx(SelectTrigger, {
2323
2345
  id: `context-picker:${sliceName}`,
2324
2346
  "data-testid": tid.contextPicker(sliceName),
2325
2347
  className: cn("w-full", hideLabel ? "h-8 text-xs" : void 0),
2326
2348
  children: jsx(SelectValue, { placeholder: placeholder ?? t("field.select", { defaultValue: "Select…" }) })
2327
- }), jsx(SelectContent, { children: options.map((o) => jsx(SelectItem, {
2349
+ }), jsxs(SelectContent, { children: [allOption === void 0 ? null : jsx(SelectItem, {
2350
+ value: ALL,
2351
+ "data-testid": tid.contextPickerOption(sliceName, "all"),
2352
+ children: allLabel
2353
+ }), options.map((o) => jsx(SelectItem, {
2328
2354
  value: o.value,
2329
2355
  "data-testid": tid.contextPickerOption(sliceName, o.value),
2330
2356
  children: o.label
2331
- }, o.value)) })]
2357
+ }, o.value))] })]
2358
+ })]
2359
+ });
2360
+ }
2361
+ //#endregion
2362
+ //#region src/layout/scope-dimension-picker.tsx
2363
+ function ScopeDimensionPicker(props) {
2364
+ const scope = useScope();
2365
+ const definition = scope.definition?.dimensions[props.dimension];
2366
+ if (definition === void 0) throw new Error(`ScopeDimensionPicker: '${props.dimension}' is not a dimension of the app's defineScope(...)`);
2367
+ if (definition.select === "none") return null;
2368
+ const source = definition.values;
2369
+ if (source === void 0) throw new Error(`ScopeDimensionPicker: dimension '${props.dimension}' declares no values; add values: { resource } or a { value, label } list`);
2370
+ const allowed = scope.access[props.dimension]?.allowed;
2371
+ const permitted = Array.isArray(allowed) ? allowed : void 0;
2372
+ const common = {
2373
+ ...props,
2374
+ multiple: definition.select === "multiple",
2375
+ all: definition.all,
2376
+ selected: scope.selection[props.dimension] ?? null,
2377
+ select: (value) => scope.select(props.dimension, value)
2378
+ };
2379
+ if (Array.isArray(source)) {
2380
+ const options = source.filter((option) => permitted === void 0 || permitted.includes(option.value));
2381
+ return jsx(DimensionControl, {
2382
+ ...common,
2383
+ options
2384
+ });
2385
+ }
2386
+ const { resource, label } = source;
2387
+ return jsx(ResourceDimension, {
2388
+ ...common,
2389
+ resource,
2390
+ ...label ? { labelField: label } : {}
2391
+ });
2392
+ }
2393
+ function ResourceDimension({ resource, labelField, ...rest }) {
2394
+ const options = useResourceDimensionOptions(rest.dimension, resource, labelField);
2395
+ return jsx(DimensionControl, {
2396
+ ...rest,
2397
+ options
2398
+ });
2399
+ }
2400
+ function DimensionControl({ dimension, label, hideLabel, className, multiple, all, selected, select, options }) {
2401
+ const tid = useTestIds();
2402
+ const chromeT = useChromeT();
2403
+ const byText = useMemo(() => new Map(options.map((option) => [String(option.value), option.value])), [options]);
2404
+ const textOptions = useMemo(() => options.map((option) => ({
2405
+ value: String(option.value),
2406
+ label: option.label
2407
+ })), [options]);
2408
+ const picked = (Array.isArray(selected) ? selected : selected === null ? [] : [selected]).map(String);
2409
+ const text = label ?? humanizeKey(dimension);
2410
+ if (multiple) return jsxs("div", {
2411
+ className: cn("space-y-1", className),
2412
+ children: [jsx("span", {
2413
+ className: hideLabel ? "sr-only" : "text-xs font-medium text-muted-foreground uppercase tracking-wide",
2414
+ children: text
2415
+ }), jsx(FacetedFilterPicker, {
2416
+ title: text,
2417
+ options: textOptions,
2418
+ selected: picked,
2419
+ ...all ? { emptyLabel: chromeT("scope.all", { defaultValue: "All" }) } : { clearable: false },
2420
+ onChange: (next) => {
2421
+ if (next.length === 0 && !all) return;
2422
+ select(next.length === 0 ? null : next.map((value) => byText.get(value) ?? value));
2423
+ },
2424
+ getTestId: (role, value) => role === "trigger" ? tid.contextPicker(dimension) : role === "option" ? tid.contextPickerOption(dimension, value ?? "") : role === "clear" ? tid.contextPickerOption(dimension, "all") : void 0
2332
2425
  })]
2333
2426
  });
2427
+ const common = {
2428
+ sliceName: dimension,
2429
+ ...label === void 0 ? {} : { label },
2430
+ options: textOptions,
2431
+ value: picked[0] ?? null,
2432
+ ...hideLabel === void 0 ? {} : { hideLabel },
2433
+ ...className === void 0 ? {} : { className }
2434
+ };
2435
+ const choose = (value) => select(value === null ? null : byText.get(value) ?? value);
2436
+ return all ? jsx(ContextPicker, {
2437
+ ...common,
2438
+ allOption: true,
2439
+ onChange: choose
2440
+ }) : jsx(ContextPicker, {
2441
+ ...common,
2442
+ onChange: choose
2443
+ });
2334
2444
  }
2335
2445
  //#endregion
2336
2446
  //#region src/layout/language-switch.tsx
@@ -2521,7 +2631,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2521
2631
  const navigate = useRouteNavigate();
2522
2632
  const isVisible = useNavVisible();
2523
2633
  const resolveLayout = useLayoutResolver();
2524
- const isResourceVisible = (r) => evaluateVisible(r.visible, visibilityCtx);
2634
+ const isResourceVisible = useResourceVisible();
2525
2635
  const pageAllowed = usePageAllowed();
2526
2636
  const isPageVisible = (p) => evaluateVisible(p.visible, visibilityCtx) && pageAllowed(p);
2527
2637
  const { topLevelResources, topLevelPages, sectionGroups } = useMemo(() => {
@@ -2547,15 +2657,15 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2547
2657
  }, [app, visibilityCtx]);
2548
2658
  const goTo = (href) => {
2549
2659
  onSelect?.();
2550
- navigate(href);
2660
+ navigate(href, { origin: "jump" });
2551
2661
  };
2552
- const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey(def), "plural", def);
2662
+ const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey$1(def), "plural", def);
2553
2663
  const pageLabel = (def) => resolveNavLabel(caps, `pages.${canonicalNavKey(def)}`, declaredText("chrome", def, "label"));
2554
2664
  const groups = [];
2555
2665
  if (topLevelResources.length > 0) groups.push(jsx(CommandGroup, {
2556
2666
  heading: t("commandPalette.resources", { defaultValue: "Resources" }),
2557
2667
  children: topLevelResources.map((def) => {
2558
- const resource = canonicalResourceKey(def);
2668
+ const resource = canonicalResourceKey$1(def);
2559
2669
  const label = resourceLabel(def);
2560
2670
  const href = hrefFor(app.registry, resource, { isVisible });
2561
2671
  return jsx(CommandItem, {
@@ -2604,7 +2714,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2604
2714
  children: label
2605
2715
  }, `section:${sectionKey}:page:${pageKey}`);
2606
2716
  }
2607
- const resource = canonicalResourceKey(child);
2717
+ const resource = canonicalResourceKey$1(child);
2608
2718
  const label = resourceLabel(child);
2609
2719
  const href = hrefFor(app.registry, resource, { isVisible });
2610
2720
  return jsx(CommandItem, {
@@ -2656,7 +2766,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
2656
2766
  const resource = child;
2657
2767
  if (!hasListPage(resource)) continue;
2658
2768
  if (!isResourceVisible(resource)) continue;
2659
- const resourceId = canonicalResourceKey(resource);
2769
+ const resourceId = canonicalResourceKey$1(resource);
2660
2770
  const label = resourceLabel(resource);
2661
2771
  const href = hrefFor(app.registry, resourceId, { isVisible });
2662
2772
  items.push(jsx(CommandItem, {
@@ -2695,10 +2805,10 @@ function opensInQuick(registry, resource, mode) {
2695
2805
  }
2696
2806
  //#endregion
2697
2807
  //#region src/layout/command-palette-recent-group.tsx
2698
- function entryHref(registry, entry, visibilityCtx) {
2808
+ function entryHref(registry, entry, isResourceVisible) {
2699
2809
  const def = registry.get(entry.canonicalRef);
2700
2810
  if (def === void 0) return void 0;
2701
- if (!evaluateVisible(def.visible, visibilityCtx)) return void 0;
2811
+ if (!isResourceVisible(def)) return void 0;
2702
2812
  for (const action of ["show", "edit"]) if (recordRouteAvailable(registry, entry.canonicalRef, action)) return hrefFor(registry, entry.canonicalRef, {
2703
2813
  action,
2704
2814
  id: entry.id
@@ -2712,34 +2822,46 @@ function CommandPaletteRecentGroup({ onSelect, getTestId, heading, refreshKey, w
2712
2822
  const navigate = useRouteNavigate();
2713
2823
  const quick = useQuick();
2714
2824
  const userId = useCurrentUserId();
2715
- const visibilityCtx = useVisibilityCtx();
2825
+ const isResourceVisible = useResourceVisible();
2716
2826
  const resolvedHeading = heading ?? t("commandPalette.recent", { defaultValue: "Recent" });
2717
- const [hydrated, setHydrated] = useState(false);
2827
+ const store = usePlatformStorage().local;
2828
+ const [stored, setStored] = useState([]);
2718
2829
  useEffect(() => {
2719
- setHydrated(true);
2720
- }, []);
2721
- const recents = useMemo(() => {
2722
- if (!hydrated || userId === null) return [];
2723
- return readRecentRecords(userId).flatMap((entry) => {
2724
- const href = entryHref(app.registry, entry, visibilityCtx);
2725
- return href === void 0 ? [] : [{
2726
- entry,
2727
- href
2728
- }];
2729
- });
2830
+ if (userId === null) {
2831
+ setStored([]);
2832
+ return;
2833
+ }
2834
+ let current = true;
2835
+ const read = readRecentRecords(store, userId);
2836
+ if (Array.isArray(read)) setStored(read);
2837
+ else read.then((entries) => {
2838
+ if (current) setStored(entries);
2839
+ }, () => void 0);
2840
+ return () => {
2841
+ current = false;
2842
+ };
2730
2843
  }, [
2731
- hydrated,
2844
+ store,
2732
2845
  userId,
2733
- refreshKey,
2846
+ refreshKey
2847
+ ]);
2848
+ const recents = useMemo(() => stored.flatMap((entry) => {
2849
+ const href = entryHref(app.registry, entry, isResourceVisible);
2850
+ return href === void 0 ? [] : [{
2851
+ entry,
2852
+ href
2853
+ }];
2854
+ }), [
2855
+ stored,
2734
2856
  app.registry,
2735
- visibilityCtx
2857
+ isResourceVisible
2736
2858
  ]);
2737
2859
  if ((useCommandState((state) => state.search) ?? "").length > 0) return null;
2738
2860
  if (recents.length === 0) return null;
2739
2861
  const goTo = (href, entry) => {
2740
2862
  onSelect?.();
2741
2863
  if (href === null) quick.show(entry.canonicalRef, entry.id);
2742
- else navigate(href);
2864
+ else navigate(href, { origin: "jump" });
2743
2865
  };
2744
2866
  return jsxs(Fragment, { children: [jsx(CommandGroup, {
2745
2867
  heading: resolvedHeading,
@@ -2797,23 +2919,24 @@ function CommandPaletteRecordsGroup({ enabled = true, pageSize = 5, minQueryLeng
2797
2919
  const t = useOptionalT();
2798
2920
  const navigate = useRouteNavigate();
2799
2921
  const quick = useQuick();
2922
+ const isResourceVisible = useResourceVisible();
2800
2923
  const resolvedHeading = heading ?? t("commandPalette.records", { defaultValue: "Records" });
2801
2924
  const liveQuery = useCommandState((state) => state.search) ?? "";
2802
2925
  const debouncedQuery = useDebouncedValue(liveQuery, debounceMs);
2803
2926
  const searchableResources = useMemo(() => {
2804
- return app.registry.list().filter((r) => isShowAvailable(app.registry, canonicalResourceKey(r)) || opensInQuick(app.registry, canonicalResourceKey(r), "show")).map((def) => {
2927
+ return app.registry.list().filter((r) => isShowAvailable(app.registry, canonicalResourceKey$1(r)) || opensInQuick(app.registry, canonicalResourceKey$1(r), "show")).filter(isResourceVisible).map((def) => {
2805
2928
  const displayField = resolveDisplayField(def);
2806
2929
  return displayField ? {
2807
2930
  def,
2808
2931
  displayField
2809
2932
  } : null;
2810
2933
  }).filter((entry) => entry !== null);
2811
- }, [app.registry]);
2934
+ }, [app.registry, isResourceVisible]);
2812
2935
  if (!(enabled && debouncedQuery.length >= minQueryLength && searchableResources.length > 0)) return null;
2813
2936
  const goTo = (href, resource, id) => {
2814
2937
  onSelect?.();
2815
2938
  if (href === null) quick.show(resource, id);
2816
- else navigate(href);
2939
+ else navigate(href, { origin: "jump" });
2817
2940
  };
2818
2941
  return jsx(CommandGroup, {
2819
2942
  heading: resolvedHeading,
@@ -2827,12 +2950,12 @@ function CommandPaletteRecordsGroup({ enabled = true, pageSize = 5, minQueryLeng
2827
2950
  recordHref,
2828
2951
  registry: app.registry,
2829
2952
  getTestId
2830
- }, canonicalResourceKey(def)))
2953
+ }, canonicalResourceKey$1(def)))
2831
2954
  }, "records");
2832
2955
  }
2833
2956
  function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recordHref, registry, getTestId }) {
2834
2957
  const caps = useI18nCaps();
2835
- const resource = canonicalResourceKey(def);
2958
+ const resource = canonicalResourceKey$1(def);
2836
2959
  const access = useListAccess(resource);
2837
2960
  const titleVisible = access.status === "ready" && access.fieldVisible(displayField);
2838
2961
  const shouldFetch = query.length > 0 && titleVisible;
@@ -2851,28 +2974,40 @@ function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recor
2851
2974
  enabled: shouldFetch
2852
2975
  });
2853
2976
  const report = useCommandPaletteResults(`records:${resource}`);
2977
+ const rowSubtitle = useRowSubtitle(resource);
2854
2978
  const rows = !shouldFetch || q.isLoading || q.isError ? [] : q.data?.data ?? [];
2855
2979
  useEffect(() => {
2856
2980
  report(rows.length);
2857
2981
  }, [report, rows.length]);
2858
2982
  if (rows.length === 0) return null;
2859
- const resourceLabel = resolveResourceLabel(caps, canonicalResourceKey(def), "single", def);
2983
+ const resourceLabel = resolveResourceLabel(caps, canonicalResourceKey$1(def), "single", def);
2984
+ const declaredIdField = def.data?.idField;
2985
+ const idField = typeof declaredIdField === "string" && declaredIdField.length > 0 ? declaredIdField : "id";
2860
2986
  return jsx(Fragment$1, { children: rows.map((row) => {
2861
- const idVal = row.id ?? row.event_id ?? row.job_id;
2987
+ const idVal = row[idField] ?? row.id ?? row.event_id ?? row.job_id;
2862
2988
  const id = idVal == null ? "" : String(idVal);
2863
2989
  if (!id) return null;
2864
2990
  const display = titleVisible ? row[displayField] : void 0;
2865
2991
  const labelText = typeof display === "string" && display.length > 0 ? display : id;
2992
+ const subtitle = rowSubtitle(row) ?? null;
2866
2993
  const href = recordHref ? recordHref(resource, id) : isShowAvailable(registry, resource) ? resourceShowHref(registry, def, resource, id) : null;
2867
2994
  return jsxs(CommandItem, {
2868
2995
  value: `record:${resource}:${id}:${labelText}`,
2869
2996
  forceMount: true,
2870
2997
  onSelect: () => goTo(href, resource, id),
2871
2998
  "data-testid": getTestId?.("records-record", `${resource}:${id}`),
2872
- children: [jsxs("span", {
2873
- className: "text-muted-foreground group-data-[selected=true]:text-accent-foreground mr-2",
2874
- children: [resourceLabel, ":"]
2875
- }), labelText]
2999
+ children: [
3000
+ jsxs("span", {
3001
+ className: "text-muted-foreground group-data-[selected=true]:text-accent-foreground mr-2",
3002
+ children: [resourceLabel, ":"]
3003
+ }),
3004
+ labelText,
3005
+ subtitle === null ? null : jsx("span", {
3006
+ "data-buro-region": "record-subtitle",
3007
+ className: "ml-auto truncate pl-2 text-xs text-muted-foreground group-data-[selected=true]:text-accent-foreground",
3008
+ children: subtitle
3009
+ })
3010
+ ]
2876
3011
  }, `record:${resource}:${id}`);
2877
3012
  }) });
2878
3013
  }
@@ -3003,7 +3138,7 @@ function CommandPaletteActionsGroup({ actions, onSelect, getTestId, heading }) {
3003
3138
  function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, heading, createHref, prefix }) {
3004
3139
  const caps = useI18nCaps();
3005
3140
  const app = useApp();
3006
- const visibilityCtx = useVisibilityCtx();
3141
+ const isResourceVisible = useResourceVisible();
3007
3142
  const gate = useAvailabilityGate();
3008
3143
  const t = useOptionalT();
3009
3144
  const navigate = useRouteNavigate();
@@ -3011,16 +3146,16 @@ function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, headin
3011
3146
  const resolvedHeading = heading ?? t("commandPalette.create", { defaultValue: "Create" });
3012
3147
  const resolvedPrefix = prefix ?? t("commandPalette.createNew", { defaultValue: "Create new" });
3013
3148
  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);
3149
+ 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
3150
  }, [
3016
3151
  app,
3017
3152
  gate,
3018
- visibilityCtx,
3153
+ isResourceVisible,
3019
3154
  resourceFilter
3020
3155
  ]);
3021
3156
  if (visibleResources.length === 0) return null;
3022
3157
  const goTo = (href) => {
3023
- navigate(href);
3158
+ navigate(href, { origin: "jump" });
3024
3159
  onSelect?.();
3025
3160
  };
3026
3161
  const createIn = (def, href) => {
@@ -3029,13 +3164,13 @@ function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, headin
3029
3164
  return;
3030
3165
  }
3031
3166
  onSelect?.();
3032
- quick.create(canonicalResourceKey(def));
3167
+ quick.create(canonicalResourceKey$1(def));
3033
3168
  };
3034
- const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey(def), "single", def);
3169
+ const resourceLabel = (def) => resolveResourceLabel(caps, canonicalResourceKey$1(def), "single", def);
3035
3170
  return jsx(CommandGroup, {
3036
3171
  heading: resolvedHeading,
3037
3172
  children: visibleResources.map((def) => {
3038
- const resource = canonicalResourceKey(def);
3173
+ const resource = canonicalResourceKey$1(def);
3039
3174
  const label = resourceLabel(def);
3040
3175
  const href = createHref?.(def) ?? resourceCreateHref(app.registry, def, resource);
3041
3176
  const value = `create:${resource}:${resolvedPrefix}:${label}`;
@@ -3097,7 +3232,7 @@ function AppActions() {
3097
3232
  variant: "outline",
3098
3233
  size: "sm",
3099
3234
  disabled: action.disabledReason !== void 0,
3100
- title: action.disabledReason,
3235
+ title: action.disabledReason === void 0 ? void 0 : reasonText(caps, action.disabledReason),
3101
3236
  "data-testid": `app-action:${action.name}`,
3102
3237
  children: action.disabledReason === void 0 ? jsx(RouterLink, {
3103
3238
  href,
@@ -3219,6 +3354,7 @@ function localizedSchema(t) {
3219
3354
  function LoginForm({ onSubmit, title, description, className }) {
3220
3355
  const tid = useTestIds();
3221
3356
  const t = useOptionalT();
3357
+ const caps = useI18nCaps();
3222
3358
  const resolvedTitle = title ?? t("auth.signIn", { defaultValue: "Sign in" });
3223
3359
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({
3224
3360
  resolver: zodResolver(localizedSchema(t)),
@@ -3233,9 +3369,11 @@ function LoginForm({ onSubmit, title, description, className }) {
3233
3369
  try {
3234
3370
  await onSubmit(values);
3235
3371
  } catch (e) {
3236
- const raw = e instanceof Error ? e.message : String(e);
3372
+ const dataCode = e !== null && typeof e === "object" && !(e instanceof Error) ? e.code : void 0;
3373
+ const raw = e instanceof Error ? e.message : technicalText(e) ?? (dataCode === void 0 ? String(e) : "");
3237
3374
  const friendly = toFriendlyError(raw, "", friendlyStringsFrom(t));
3238
- setSubmitError(friendly.replaced ? `${friendly.title}. ${friendly.detail}` : credentialsMessage(raw, t));
3375
+ const credentials = credentialsMessage(raw, t);
3376
+ setSubmitError(friendly.replaced ? `${friendly.title}. ${friendly.detail}` : typeof dataCode === "string" && credentials === raw ? resolveErrorText(e, caps) : credentials);
3239
3377
  }
3240
3378
  });
3241
3379
  return jsxs(Card, {
@@ -3360,15 +3498,139 @@ function AuthPage({ children, brand, footer, className }) {
3360
3498
  });
3361
3499
  }
3362
3500
  //#endregion
3501
+ //#region src/enum/enum-choice-group.tsx
3502
+ const NONE = "__none__";
3503
+ function isEmpty$1(value) {
3504
+ return value === null || value === void 0 || value === "";
3505
+ }
3506
+ function EnumChoiceGroup({ control, options, current, settling, value, onChange, testid, name, id, required, disabled, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, className }) {
3507
+ const t = useOptionalT();
3508
+ const generatedName = React.useId();
3509
+ const groupName = name ?? generatedName;
3510
+ const empty = isEmpty$1(value);
3511
+ const noneLabel = t("field.none", { defaultValue: "None" });
3512
+ const pick = (next) => {
3513
+ if (!disabled) onChange(next);
3514
+ };
3515
+ const ariaProps = {
3516
+ "aria-invalid": ariaInvalid || void 0,
3517
+ "aria-describedby": ariaDescribedBy,
3518
+ ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {}
3519
+ };
3520
+ if (control === "segmented") {
3521
+ const segmentLabel = (option) => jsxs(Fragment$1, { children: [option.icon ? jsx("span", {
3522
+ "data-testid": `${testid}:option:${option.value}:icon`,
3523
+ className: "contents",
3524
+ children: jsx(Icon, {
3525
+ name: option.icon,
3526
+ className: "size-3.5 shrink-0"
3527
+ })
3528
+ }) : null, option.label] });
3529
+ const segment = (key, label, active, select, currentItem = false) => jsx("button", {
3530
+ type: "button",
3531
+ "aria-pressed": active,
3532
+ "data-state": active ? "active" : "inactive",
3533
+ "data-testid": currentItem ? `${testid}:current` : `${testid}:option:${key}`,
3534
+ ...currentItem ? { "data-current": "" } : {},
3535
+ disabled: disabled || currentItem,
3536
+ onClick: select,
3537
+ 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"),
3538
+ children: label
3539
+ }, currentItem ? `current:${key}` : key);
3540
+ return jsxs("div", {
3541
+ role: "group",
3542
+ id,
3543
+ "aria-labelledby": ariaLabelledBy,
3544
+ "data-testid": testid,
3545
+ className: cn("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", className),
3546
+ ...ariaProps,
3547
+ children: [
3548
+ current === void 0 ? null : segment(current.value, segmentLabel(current), true, () => void 0, true),
3549
+ required || settling !== void 0 ? null : segment(NONE, noneLabel, empty, () => pick(void 0)),
3550
+ settling === void 0 ? null : jsx(EnumSettlingRow, {
3551
+ status: settling,
3552
+ testid
3553
+ }),
3554
+ options.map((option) => segment(option.value, segmentLabel(option), value === option.value, () => pick(option.value)))
3555
+ ]
3556
+ });
3557
+ }
3558
+ const cards = control === "cards";
3559
+ const choice = (key, option, label, checked, select, currentItem = false) => {
3560
+ const inputId = `${id ?? groupName}:${currentItem ? "current:" : ""}${key}`;
3561
+ const inert = disabled || currentItem;
3562
+ return jsxs("label", {
3563
+ htmlFor: inputId,
3564
+ 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"), inert && "cursor-not-allowed opacity-60"),
3565
+ children: [jsx("input", {
3566
+ type: "radio",
3567
+ id: inputId,
3568
+ name: groupName,
3569
+ value: key,
3570
+ checked,
3571
+ onChange: select,
3572
+ disabled: inert,
3573
+ ...ariaProps,
3574
+ "data-testid": currentItem ? `${testid}:current` : `${testid}:option:${key}`,
3575
+ ...currentItem ? { "data-current": "" } : {},
3576
+ className: cn("h-4 w-4 shrink-0 accent-primary", cards ? "mt-0.5" : "mt-1")
3577
+ }), jsxs("span", {
3578
+ className: "flex min-w-0 flex-col gap-0.5",
3579
+ children: [jsxs("span", {
3580
+ className: cn("flex items-center gap-1.5", cards && "font-medium"),
3581
+ children: [option?.icon ? jsx("span", {
3582
+ "data-testid": `${testid}:option:${key}:icon`,
3583
+ className: "contents",
3584
+ children: jsx(Icon, {
3585
+ name: option.icon,
3586
+ className: "size-4 shrink-0"
3587
+ })
3588
+ }) : null, jsx("span", { children: label })]
3589
+ }), option?.summary === void 0 || option.summary === "" ? null : jsx("span", {
3590
+ "data-testid": `${testid}:option:${key}:doc`,
3591
+ className: "text-sm text-muted-foreground",
3592
+ children: option.summary
3593
+ })]
3594
+ })]
3595
+ }, currentItem ? `current:${key}` : key);
3596
+ };
3597
+ return jsxs("div", {
3598
+ role: "radiogroup",
3599
+ id,
3600
+ "aria-labelledby": ariaLabelledBy,
3601
+ "data-testid": testid,
3602
+ "aria-required": required,
3603
+ className: cn("flex flex-col", cards ? "gap-2" : "gap-1.5", className),
3604
+ children: [
3605
+ current === void 0 ? null : choice(current.value, current, current.label, true, () => void 0, true),
3606
+ required || settling !== void 0 ? null : choice(NONE, void 0, noneLabel, empty, () => pick(void 0)),
3607
+ settling === void 0 ? null : jsx(EnumSettlingRow, {
3608
+ status: settling,
3609
+ testid
3610
+ }),
3611
+ options.map((option) => choice(option.value, option, option.label, value === option.value, () => pick(option.value)))
3612
+ ]
3613
+ });
3614
+ }
3615
+ //#endregion
3363
3616
  //#region src/enum/enum-picker.tsx
3364
3617
  const ENUM_PICKER_CLEAR_SENTINEL = "__buro_clear__";
3365
- function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id, testid, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, className, clearable = false, clearLabel = "All" }) {
3618
+ function withSummary(summary) {
3619
+ return summary === void 0 ? {} : { summary };
3620
+ }
3621
+ function EnumPicker({ enumName: enumInput, value, onChange, control = "select", name, id, testid, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, className, clearable = false, clearLabel = "All", use }) {
3622
+ const enumName = enumBindingKey(enumInput) ?? "";
3366
3623
  const app = useApp();
3367
3624
  const t = useOptionalT();
3368
3625
  const def = app.registry.enums.get(enumName);
3369
3626
  const caps = useI18nCaps();
3370
3627
  const ns = def?.i18nNamespace ?? enumName;
3371
3628
  const { values, isLoading, error } = useEnumValues(enumName);
3629
+ const available = useAvailableEnumValues({
3630
+ values: isLoading || values === void 0 ? "pending" : values,
3631
+ ...def === void 0 ? {} : { enum: def },
3632
+ ...use === void 0 ? {} : { use }
3633
+ });
3372
3634
  const tid = testid ?? `enum-picker:${enumName}`;
3373
3635
  if (!def) return jsxs("span", {
3374
3636
  "data-testid": `${tid}:unregistered`,
@@ -3393,63 +3655,56 @@ function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id,
3393
3655
  children: jsx(SelectValue, { placeholder: t("field.loading", { defaultValue: "Loading…" }) })
3394
3656
  }), jsx(SelectContent, {})]
3395
3657
  });
3396
- if (error || !values) return jsxs("span", {
3397
- "data-testid": `${tid}:error`,
3658
+ if (error || !values) return jsxs(LoadError, {
3659
+ surface: "picker",
3660
+ error,
3661
+ testId: `${tid}:error`,
3398
3662
  className: "text-muted-foreground",
3399
- title: error?.message,
3663
+ fallback: {
3664
+ key: "relation.failed",
3665
+ default: "Failed to load options."
3666
+ },
3400
3667
  children: [
3401
3668
  "(failed to load enum ‘",
3402
3669
  enumName,
3403
3670
  "’)"
3404
3671
  ]
3405
3672
  });
3406
- if (renderAs === "radio") return jsx("div", {
3407
- role: "radiogroup",
3408
- "data-testid": tid,
3409
- className: cn("flex flex-col gap-2", className),
3410
- "aria-required": required,
3411
- children: values.map((entry) => {
3412
- const inputId = `${id ?? tid}:${entry.value}`;
3413
- return jsxs("label", {
3414
- htmlFor: inputId,
3415
- className: "flex items-center gap-2",
3416
- children: [jsx("input", {
3417
- type: "radio",
3418
- id: inputId,
3419
- name: name ?? tid,
3420
- value: entry.value,
3421
- checked: value === entry.value,
3422
- onChange: (e) => onChange(e.target.value),
3423
- disabled,
3424
- "aria-invalid": ariaInvalid || void 0,
3425
- "aria-describedby": ariaDescribedBy,
3426
- ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3427
- "data-testid": `${tid}:option:${entry.value}`,
3428
- className: "h-4 w-4"
3429
- }), jsx("span", { children: resolveEnumLabel(caps, ns, entry.value, enumValueText(def, entry.value) ?? entry.label) })]
3430
- }, entry.value);
3431
- })
3432
- });
3433
- if (renderAs === "segmented") return jsx("div", {
3434
- role: "group",
3435
- "data-testid": tid,
3436
- className: cn("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", className),
3437
- "aria-invalid": ariaInvalid || void 0,
3438
- "aria-describedby": ariaDescribedBy,
3439
- ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3440
- children: values.map((entry) => {
3441
- const isActive = value === entry.value;
3442
- return jsx("button", {
3443
- type: "button",
3444
- "aria-pressed": isActive,
3445
- "data-state": isActive ? "active" : "inactive",
3446
- "data-testid": `${tid}:option:${entry.value}`,
3447
- disabled,
3448
- onClick: () => onChange(entry.value),
3449
- 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"),
3450
- children: resolveEnumLabel(caps, ns, entry.value, enumValueText(def, entry.value) ?? entry.label)
3451
- }, entry.value);
3452
- })
3673
+ const labelOf = (entry) => resolveEnumLabel(caps, ns, entry.value, enumValueText(def, entry.value) ?? entry.label);
3674
+ const summaryOf = (entry) => resolveDoc(caps, `enum/${ns}/${entry.value}`)?.summary || void 0;
3675
+ const settling = available.status === "ready" ? void 0 : available.status;
3676
+ const offered = settling === void 0 ? available.values : [];
3677
+ const known = value == null ? void 0 : values.find((entry) => entry.value === value);
3678
+ const current = value == null || value === "" || offered.some((entry) => entry.value === value) ? void 0 : known === void 0 ? {
3679
+ value,
3680
+ label: unknownEnumValueLabel(caps, def, ns, value).label
3681
+ } : {
3682
+ value,
3683
+ label: labelOf(known),
3684
+ ...known.icon === void 0 ? {} : { icon: known.icon }
3685
+ };
3686
+ if (control !== "select") return jsx(EnumChoiceGroup, {
3687
+ control,
3688
+ ...current === void 0 ? {} : { current },
3689
+ ...settling === void 0 ? {} : { settling },
3690
+ options: offered.map((entry) => ({
3691
+ value: entry.value,
3692
+ label: labelOf(entry),
3693
+ ...entry.icon === void 0 ? {} : { icon: entry.icon },
3694
+ ...withSummary(summaryOf(entry))
3695
+ })),
3696
+ value,
3697
+ onChange,
3698
+ testid: tid,
3699
+ name,
3700
+ id,
3701
+ required,
3702
+ disabled,
3703
+ ariaInvalid,
3704
+ ariaDescribedBy,
3705
+ ariaErrorMessage,
3706
+ ariaLabelledBy,
3707
+ className
3453
3708
  });
3454
3709
  const selectValue = value == null ? clearable ? ENUM_PICKER_CLEAR_SENTINEL : "" : value;
3455
3710
  const handleValueChange = (next) => {
@@ -3469,46 +3724,74 @@ function EnumPicker({ enumName, value, onChange, renderAs = "select", name, id,
3469
3724
  name,
3470
3725
  id,
3471
3726
  "aria-required": required,
3727
+ "aria-busy": settling === "pending" || void 0,
3472
3728
  "aria-invalid": ariaInvalid || void 0,
3473
3729
  "aria-describedby": ariaDescribedBy,
3474
3730
  ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3475
3731
  className,
3476
3732
  children: jsx(SelectValue, { placeholder: placeholder ?? t("field.select", { defaultValue: "Select…" }) })
3477
- }), jsxs(SelectContent, { children: [clearable && jsx(SelectItem, {
3478
- value: "__buro_clear__",
3479
- "data-testid": `${tid}:option:__clear__`,
3480
- children: clearLabel
3481
- }), values.map((entry) => {
3482
- const optionLabel = resolveEnumLabel(caps, ns, entry.value, enumValueText(def, entry.value) ?? entry.label);
3483
- const optionDoc = resolveDoc(caps, `enum/${ns}/${entry.value}`)?.summary;
3484
- return jsx(SelectItem, {
3485
- value: entry.value,
3486
- "data-testid": `${tid}:option:${entry.value}`,
3487
- children: optionDoc === void 0 ? optionLabel : jsxs("span", {
3488
- className: "flex flex-col gap-0.5",
3489
- children: [jsx("span", { children: optionLabel }), jsx("span", {
3490
- className: "text-xs leading-snug text-muted-foreground",
3733
+ }), jsxs(SelectContent, { children: [
3734
+ clearable && jsx(SelectItem, {
3735
+ value: "__buro_clear__",
3736
+ "data-testid": `${tid}:option:__clear__`,
3737
+ children: clearLabel
3738
+ }),
3739
+ current === void 0 ? null : jsx(SelectItem, {
3740
+ value: current.value,
3741
+ disabled: true,
3742
+ "data-current": "",
3743
+ "data-testid": `${tid}:current`,
3744
+ children: current.label
3745
+ }),
3746
+ settling === void 0 ? null : jsx(EnumSettlingRow, {
3747
+ status: settling,
3748
+ testid: tid
3749
+ }),
3750
+ offered.map((entry) => {
3751
+ const optionLabel = labelOf(entry);
3752
+ const optionDoc = summaryOf(entry);
3753
+ return jsx(SelectItem, {
3754
+ value: entry.value,
3755
+ "data-testid": `${tid}:option:${entry.value}`,
3756
+ description: optionDoc === void 0 ? void 0 : jsx("span", {
3491
3757
  "data-testid": `${tid}:option:${entry.value}:doc`,
3492
3758
  children: optionDoc
3493
- })]
3494
- })
3495
- }, entry.value);
3496
- })] })]
3759
+ }),
3760
+ children: optionLabel
3761
+ }, entry.value);
3762
+ })
3763
+ ] })]
3497
3764
  });
3498
3765
  }
3499
3766
  //#endregion
3767
+ //#region src/enum/resolve-enum-control.ts
3768
+ const warned = new Set();
3769
+ function resolveEnumControl(fieldControl, enumDef, where) {
3770
+ if (isEnumControl(fieldControl)) return fieldControl;
3771
+ if (fieldControl !== void 0) {
3772
+ const shown = typeof fieldControl === "string" ? `'${fieldControl}'` : String(fieldControl);
3773
+ const key = `${where}\u0000${shown}`;
3774
+ if (!warned.has(key)) {
3775
+ warned.add(key);
3776
+ 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'.`);
3777
+ }
3778
+ }
3779
+ return enumDef?.control ?? "select";
3780
+ }
3781
+ //#endregion
3500
3782
  //#region src/forms/issue-severity.ts
3501
3783
  function isErrorIssue(issue) {
3502
3784
  return issue.severity === "error";
3503
3785
  }
3504
3786
  //#endregion
3505
3787
  //#region src/fields/boolean.tsx
3506
- function BooleanDisplay({ value }) {
3788
+ function BooleanDisplay({ value, text }) {
3507
3789
  const t = useOptionalT();
3508
3790
  if (value == null) return jsx("span", {
3509
3791
  className: "text-muted-foreground",
3510
3792
  children: "—"
3511
3793
  });
3794
+ if (text !== void 0) return jsx("span", { children: text });
3512
3795
  return jsx("span", { children: value ? t("boolean.yes", { defaultValue: "Yes" }) : t("boolean.no", { defaultValue: "No" }) });
3513
3796
  }
3514
3797
  function BooleanInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
@@ -3527,34 +3810,113 @@ function BooleanInput({ name, id, value, onChange, testid, disabled, required, a
3527
3810
  }
3528
3811
  //#endregion
3529
3812
  //#region src/fields/date.tsx
3530
- function DateDisplay({ value }) {
3813
+ function DateDisplay(props) {
3814
+ return jsx(TemporalDisplay, {
3815
+ kind: "date",
3816
+ ...props
3817
+ });
3818
+ }
3819
+ function DateTimeDisplay(props) {
3820
+ return jsx(TemporalDisplay, {
3821
+ kind: "dateTime",
3822
+ ...props
3823
+ });
3824
+ }
3825
+ function TimeDisplay(props) {
3826
+ return jsx(TemporalDisplay, {
3827
+ kind: "time",
3828
+ ...props
3829
+ });
3830
+ }
3831
+ function storedText(value) {
3832
+ return value instanceof Date && !Number.isNaN(value.getTime()) ? value.toISOString() : String(value);
3833
+ }
3834
+ function TemporalDisplay({ kind, value, text, field, record }) {
3531
3835
  const locale = useOptionalLocale();
3836
+ const zone = useFieldTimeZone(field?.zone, record);
3837
+ const viewer = useTimeZone();
3532
3838
  if (value == null || value === "") return jsx("span", {
3533
3839
  className: "text-muted-foreground",
3534
3840
  children: "—"
3535
3841
  });
3536
- const d = new Date(String(value));
3537
- if (Number.isNaN(d.getTime())) return jsx("span", { children: String(value) });
3842
+ if (zone.status === "pending" || viewer.status === "pending") return jsx(Skeleton, {
3843
+ className: "h-4 w-24",
3844
+ "aria-busy": "true"
3845
+ });
3846
+ const raw = storedText(value);
3847
+ const formatted = formatTimeValue(kind, raw, {
3848
+ zone: zone.zone,
3849
+ viewerZone: viewer.zone,
3850
+ locale: localeTag(locale),
3851
+ params: {
3852
+ ...kind === "time" && field?.ui?.seconds === true ? { timeStyle: "medium" } : {},
3853
+ ...dateDisplayParams(field?.ui)
3854
+ }
3855
+ });
3856
+ if (text !== void 0) return jsx("span", {
3857
+ title: formatted,
3858
+ children: text
3859
+ });
3860
+ if (formatted === void 0) return jsx("span", { children: raw });
3538
3861
  return jsx("span", {
3539
3862
  className: "whitespace-nowrap",
3540
- children: formatDate(d, locale)
3863
+ children: formatted
3541
3864
  });
3542
3865
  }
3543
3866
  function toIsoDate(v) {
3544
3867
  if (v == null || v === "") return "";
3545
3868
  const s = String(v);
3546
- if (/^\d{4}-\d{2}-\d{2}/.test(s)) return s.slice(0, 10);
3547
- const d = new Date(s);
3548
- if (Number.isNaN(d.getTime())) return "";
3549
- return d.toISOString().slice(0, 10);
3869
+ return isCalendarDate(s) ? s : "";
3550
3870
  }
3551
- function DateInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
3552
- return jsx(Input, {
3871
+ function DateInput(props) {
3872
+ return jsx(TemporalInput, {
3873
+ ...props,
3874
+ type: "date",
3875
+ toText: toIsoDate,
3876
+ fromText: (text) => text || null
3877
+ });
3878
+ }
3879
+ function DateTimeInput(props) {
3880
+ const locale = useOptionalLocale();
3881
+ const zone = useFieldTimeZone(props.field?.zone, props.values === void 0 ? props.record : {
3882
+ ...props.record,
3883
+ ...props.values
3884
+ });
3885
+ const viewer = useTimeZone();
3886
+ const pending = zone.status === "pending";
3887
+ const fieldZone = zone.status === "ready" ? zone.zone : void 0;
3888
+ const named = fieldZone !== void 0 && viewer.status === "ready" && fieldZone !== viewer.zone ? zoneShortName(canonicalInstant(String(props.value ?? "")) ?? epochMsToInstant(Date.now()), fieldZone, localeTag(locale)) : void 0;
3889
+ return jsx(TemporalInput, {
3890
+ ...props,
3891
+ type: "datetime-local",
3892
+ disabled: props.disabled || pending,
3893
+ toText: (value) => fieldZone === void 0 ? "" : toLocalDateTimeText(value, fieldZone),
3894
+ fromText: (text) => fieldZone === void 0 ? null : fromLocalDateTimeText(text, fieldZone) ?? null,
3895
+ ...named === void 0 ? {} : { zoneName: named }
3896
+ });
3897
+ }
3898
+ function TimeInput(props) {
3899
+ const seconds = props.field?.ui?.seconds === true;
3900
+ return jsx(TemporalInput, {
3901
+ ...props,
3902
+ type: "time",
3903
+ step: seconds ? 1 : 60,
3904
+ toText: (value) => value == null ? "" : String(value),
3905
+ fromText: (text) => {
3906
+ if (text === "") return null;
3907
+ if (seconds) return text.length === 5 ? `${text}:00` : text;
3908
+ return text.slice(0, 5);
3909
+ }
3910
+ });
3911
+ }
3912
+ function TemporalInput({ type, step, toText, fromText, name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, zoneName }) {
3913
+ const input = jsx(Input, {
3553
3914
  name,
3554
3915
  id,
3555
- type: "date",
3556
- value: toIsoDate(value),
3557
- onChange: (e) => onChange(e.target.value || null),
3916
+ type,
3917
+ ...step === void 0 ? {} : { step },
3918
+ value: toText(value),
3919
+ onChange: (e) => onChange(fromText(e.target.value)),
3558
3920
  "data-testid": testid,
3559
3921
  disabled,
3560
3922
  required,
@@ -3563,25 +3925,18 @@ function DateInput({ name, id, value, onChange, testid, disabled, required, aria
3563
3925
  "aria-describedby": ariaDescribedBy,
3564
3926
  ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {}
3565
3927
  });
3928
+ if (zoneName === void 0) return input;
3929
+ return jsxs("div", {
3930
+ className: "flex items-center gap-2",
3931
+ children: [input, jsx("span", {
3932
+ className: "whitespace-nowrap text-xs text-muted-foreground",
3933
+ "data-testid": testid === void 0 ? void 0 : `${testid}:zone`,
3934
+ children: zoneName
3935
+ })]
3936
+ });
3566
3937
  }
3567
3938
  //#endregion
3568
3939
  //#region src/fields/enum.tsx
3569
- function optionLabel(caps, field, value, inline) {
3570
- const ns = enumNamespaceOf(field);
3571
- if (ns) return resolveEnumLabel(caps, ns, value, inline);
3572
- return field ? resolveFieldOptionLabel(caps, field.name, value, inline) : inline;
3573
- }
3574
- function declaredOptionLabel(option, value, enumDef) {
3575
- return (enumDef === void 0 ? void 0 : enumValueText(enumDef, value)) ?? (typeof option?.label === "string" ? option.label : value);
3576
- }
3577
- function useBoundEnum(field) {
3578
- const app = useOptionalApp();
3579
- const namespace = enumNamespaceOf(field);
3580
- return namespace === void 0 ? void 0 : app?.registry.enums.get(namespace);
3581
- }
3582
- function useOptionCaps(resource) {
3583
- return useI18nCaps(useContext(ResourceContext).resource ?? resource);
3584
- }
3585
3940
  function EnumDisplay({ value, options, field, resource }) {
3586
3941
  const caps = useOptionCaps(resource);
3587
3942
  const enumDef = useBoundEnum(field);
@@ -3590,7 +3945,7 @@ function EnumDisplay({ value, options, field, resource }) {
3590
3945
  children: "—"
3591
3946
  });
3592
3947
  const opt = options?.find((o) => o.value === String(value));
3593
- const label = optionLabel(caps, field, String(value), declaredOptionLabel(opt, String(value), enumDef));
3948
+ const label = resolveOptionLabel(caps, field, String(value), opt, enumDef);
3594
3949
  return jsxs("span", {
3595
3950
  className: "inline-flex items-center gap-1",
3596
3951
  children: [opt?.icon ? jsx(Icon, {
@@ -3599,10 +3954,62 @@ function EnumDisplay({ value, options, field, resource }) {
3599
3954
  }) : null, label]
3600
3955
  });
3601
3956
  }
3602
- function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, resource }) {
3957
+ function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, ariaLabelledBy, resource, record, values, metaSources, baseline }) {
3603
3958
  const t = useOptionalT();
3604
3959
  const caps = useOptionCaps(resource);
3605
3960
  const enumDef = useBoundEnum(field);
3961
+ const ns = enumNamespaceOf(field);
3962
+ const control = resolveEnumControl(field?.ui?.control, enumDef, `field ${field?.name ?? name}`);
3963
+ const sources = useFieldMetaSources({
3964
+ record,
3965
+ values,
3966
+ extra: metaSources
3967
+ });
3968
+ const available = useAvailableEnumValues({
3969
+ values: options ?? [],
3970
+ use: {
3971
+ narrow: narrowEntriesOf(field?.meta?.narrow),
3972
+ sources,
3973
+ mode: record === void 0 ? "create" : "edit",
3974
+ baseline: baseline ?? record
3975
+ }
3976
+ });
3977
+ const settling = available.status === "ready" ? void 0 : available.status;
3978
+ const offered = settling === void 0 ? available.values : [];
3979
+ const tid = testid ?? name;
3980
+ const resolved = offered.map((o) => {
3981
+ const summary = ns === void 0 ? void 0 : resolveDoc(caps, `enum/${ns}/${o.value}`)?.summary;
3982
+ return {
3983
+ value: o.value,
3984
+ label: resolveOptionLabel(caps, field, o.value, o, enumDef),
3985
+ ...o.icon === void 0 ? {} : { icon: o.icon },
3986
+ ...summary === void 0 ? {} : { summary }
3987
+ };
3988
+ });
3989
+ const raw = value == null || value === "" ? void 0 : String(value);
3990
+ const known = raw === void 0 ? void 0 : options?.find((o) => o.value === raw);
3991
+ const current = raw === void 0 || resolved.some((o) => o.value === raw) ? void 0 : {
3992
+ value: raw,
3993
+ label: resolveOptionLabel(caps, field, raw, known, enumDef),
3994
+ ...known?.icon === void 0 ? {} : { icon: known.icon }
3995
+ };
3996
+ if (control !== "select") return jsx(EnumChoiceGroup, {
3997
+ control,
3998
+ options: resolved,
3999
+ ...current === void 0 ? {} : { current },
4000
+ ...settling === void 0 ? {} : { settling },
4001
+ value,
4002
+ onChange,
4003
+ testid: tid,
4004
+ name,
4005
+ id,
4006
+ required,
4007
+ disabled,
4008
+ ariaInvalid,
4009
+ ariaDescribedBy,
4010
+ ariaErrorMessage,
4011
+ ariaLabelledBy
4012
+ });
3606
4013
  return jsxs(Select, {
3607
4014
  value: value ?? "",
3608
4015
  onValueChange: onChange,
@@ -3613,20 +4020,39 @@ function EnumInput({ name, id, value, onChange, testid, options, field, placehol
3613
4020
  name,
3614
4021
  id,
3615
4022
  "aria-required": required,
4023
+ "aria-busy": settling === "pending" || void 0,
3616
4024
  "aria-invalid": ariaInvalid || void 0,
3617
4025
  "aria-describedby": ariaDescribedBy,
3618
4026
  ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
3619
4027
  children: jsx(SelectValue, { placeholder: placeholder ?? t("field.select", { defaultValue: "Select…" }) })
3620
- }), jsx(SelectContent, { children: options?.map((o) => jsx(SelectItem, {
3621
- value: o.value,
3622
- children: jsxs("span", {
3623
- className: "inline-flex items-center gap-1.5",
3624
- children: [o.icon ? jsx(Icon, {
3625
- name: o.icon,
3626
- className: "size-3.5 shrink-0 text-muted-foreground"
3627
- }) : null, optionLabel(caps, field, o.value, declaredOptionLabel(o, o.value, enumDef))]
3628
- })
3629
- }, o.value)) })]
4028
+ }), jsxs(SelectContent, { children: [
4029
+ current === void 0 ? null : jsx(SelectItem, {
4030
+ value: current.value,
4031
+ disabled: true,
4032
+ "data-current": "",
4033
+ "data-testid": `${tid}:current`,
4034
+ children: current.label
4035
+ }),
4036
+ settling === void 0 ? null : jsx(EnumSettlingRow, {
4037
+ status: settling,
4038
+ testid: tid
4039
+ }),
4040
+ resolved.map((o) => jsx(SelectItem, {
4041
+ value: o.value,
4042
+ "data-testid": testid === void 0 ? void 0 : `${testid}:option:${o.value}`,
4043
+ description: o.summary === void 0 ? void 0 : jsx("span", {
4044
+ "data-testid": testid === void 0 ? void 0 : `${testid}:option:${o.value}:doc`,
4045
+ children: o.summary
4046
+ }),
4047
+ children: jsxs("span", {
4048
+ className: "inline-flex items-center gap-1.5",
4049
+ children: [o.icon ? jsx(Icon, {
4050
+ name: o.icon,
4051
+ className: "size-3.5 shrink-0 text-muted-foreground"
4052
+ }) : null, o.label]
4053
+ })
4054
+ }, o.value))
4055
+ ] })]
3630
4056
  });
3631
4057
  }
3632
4058
  //#endregion
@@ -4047,7 +4473,7 @@ function JsonInput({ name, id, value, onChange, testid, disabled, required, aria
4047
4473
  }
4048
4474
  //#endregion
4049
4475
  //#region src/fields/lazy-relation-many.tsx
4050
- const RelationManyPickerInput = lazy(() => import("./relation-many-picker-bvkjkv-k.js").then((module) => ({ default: module.RelationManyPickerInput })));
4476
+ const RelationManyPickerInput = lazy(() => import("./relation-many-picker-BlrGRwMV.js").then((module) => ({ default: module.RelationManyPickerInput })));
4051
4477
  function LazyRelationManyInput(props) {
4052
4478
  return jsx(Suspense, {
4053
4479
  fallback: jsx(Skeleton, {
@@ -4060,8 +4486,8 @@ function LazyRelationManyInput(props) {
4060
4486
  }
4061
4487
  //#endregion
4062
4488
  //#region src/fields/lazy-rich-text.tsx
4063
- const RichTextDisplay = lazy(() => import("./rich-text-DFerywgz.js").then((module) => ({ default: module.RichTextDisplay })));
4064
- const RichTextInput = lazy(() => import("./rich-text-DFerywgz.js").then((module) => ({ default: module.RichTextInput })));
4489
+ const RichTextDisplay = lazy(() => import("./rich-text-RcPBf6hr.js").then((module) => ({ default: module.RichTextDisplay })));
4490
+ const RichTextInput = lazy(() => import("./rich-text-RcPBf6hr.js").then((module) => ({ default: module.RichTextInput })));
4065
4491
  function RichTextDisplayLoading() {
4066
4492
  const t = useOptionalT();
4067
4493
  return jsx("div", {
@@ -4118,12 +4544,13 @@ function isIdentifier(field) {
4118
4544
  const n = field?.name;
4119
4545
  return n === "id" || typeof n === "string" && n.endsWith("_id");
4120
4546
  }
4121
- function NumberDisplay({ value, field }) {
4547
+ function NumberDisplay({ value, field, text }) {
4122
4548
  const locale = useOptionalLocale();
4123
4549
  if (value == null || value === "") return jsx("span", {
4124
4550
  className: "text-muted-foreground",
4125
4551
  children: "—"
4126
4552
  });
4553
+ if (text !== void 0) return jsx("span", { children: text });
4127
4554
  let raw = value;
4128
4555
  if (typeof raw === "string") raw = unquote(raw);
4129
4556
  const n = typeof raw === "number" ? raw : Number(raw);
@@ -4158,12 +4585,149 @@ function NumberInput({ name, id, value, onChange, testid, placeholder, disabled,
4158
4585
  });
4159
4586
  }
4160
4587
  //#endregion
4588
+ //#region src/fields/object-group.tsx
4589
+ const GROUP = "rounded-md border border-input p-3";
4590
+ function ObjectGroupInput(props) {
4591
+ const { testIds, accessibility, children } = props;
4592
+ return jsx("div", {
4593
+ role: "group",
4594
+ "data-buro-object-group": "input",
4595
+ className: cn(GROUP, "space-y-3"),
4596
+ "data-testid": testIds.input,
4597
+ id: accessibility.input,
4598
+ "aria-labelledby": accessibility.label,
4599
+ "aria-describedby": accessibility.description,
4600
+ "aria-invalid": props.issues.some((issue) => issue.severity === "error" && issue.path.length === 0) || void 0,
4601
+ "aria-errormessage": accessibility.error,
4602
+ children
4603
+ });
4604
+ }
4605
+ function ObjectGroupDisplay(props) {
4606
+ const { testIds, children } = props;
4607
+ return jsx("div", {
4608
+ role: "group",
4609
+ "data-buro-object-group": "display",
4610
+ className: cn(GROUP, "space-y-2"),
4611
+ "data-testid": testIds.display,
4612
+ children
4613
+ });
4614
+ }
4615
+ //#endregion
4616
+ //#region src/fields/repeater.tsx
4617
+ function useRepeaterTexts() {
4618
+ const t = useChromeT();
4619
+ return {
4620
+ add: t("field.repeater.add", { defaultValue: "Add" }),
4621
+ empty: t("field.repeater.empty", { defaultValue: "No items yet" }),
4622
+ remove: (position) => t("field.repeater.remove", {
4623
+ params: { position },
4624
+ defaultValue: "Remove item {{position}}"
4625
+ }),
4626
+ moveUp: (position) => t("field.repeater.moveUp", {
4627
+ params: { position },
4628
+ defaultValue: "Move item {{position}} up"
4629
+ }),
4630
+ moveDown: (position) => t("field.repeater.moveDown", {
4631
+ params: { position },
4632
+ defaultValue: "Move item {{position}} down"
4633
+ })
4634
+ };
4635
+ }
4636
+ function RepeaterInput(props) {
4637
+ const texts = useRepeaterTexts();
4638
+ const { value, renderElement, elementKeys, onAdd, onRemove, onMove, testIds, accessibility } = props;
4639
+ const root = testIds.input;
4640
+ const at = (suffix) => root === void 0 ? void 0 : `${root}:${suffix}`;
4641
+ const editable = props.disabled !== true && props.readOnly !== true;
4642
+ const canAdd = editable && onAdd !== void 0 && props.canAdd !== false;
4643
+ const canRemove = editable && onRemove !== void 0 && props.canRemove !== false;
4644
+ const last = value.length - 1;
4645
+ return jsxs("div", {
4646
+ role: "group",
4647
+ className: "space-y-2",
4648
+ "data-testid": root,
4649
+ id: accessibility.input,
4650
+ "aria-labelledby": accessibility.label,
4651
+ "aria-describedby": accessibility.description,
4652
+ "aria-errormessage": accessibility.error,
4653
+ children: [value.length === 0 ? jsx("p", {
4654
+ className: "text-sm text-muted-foreground",
4655
+ "data-testid": at("empty"),
4656
+ children: texts.empty
4657
+ }) : jsx("ol", {
4658
+ className: "space-y-2",
4659
+ children: value.map((_, index) => jsxs("li", {
4660
+ className: "flex items-start gap-2 rounded-md border border-input p-3",
4661
+ "data-testid": at(`item:${index}`),
4662
+ children: [jsx("div", {
4663
+ className: "min-w-0 flex-1",
4664
+ children: renderElement?.(index)
4665
+ }), onMove === void 0 && onRemove === void 0 ? null : jsxs("div", {
4666
+ className: "flex shrink-0 gap-1",
4667
+ children: [onMove === void 0 ? null : jsxs(Fragment$1, { children: [jsx(Button, {
4668
+ type: "button",
4669
+ variant: "ghost",
4670
+ size: "icon",
4671
+ "aria-label": texts.moveUp(index + 1),
4672
+ disabled: !editable || index === 0,
4673
+ "data-testid": at(`up:${index}`),
4674
+ onClick: () => onMove(index, index - 1),
4675
+ children: jsx(IconArrowUp, { "aria-hidden": "true" })
4676
+ }), jsx(Button, {
4677
+ type: "button",
4678
+ variant: "ghost",
4679
+ size: "icon",
4680
+ "aria-label": texts.moveDown(index + 1),
4681
+ disabled: !editable || index === last,
4682
+ "data-testid": at(`down:${index}`),
4683
+ onClick: () => onMove(index, index + 1),
4684
+ children: jsx(IconArrowDown, { "aria-hidden": "true" })
4685
+ })] }), onRemove === void 0 ? null : jsx(Button, {
4686
+ type: "button",
4687
+ variant: "ghost",
4688
+ size: "icon",
4689
+ "aria-label": texts.remove(index + 1),
4690
+ disabled: !canRemove,
4691
+ "data-testid": at(`remove:${index}`),
4692
+ onClick: () => onRemove(index),
4693
+ children: jsx(IconTrash, { "aria-hidden": "true" })
4694
+ })]
4695
+ })]
4696
+ }, elementKeys?.[index] ?? index))
4697
+ }), onAdd === void 0 ? null : jsxs(Button, {
4698
+ type: "button",
4699
+ variant: "outline",
4700
+ size: "sm",
4701
+ disabled: !canAdd,
4702
+ "data-testid": at("add"),
4703
+ onClick: onAdd,
4704
+ children: [jsx(IconPlus, { "aria-hidden": "true" }), texts.add]
4705
+ })]
4706
+ });
4707
+ }
4708
+ function RepeaterDisplay(props) {
4709
+ const texts = useRepeaterTexts();
4710
+ const { value, renderElement, testIds } = props;
4711
+ if (value.length === 0) return jsx("span", {
4712
+ className: "text-muted-foreground",
4713
+ "data-testid": testIds.display,
4714
+ children: texts.empty
4715
+ });
4716
+ return jsx("ol", {
4717
+ className: "space-y-2",
4718
+ "data-testid": testIds.display,
4719
+ children: value.map((_, index) => jsx("li", {
4720
+ className: "rounded-md border border-input p-3",
4721
+ children: renderElement?.(index)
4722
+ }, index))
4723
+ });
4724
+ }
4725
+ //#endregion
4161
4726
  //#region src/fields/link-keys-display.tsx
4162
4727
  const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4163
- function qualify(name, owner) {
4728
+ function qualify(name) {
4164
4729
  try {
4165
- const namespace = normalizeEntityRef(owner).namespace;
4166
- const ref = normalizeEntityRef(name, { defaultNamespace: namespace });
4730
+ const ref = normalizeEntityRef(name);
4167
4731
  return ref.namespace === "default" ? ref.id : `${ref.namespace}:${ref.id}`;
4168
4732
  } catch {
4169
4733
  return;
@@ -4188,7 +4752,7 @@ function LinkKeysDisplay(props) {
4188
4752
  const ownerDef = owner === "" ? void 0 : app.registry.get(owner);
4189
4753
  const declaration = ownerDef === void 0 ? void 0 : declaredField(ownerDef, name);
4190
4754
  const link = isRecord$1(declaration) ? linkOf(declaration) : void 0;
4191
- const junctionName = link === void 0 ? void 0 : qualify(link.junction, owner);
4755
+ const junctionName = link === void 0 ? void 0 : qualify(link.junction);
4192
4756
  const junctionDef = junctionName === void 0 ? void 0 : app.registry.get(junctionName);
4193
4757
  const junctionField = (key) => junctionDef === void 0 ? void 0 : declaredField(junctionDef, key);
4194
4758
  const ownerField = link === void 0 ? idFieldOf(ownerDef) : byOf(junctionField(link.sourceKey)) ?? idFieldOf(ownerDef);
@@ -4196,11 +4760,11 @@ function LinkKeysDisplay(props) {
4196
4760
  const ownerKey = record?.[ownerField];
4197
4761
  const { keys, complete, isLoading, error } = useLinkKeys(owner, name, ownerKey);
4198
4762
  const [missed, setMissed] = useState(() => new Set());
4199
- const onMiss = useCallback((key, miss) => {
4763
+ const onMiss = useCallback((key, isMiss) => {
4200
4764
  setMissed((current) => {
4201
- if (current.has(key) === miss) return current;
4765
+ if (current.has(key) === isMiss) return current;
4202
4766
  const next = new Set(current);
4203
- if (miss) next.add(key);
4767
+ if (isMiss) next.add(key);
4204
4768
  else next.delete(key);
4205
4769
  return next;
4206
4770
  });
@@ -4220,12 +4784,17 @@ function LinkKeysDisplay(props) {
4220
4784
  targetBy
4221
4785
  ]);
4222
4786
  const chipsId = tid.linkChips(owner, name);
4223
- if (error) return jsx("span", {
4224
- "data-testid": tid.linkError(owner, name),
4225
- role: "status",
4787
+ if (error) return jsx(LoadError, {
4788
+ surface: "relation",
4789
+ error,
4790
+ ...typeof target === "string" ? { resource: target } : {},
4791
+ testId: tid.linkError(owner, name),
4226
4792
  className: "text-destructive text-xs",
4227
- title: error.message,
4228
- children: t("fields.link.failed", { defaultValue: "Links could not be loaded" })
4793
+ attributes: { role: "status" },
4794
+ fallback: {
4795
+ key: "field.link.failed",
4796
+ default: "Links could not be loaded"
4797
+ }
4229
4798
  });
4230
4799
  if (keys === void 0) {
4231
4800
  if (!isLoading) return jsx("span", {
@@ -4245,7 +4814,7 @@ function LinkKeysDisplay(props) {
4245
4814
  children: jsx("span", {
4246
4815
  "data-testid": tid.linkIncomplete(owner, name),
4247
4816
  className: "text-muted-foreground",
4248
- children: t("fields.link.incomplete", {
4817
+ children: t("field.link.incomplete", {
4249
4818
  count: keys.length,
4250
4819
  defaultValue: "More than {{count}} links"
4251
4820
  })
@@ -4267,7 +4836,7 @@ function LinkKeysDisplay(props) {
4267
4836
  }, String(key))), hidden > 0 ? jsx("span", {
4268
4837
  "data-testid": tid.linkHidden(owner, name),
4269
4838
  className: `${chipClassName} text-muted-foreground`,
4270
- children: t("fields.link.hidden", {
4839
+ children: t("field.link.hidden", {
4271
4840
  count: hidden,
4272
4841
  defaultValue: "{{count}} not visible"
4273
4842
  })
@@ -4355,7 +4924,7 @@ const relationMetaSchema = Object.freeze({ "~standard": Object.freeze({
4355
4924
  path: []
4356
4925
  }] };
4357
4926
  const issues = [];
4358
- const { to, by, resolve, searchField, candidates } = value;
4927
+ const { to, by, resolve, searchField, candidates, scope } = value;
4359
4928
  if (to !== void 0 && !(typeof to === "string" && to.length > 0) && !(isRecord(to) && (typeof to.id === "string" || isRecord(to.ref)))) issues.push({
4360
4929
  message: "must be a resource name or reference",
4361
4930
  path: ["to"]
@@ -4376,16 +4945,21 @@ const relationMetaSchema = Object.freeze({ "~standard": Object.freeze({
4376
4945
  message: "must be an array of conditions",
4377
4946
  path: ["candidates"]
4378
4947
  });
4948
+ if (scope !== void 0 && scope !== "context" && scope !== "session" && scope !== "none") issues.push({
4949
+ message: "must be 'context', 'session' or 'none'",
4950
+ path: ["scope"]
4951
+ });
4379
4952
  return issues.length > 0 ? { issues } : { value };
4380
4953
  }
4381
4954
  }) });
4382
4955
  //#endregion
4383
4956
  //#region src/fields/text.tsx
4384
- function TextDisplay({ value }) {
4957
+ function TextDisplay({ value, text: formatted }) {
4385
4958
  if (value == null || value === "") return jsx("span", {
4386
4959
  className: "text-muted-foreground",
4387
4960
  children: "—"
4388
4961
  });
4962
+ if (formatted !== void 0) return jsx("span", { children: formatted });
4389
4963
  const { text, isStructured } = toDisplayText(value);
4390
4964
  return isStructured ? jsx("span", {
4391
4965
  className: "font-mono text-xs break-all",
@@ -4414,23 +4988,26 @@ function TextInput({ name, id, value, onChange, testid, placeholder, disabled, r
4414
4988
  }
4415
4989
  //#endregion
4416
4990
  //#region src/fields/textarea.tsx
4417
- function TextareaDisplay({ value }) {
4991
+ function TextareaDisplay({ value, text: formatted }) {
4418
4992
  if (value == null || value === "") return jsx("span", {
4419
4993
  className: "text-muted-foreground",
4420
4994
  children: "—"
4421
4995
  });
4422
- const { text, isStructured } = toDisplayText(value);
4996
+ const { text, isStructured } = formatted === void 0 ? toDisplayText(value) : {
4997
+ text: formatted,
4998
+ isStructured: false
4999
+ };
4423
5000
  return jsx("pre", {
4424
5001
  className: isStructured ? "whitespace-pre-wrap break-all font-mono text-xs" : "whitespace-pre-wrap font-sans text-sm",
4425
5002
  children: text
4426
5003
  });
4427
5004
  }
4428
- function CodeDisplay({ value, presentation, testid }) {
5005
+ function CodeDisplay({ value, text: formatted, presentation, testid }) {
4429
5006
  if (value == null || value === "") return jsx("span", {
4430
5007
  className: "text-muted-foreground",
4431
5008
  children: "—"
4432
5009
  });
4433
- const { text } = toDisplayText(value);
5010
+ const text = formatted ?? toDisplayText(value).text;
4434
5011
  return jsx("pre", {
4435
5012
  "data-testid": testid,
4436
5013
  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",
@@ -4474,14 +5051,14 @@ function TextareaInput({ name, id, value, onChange, testid, placeholder, disable
4474
5051
  //#endregion
4475
5052
  //#region src/fields/default-renderers.ts
4476
5053
  function legacyField(props) {
4477
- const name = typeof props.path.at(-1) === "string" ? props.path.at(-1) : "field";
4478
- const texts = props.field.texts;
5054
+ const name = optionFieldName(props.path);
5055
+ const { texts, zone } = props.field;
4479
5056
  return Object.freeze({
4480
5057
  name,
4481
5058
  meta: props.meta,
4482
5059
  ui: props.ui,
4483
- codec: props.field.codec,
4484
- ...texts === void 0 ? {} : { texts }
5060
+ ...texts === void 0 ? {} : { texts },
5061
+ ...zone === void 0 ? {} : { zone }
4485
5062
  });
4486
5063
  }
4487
5064
  function legacyOptions(options, field) {
@@ -4502,6 +5079,7 @@ function display(renderer) {
4502
5079
  return function ShadcnDisplay(props) {
4503
5080
  return createElement(renderer, {
4504
5081
  value: props.value,
5082
+ ...props.text === void 0 ? {} : { text: props.text },
4505
5083
  multiple: props.multiple,
4506
5084
  field: legacyField(props),
4507
5085
  options: legacyOptions(props.options, props.field),
@@ -4539,12 +5117,17 @@ function input(renderer) {
4539
5117
  ariaInvalid: props.issues.some(isErrorIssue),
4540
5118
  ariaDescribedBy: props.accessibility.description,
4541
5119
  ariaErrorMessage: props.accessibility.error,
5120
+ ariaLabelledBy: props.accessibility.label,
4542
5121
  field: legacyField(props),
4543
5122
  resource: props.resource.id,
4544
5123
  record: props.record,
5124
+ values: props.form,
4545
5125
  relationSeeds: props.relationSeeds,
4546
5126
  fixedFilters: props.fixedFilters,
4547
- metaSources: props.metaSources
5127
+ optionSubtitle: props.optionSubtitle,
5128
+ metaSources: props.metaSources,
5129
+ ...props.baseline === void 0 ? {} : { baseline: props.baseline },
5130
+ link: props.link
4548
5131
  });
4549
5132
  };
4550
5133
  }
@@ -4592,9 +5175,40 @@ function scalarOrCollection(renderer, layout) {
4592
5175
  });
4593
5176
  }
4594
5177
  function enumName(ui) {
4595
- const value = ui.enum;
4596
- if (typeof value === "string" && value.length > 0) return value;
4597
- if (value !== null && typeof value === "object" && typeof value.id === "string") return value.id;
5178
+ return enumBindingKey(ui.enum);
5179
+ }
5180
+ function RegistryEnumInput(props) {
5181
+ const app = useOptionalApp();
5182
+ const meta = props.field.meta;
5183
+ const sources = useFieldMetaSources({
5184
+ record: props.record,
5185
+ values: props.form,
5186
+ extra: props.metaSources
5187
+ });
5188
+ 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}`);
5189
+ return createElement(EnumPicker, {
5190
+ enumName: props.enumName,
5191
+ control,
5192
+ value: props.value == null ? void 0 : String(props.value),
5193
+ onChange: props.onChange ?? (() => void 0),
5194
+ name: props.accessibility.input,
5195
+ id: props.accessibility.input,
5196
+ testid: props.testIds.input,
5197
+ placeholder: typeof props.ui.placeholder === "string" ? props.ui.placeholder : void 0,
5198
+ disabled: props.disabled,
5199
+ required: props.field.required === true,
5200
+ ariaInvalid: props.issues.some(isErrorIssue),
5201
+ ariaDescribedBy: props.accessibility.description,
5202
+ ariaErrorMessage: props.accessibility.error,
5203
+ ariaLabelledBy: props.accessibility.label,
5204
+ use: {
5205
+ params: enumParamsOf(meta?.enumParams),
5206
+ narrow: narrowEntriesOf(meta?.narrow),
5207
+ sources,
5208
+ mode: props.record === void 0 ? "create" : "edit",
5209
+ baseline: props.baseline ?? props.record
5210
+ }
5211
+ });
4598
5212
  }
4599
5213
  function enumAware(renderer, layout) {
4600
5214
  const FallbackInput = input(renderer.Input);
@@ -4604,19 +5218,9 @@ function enumAware(renderer, layout) {
4604
5218
  return Object.freeze({
4605
5219
  Input: (props) => {
4606
5220
  const name = enumName(props.ui);
4607
- if (name !== void 0) return createElement(EnumPicker, {
4608
- enumName: name,
4609
- value: props.value == null ? void 0 : String(props.value),
4610
- onChange: props.onChange ?? (() => void 0),
4611
- name: props.accessibility.input,
4612
- id: props.accessibility.input,
4613
- testid: props.testIds.input,
4614
- placeholder: typeof props.ui.placeholder === "string" ? props.ui.placeholder : void 0,
4615
- disabled: props.disabled,
4616
- required: props.field.required === true,
4617
- ariaInvalid: props.issues.some(isErrorIssue),
4618
- ariaDescribedBy: props.accessibility.description,
4619
- ariaErrorMessage: props.accessibility.error
5221
+ if (name !== void 0) return createElement(RegistryEnumInput, {
5222
+ ...props,
5223
+ enumName: name
4620
5224
  });
4621
5225
  if (props.options.length > 0) return createElement(OptionedInput, props);
4622
5226
  return createElement(FallbackInput, props);
@@ -4651,6 +5255,14 @@ const date = {
4651
5255
  Display: DateDisplay,
4652
5256
  Input: DateInput
4653
5257
  };
5258
+ const dateTime = {
5259
+ Display: DateTimeDisplay,
5260
+ Input: DateTimeInput
5261
+ };
5262
+ const time = {
5263
+ Display: TimeDisplay,
5264
+ Input: TimeInput
5265
+ };
4654
5266
  const en = {
4655
5267
  Display: EnumDisplay,
4656
5268
  Input: EnumInput
@@ -4686,9 +5298,11 @@ const shadcnFieldImplementations = Object.freeze({
4686
5298
  number: scalar(num),
4687
5299
  boolean: scalar(bool),
4688
5300
  date: scalar(date),
4689
- dateTime: scalar(date),
4690
- "date-time": scalar(date),
4691
- "default:date-time": scalar(date),
5301
+ dateTime: scalar(dateTime),
5302
+ "date-time": scalar(dateTime),
5303
+ "default:date-time": scalar(dateTime),
5304
+ time: scalar(time),
5305
+ "default:time": scalar(time),
4692
5306
  enum: enumAware(en),
4693
5307
  json: scalar({
4694
5308
  Display: JsonDisplay,
@@ -4702,316 +5316,19 @@ const shadcnFieldImplementations = Object.freeze({
4702
5316
  richText: scalar(rich, { size: "full" }),
4703
5317
  "rich-text": scalar(rich, { size: "full" }),
4704
5318
  "default:rich-text": scalar(rich, { size: "full" }),
4705
- file: scalarOrCollection(file, { size: "full" })
5319
+ file: scalarOrCollection(file, { size: "full" }),
5320
+ object: Object.freeze({
5321
+ Input: ObjectGroupInput,
5322
+ Display: ObjectGroupDisplay,
5323
+ chrome: true
5324
+ }),
5325
+ array: Object.freeze({
5326
+ CollectionInput: RepeaterInput,
5327
+ CollectionDisplay: RepeaterDisplay,
5328
+ chrome: true
5329
+ })
4706
5330
  });
4707
5331
  //#endregion
4708
- //#region src/fields/use-relation-picker.tsx
4709
- const noop = () => void 0;
4710
- function safeKey(input) {
4711
- if (input === void 0 || input === null) return void 0;
4712
- try {
4713
- return resourceKey(input);
4714
- } catch {
4715
- return;
4716
- }
4717
- }
4718
- function sessionIdentity(runtime) {
4719
- const state = runtime.dataSession.getState();
4720
- const snapshot = state.status === "ready" ? state.snapshot : state.target;
4721
- let scopeKey;
4722
- try {
4723
- scopeKey = JSON.stringify(snapshot.scopeKey) ?? "";
4724
- } catch {
4725
- scopeKey = String(snapshot.scopeKey);
4726
- }
4727
- return {
4728
- sessionKey: snapshot.sessionKey,
4729
- scopeKey
4730
- };
4731
- }
4732
- function PickerHost(props) {
4733
- const { register } = props;
4734
- React.useEffect(() => register(), [register]);
4735
- return jsx(Fragment$1, { children: props.children });
4736
- }
4737
- function useRelationPicker(options) {
4738
- const app = useApp();
4739
- const runtime = useDataRuntime();
4740
- const frame = useOptionalRecordFrame();
4741
- const frames = useRecordScopeFrames();
4742
- const multiple = options.multiple === true;
4743
- const fieldName = options.field;
4744
- let to;
4745
- let declaredBy;
4746
- let declaredCandidates;
4747
- if (fieldName !== void 0) {
4748
- const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
4749
- to = safeKey(meta?.to);
4750
- declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
4751
- declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
4752
- } else to = safeKey(options.to);
4753
- const target = to ?? "";
4754
- const by = options.valueField ?? declaredBy;
4755
- const scope = options.scope ?? "context";
4756
- const selection = useCandidateSelection({
4757
- to,
4758
- candidates: options.candidates ?? declaredCandidates,
4759
- sources: frame?.controller.sources,
4760
- resource: frame?.resource ?? "",
4761
- fieldName: fieldName ?? target,
4762
- fixedFilters: options.fixedFilters
4763
- });
4764
- const held = selection.gate !== void 0;
4765
- const pendingRef = React.useRef(null);
4766
- const [active, setActive] = React.useState(null);
4767
- const activeRef = React.useRef(null);
4768
- activeRef.current = active;
4769
- const rowsRef = React.useRef(new Map());
4770
- const createdRowRef = React.useRef(void 0);
4771
- const alive = React.useRef(true);
4772
- const mounts = React.useRef(0);
4773
- const sequence = React.useRef(0);
4774
- const latest = React.useRef({
4775
- multiple,
4776
- select: options.select,
4777
- idField: "id",
4778
- valueField: "id",
4779
- available: false,
4780
- frames
4781
- });
4782
- const project = React.useCallback((row) => {
4783
- const select = latest.current.select;
4784
- if (select === void 0) return { ...row };
4785
- const out = {};
4786
- const key = row.id !== void 0 ? row.id : row[latest.current.idField];
4787
- if (key !== void 0) out.id = key;
4788
- const { valueField } = latest.current;
4789
- if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
4790
- for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
4791
- return out;
4792
- }, []);
4793
- const settle = React.useCallback((result) => {
4794
- const pending = pendingRef.current;
4795
- if (pending === null) return;
4796
- pendingRef.current = null;
4797
- pending.detach();
4798
- if (alive.current) setActive(null);
4799
- pending.resolve(result);
4800
- }, []);
4801
- const toggle = React.useCallback((key, row) => {
4802
- setActive((current) => {
4803
- if (current === null) return current;
4804
- const has = current.selected.some((k) => String(k) === String(key));
4805
- if (has) rowsRef.current.delete(String(key));
4806
- else if (row !== void 0) rowsRef.current.set(String(key), row);
4807
- const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
4808
- const next = {
4809
- ...current,
4810
- selected
4811
- };
4812
- activeRef.current = next;
4813
- return next;
4814
- });
4815
- }, []);
4816
- const onValue = React.useCallback((value) => {
4817
- const row = createdRowRef.current;
4818
- createdRowRef.current = void 0;
4819
- if (latest.current.multiple) {
4820
- const current = activeRef.current;
4821
- if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
4822
- return;
4823
- }
4824
- settle({
4825
- kind: "selected",
4826
- keys: [value],
4827
- rows: row === void 0 ? [] : [project(row)]
4828
- });
4829
- }, [
4830
- project,
4831
- settle,
4832
- toggle
4833
- ]);
4834
- const wiring = useRelationSelect({
4835
- to: target,
4836
- by,
4837
- fixedFilters: selection.fixedFilters,
4838
- scope,
4839
- ...fieldName === void 0 ? {} : { scopeField: fieldName },
4840
- held,
4841
- onValue
4842
- });
4843
- latest.current = {
4844
- multiple,
4845
- select: options.select,
4846
- idField: wiring.idField,
4847
- valueField: wiring.valueField,
4848
- available: to !== void 0 && wiring.targetDef !== void 0 && !held,
4849
- frames
4850
- };
4851
- const { create: shared } = wiring;
4852
- const create = React.useMemo(() => ({
4853
- ...shared,
4854
- start: (openCreate) => shared.start(() => {
4855
- const opened = openCreate();
4856
- opened.then((row) => {
4857
- createdRowRef.current = row === null ? void 0 : { ...row };
4858
- }, noop);
4859
- return opened;
4860
- })
4861
- }), [shared]);
4862
- React.useEffect(() => runtime.dataSession.subscribe(() => {
4863
- const pending = pendingRef.current;
4864
- if (pending === null) return;
4865
- const now = sessionIdentity(runtime);
4866
- if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
4867
- kind: "cancelled",
4868
- reason: "session-changed"
4869
- });
4870
- }), [runtime, settle]);
4871
- React.useEffect(() => {
4872
- alive.current = true;
4873
- return () => {
4874
- alive.current = false;
4875
- settle({
4876
- kind: "cancelled",
4877
- reason: "unmounted"
4878
- });
4879
- };
4880
- }, [settle]);
4881
- React.useEffect(() => {
4882
- if (held) settle({
4883
- kind: "cancelled",
4884
- reason: "unavailable"
4885
- });
4886
- }, [held, settle]);
4887
- const register = React.useCallback(() => {
4888
- mounts.current += 1;
4889
- return () => {
4890
- mounts.current -= 1;
4891
- if (mounts.current === 0) settle({
4892
- kind: "cancelled",
4893
- reason: "unmounted"
4894
- });
4895
- };
4896
- }, [settle]);
4897
- const open = React.useCallback((opts = {}) => new Promise((resolve) => {
4898
- settle({
4899
- kind: "cancelled",
4900
- reason: "superseded"
4901
- });
4902
- if (mounts.current === 0) {
4903
- if (process.env.NODE_ENV !== "production") console.error("[burojs] useRelationPicker: open() was called but its element is not rendered — render `handle.element` once in your tree.");
4904
- resolve({
4905
- kind: "cancelled",
4906
- reason: "not-mounted"
4907
- });
4908
- return;
4909
- }
4910
- const { signal } = opts;
4911
- if (signal?.aborted === true) {
4912
- resolve({
4913
- kind: "cancelled",
4914
- reason: "aborted"
4915
- });
4916
- return;
4917
- }
4918
- if (!latest.current.available) {
4919
- resolve({
4920
- kind: "cancelled",
4921
- reason: "unavailable"
4922
- });
4923
- return;
4924
- }
4925
- sequence.current += 1;
4926
- const id = sequence.current;
4927
- const onAbort = () => {
4928
- if (pendingRef.current?.id === id) settle({
4929
- kind: "cancelled",
4930
- reason: "aborted"
4931
- });
4932
- };
4933
- signal?.addEventListener("abort", onAbort, { once: true });
4934
- pendingRef.current = {
4935
- id,
4936
- ...sessionIdentity(runtime),
4937
- resolve,
4938
- detach: () => signal?.removeEventListener("abort", onAbort)
4939
- };
4940
- rowsRef.current = new Map();
4941
- createdRowRef.current = void 0;
4942
- const next = {
4943
- id,
4944
- frames: latest.current.frames,
4945
- selected: opts.selected ?? []
4946
- };
4947
- activeRef.current = next;
4948
- setActive(next);
4949
- }), [runtime, settle]);
4950
- const onSelectRow = React.useCallback((row) => {
4951
- const field = wiring.valueField;
4952
- const value = field === "id" ? row.id : row[field];
4953
- if (typeof value !== "string" && typeof value !== "number") return;
4954
- settle({
4955
- kind: "selected",
4956
- keys: [value],
4957
- rows: [project(row)]
4958
- });
4959
- }, [
4960
- wiring.valueField,
4961
- settle,
4962
- project
4963
- ]);
4964
- const onDone = React.useCallback(() => {
4965
- const keys = activeRef.current?.selected ?? [];
4966
- const rows = keys.flatMap((key) => {
4967
- const row = rowsRef.current.get(String(key));
4968
- return row === void 0 ? [] : [project(row)];
4969
- });
4970
- settle({
4971
- kind: "selected",
4972
- keys,
4973
- rows
4974
- });
4975
- }, [settle, project]);
4976
- const onOpenChange = React.useCallback((next) => {
4977
- if (!next) settle({
4978
- kind: "cancelled",
4979
- reason: "dismissed"
4980
- });
4981
- }, [settle]);
4982
- return {
4983
- open,
4984
- element: jsx(PickerHost, {
4985
- register,
4986
- children: active === null || held ? null : jsxs(RecordScopeChain, {
4987
- frames: active.frames,
4988
- children: [jsx(LazyRelationPickerDialog, {
4989
- to: target,
4990
- open: true,
4991
- onOpenChange,
4992
- onSelect: noop,
4993
- onSelectRow,
4994
- valueField: wiring.valueField,
4995
- ...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
4996
- scope,
4997
- ...fieldName === void 0 ? {} : { scopeField: fieldName },
4998
- ...options.create === void 0 ? {} : {
4999
- create,
5000
- ...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
5001
- },
5002
- fieldName: fieldName ?? target,
5003
- ...multiple ? {
5004
- multiple: true,
5005
- selected: active.selected,
5006
- onToggle: (value, row) => toggle(value, row),
5007
- onDone
5008
- } : {}
5009
- }), wiring.reader]
5010
- }, active.id)
5011
- })
5012
- };
5013
- }
5014
- //#endregion
5015
5332
  //#region src/forms/declarative-display.tsx
5016
5333
  function DeclarativeDisplay(props) {
5017
5334
  return jsx(ScreenStateProvider, { children: jsx(DisplayBody, { ...props }) });
@@ -5037,7 +5354,8 @@ function DisplayBody(props) {
5037
5354
  }
5038
5355
  //#endregion
5039
5356
  //#region src/enum/enum-display.tsx
5040
- function EnumDisplay$1({ enumName, value, variant = "badge", testid, className }) {
5357
+ function EnumDisplay$1({ enumName: enumInput, value, variant = "badge", testid, className }) {
5358
+ const enumName = enumBindingKey(enumInput) ?? "";
5041
5359
  const app = useApp();
5042
5360
  const caps = useI18nCaps();
5043
5361
  const def = app.registry.enums.get(enumName);
@@ -5085,159 +5403,6 @@ function EnumDisplay$1({ enumName, value, variant = "badge", testid, className }
5085
5403
  });
5086
5404
  }
5087
5405
  //#endregion
5088
- //#region src/data-table/data-table-faceted-filter.tsx
5089
- function DataTableFacetedFilter({ column, title, options = [], loadOptions, selectedLabelFor, searchPlaceholder, emptyText, getTestId, className }) {
5090
- const t = useOptionalT();
5091
- const filterValue = column.getFilterValue();
5092
- const selected = useMemo(() => {
5093
- if (Array.isArray(filterValue)) return new Set(filterValue.map(String));
5094
- return new Set();
5095
- }, [filterValue]);
5096
- const [search, setSearch] = useState("");
5097
- const [asyncOptions, setAsyncOptions] = useState([]);
5098
- const [loading, setLoading] = useState(false);
5099
- const debounceRef = useRef(null);
5100
- const loadOptionsRef = useRef(loadOptions);
5101
- useEffect(() => {
5102
- loadOptionsRef.current = loadOptions;
5103
- });
5104
- useEffect(() => {
5105
- if (!loadOptionsRef.current) return;
5106
- setLoading(true);
5107
- if (debounceRef.current !== null) clearTimeout(debounceRef.current);
5108
- let cancelled = false;
5109
- debounceRef.current = setTimeout(() => {
5110
- const loader = loadOptionsRef.current;
5111
- if (!loader) return;
5112
- loader(search).then((results) => {
5113
- if (!cancelled) {
5114
- setAsyncOptions(results);
5115
- setLoading(false);
5116
- }
5117
- }).catch(() => {
5118
- if (!cancelled) setLoading(false);
5119
- });
5120
- }, 250);
5121
- return () => {
5122
- cancelled = true;
5123
- if (debounceRef.current !== null) clearTimeout(debounceRef.current);
5124
- };
5125
- }, [search]);
5126
- const effectiveOptions = loadOptions ? asyncOptions : options;
5127
- const labelFor = (value) => {
5128
- const opt = effectiveOptions.find((o) => o.value === value);
5129
- if (opt) return opt.label;
5130
- return selectedLabelFor?.(value) ?? value;
5131
- };
5132
- const toggle = (value) => {
5133
- const next = new Set(selected);
5134
- if (next.has(value)) next.delete(value);
5135
- else next.add(value);
5136
- column.setFilterValue(next.size === 0 ? void 0 : Array.from(next));
5137
- };
5138
- const selectedValues = Array.from(selected);
5139
- return jsxs(Popover, { children: [jsx(PopoverTrigger, {
5140
- asChild: true,
5141
- children: jsxs(Button, {
5142
- type: "button",
5143
- variant: "outline",
5144
- size: "sm",
5145
- "data-testid": getTestId?.("trigger"),
5146
- className: cn("h-8 border-dashed", className),
5147
- children: [
5148
- jsx(IconCirclePlus, {
5149
- className: "mr-2 h-4 w-4",
5150
- "aria-hidden": "true"
5151
- }),
5152
- title,
5153
- selected.size > 0 && jsxs(Fragment$1, { children: [
5154
- jsx("span", {
5155
- className: "mx-2 h-4 w-px bg-border",
5156
- "aria-hidden": "true"
5157
- }),
5158
- jsx(Badge, {
5159
- variant: "secondary",
5160
- className: "rounded-sm px-1 font-normal lg:hidden",
5161
- children: selected.size
5162
- }),
5163
- jsx("div", {
5164
- className: "hidden gap-1 lg:flex",
5165
- children: selected.size > 2 ? jsx(Badge, {
5166
- variant: "secondary",
5167
- className: "rounded-sm px-1 font-normal",
5168
- children: t("table.selectedCount", {
5169
- count: selected.size,
5170
- defaultValue: "{{count}} selected"
5171
- })
5172
- }) : selectedValues.map((value) => jsx(Badge, {
5173
- variant: "secondary",
5174
- className: "rounded-sm px-1 font-normal",
5175
- children: labelFor(value)
5176
- }, value))
5177
- })
5178
- ] })
5179
- ]
5180
- })
5181
- }), jsx(PopoverContent, {
5182
- className: "w-[220px] p-0",
5183
- align: "start",
5184
- children: jsxs(Command, {
5185
- shouldFilter: !loadOptions,
5186
- children: [
5187
- jsx(CommandInput, {
5188
- placeholder: searchPlaceholder ?? title,
5189
- "data-testid": getTestId?.("search"),
5190
- ...loadOptions ? {
5191
- value: search,
5192
- onValueChange: setSearch
5193
- } : {}
5194
- }),
5195
- jsxs(CommandList, { children: [
5196
- loading && jsx("div", {
5197
- className: "py-6 text-center text-sm text-muted-foreground",
5198
- children: t("list.loadingShort", { defaultValue: "Loading…" })
5199
- }),
5200
- jsx(CommandEmpty, { children: emptyText ?? t("relation.noResults", { defaultValue: "No results." }) }),
5201
- jsx(CommandGroup, { children: effectiveOptions.map((opt) => {
5202
- const isSelected = selected.has(opt.value);
5203
- const Icon = opt.icon;
5204
- return jsxs(CommandItem, {
5205
- value: opt.label,
5206
- "data-testid": getTestId?.("option", opt.value),
5207
- role: "option",
5208
- "aria-selected": isSelected,
5209
- "aria-checked": isSelected,
5210
- onSelect: () => toggle(opt.value),
5211
- children: [
5212
- jsx("span", {
5213
- 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"),
5214
- "aria-hidden": "true",
5215
- children: isSelected && jsx(IconCheck, { className: "h-3.5 w-3.5" })
5216
- }),
5217
- Icon && jsx(Icon, {
5218
- className: "mr-2 h-4 w-4 text-muted-foreground",
5219
- "aria-hidden": "true"
5220
- }),
5221
- jsx("span", {
5222
- className: "flex-1 text-left",
5223
- children: opt.label
5224
- })
5225
- ]
5226
- }, opt.value);
5227
- }) })
5228
- ] }),
5229
- selected.size > 0 && jsx("button", {
5230
- type: "button",
5231
- "data-testid": getTestId?.("clear"),
5232
- onClick: () => column.setFilterValue(void 0),
5233
- className: "flex w-full items-center justify-center border-t px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
5234
- children: t("filters.clearAll", { defaultValue: "Clear filters" })
5235
- })
5236
- ]
5237
- })
5238
- })] });
5239
- }
5240
- //#endregion
5241
5406
  //#region src/data-table/data-table-text-filter.tsx
5242
5407
  function DataTableTextFilter({ column, placeholder, testid, className }) {
5243
5408
  const external = column.getFilterValue() ?? "";
@@ -5391,14 +5556,15 @@ const OPERATOR_LABELS = {
5391
5556
  has: "has",
5392
5557
  hasAny: "has any of",
5393
5558
  hasAll: "has all of",
5394
- hasNone: "has none of"
5559
+ hasNone: "has none of",
5560
+ some: "has an item where"
5395
5561
  };
5396
- function formatOperatorLabel(op, t) {
5397
- return t ? t(`filters.operators.${op}`, { defaultValue: OPERATOR_LABELS[op] }) : OPERATOR_LABELS[op];
5562
+ function formatOperatorLabel(op, chromeT) {
5563
+ return chromeT ? chromeT(`filters.operators.${op}`, { defaultValue: OPERATOR_LABELS[op] }) : OPERATOR_LABELS[op];
5398
5564
  }
5399
5565
  function useOperatorLabel() {
5400
- const t = useOptionalT();
5401
- return (op) => formatOperatorLabel(op, t);
5566
+ const chromeT = useChromeT();
5567
+ return (op) => formatOperatorLabel(op, chromeT);
5402
5568
  }
5403
5569
  function isEmpty(v) {
5404
5570
  return v === void 0 || v === null || v === "";
@@ -5492,7 +5658,7 @@ function DataTableOperatorFilter({ column, operators, defaultOperator, valueKind
5492
5658
  //#region src/data-table/data-table-row-actions.tsx
5493
5659
  function DataTableRowActions({ row, actions, getTestId, className }) {
5494
5660
  const [open, setOpen] = useState(false);
5495
- const t = useOptionalT();
5661
+ const chromeT = useChromeT();
5496
5662
  if (actions.length === 0) return null;
5497
5663
  return jsxs(Popover, {
5498
5664
  open,
@@ -5510,7 +5676,7 @@ function DataTableRowActions({ row, actions, getTestId, className }) {
5510
5676
  "aria-hidden": "true"
5511
5677
  }), jsx("span", {
5512
5678
  className: "sr-only",
5513
- children: t("actions.openMenu", { defaultValue: "Open row actions menu" })
5679
+ children: chromeT("actions.openMenu", { defaultValue: "Open row actions menu" })
5514
5680
  })]
5515
5681
  })
5516
5682
  }), jsx(PopoverContent, {
@@ -5631,7 +5797,8 @@ function BlockScreen({ block, props, resource, record, label }) {
5631
5797
  children: jsx(PlannedBody, {
5632
5798
  screen: compiled,
5633
5799
  name: "modal",
5634
- label: label ?? ""
5800
+ label: label ?? "",
5801
+ ...needsRow && row !== null ? { record: row } : {}
5635
5802
  })
5636
5803
  })
5637
5804
  });
@@ -5640,13 +5807,20 @@ function relation(field) {
5640
5807
  if (field === null || typeof field !== "object") return false;
5641
5808
  return isRelationField(field);
5642
5809
  }
5810
+ const resolveOf = (bag) => bag !== null && typeof bag === "object" ? bag.resolve : void 0;
5811
+ function lazyToOne(field) {
5812
+ const entry = field;
5813
+ if (entry.kind === "relationMany" || entry.type === "array") return false;
5814
+ return (resolveOf(entry.meta) ?? resolveOf(entry.opts)) === "lazy";
5815
+ }
5643
5816
  function peekFields(def, columns, capabilityFor, fieldVisible = () => true) {
5644
5817
  const record = new Set(dtoFields(def, "record"));
5645
5818
  const out = [];
5646
5819
  for (const name of columns) {
5647
5820
  if (out.length === 5) break;
5648
5821
  if (name === def.displayField || out.includes(name) || !record.has(name)) continue;
5649
- if (!peekLeafAllowed(def, name, capabilityFor, fieldVisible) || relation(peekFieldDefinition(def, name))) continue;
5822
+ const field = peekFieldDefinition(def, name);
5823
+ if (!peekLeafAllowed(def, name, capabilityFor, fieldVisible) || relation(field) && !lazyToOne(field)) continue;
5650
5824
  out.push(name);
5651
5825
  }
5652
5826
  return Object.freeze(out);
@@ -5729,7 +5903,7 @@ function PeekByKey({ target, by }) {
5729
5903
  } else if (query.error != null) read = failure(query.error, () => void query.refetch());
5730
5904
  else read = { state: "pending" };
5731
5905
  useEffect(() => {
5732
- if (read.state === "ambiguous" && process.env.NODE_ENV !== "production") console.error(`[burojs] ${relationAmbiguousError(target, by.field, by.key).message}`);
5906
+ if (read.state === "ambiguous" && process.env.NODE_ENV !== "production") console.error(`[burojs] ${technicalText(relationAmbiguousError(target, by.field, by.key))}`);
5733
5907
  }, [
5734
5908
  read.state,
5735
5909
  target,
@@ -5745,6 +5919,7 @@ function PeekByKey({ target, by }) {
5745
5919
  function PeekBody({ target, peekKey, read }) {
5746
5920
  const tid = useTestIds();
5747
5921
  const t = useOptionalT();
5922
+ const chromeT = useChromeT();
5748
5923
  const testid = tid.peek(target, peekKey);
5749
5924
  if (read.state === "pending") return jsx("div", {
5750
5925
  "data-testid": testid,
@@ -5772,7 +5947,7 @@ function PeekBody({ target, peekKey, read }) {
5772
5947
  variant: "outline",
5773
5948
  "data-testid": `${testid}:retry`,
5774
5949
  onClick: read.retry,
5775
- children: t("actions.retry", { defaultValue: "Retry" })
5950
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
5776
5951
  })]
5777
5952
  });
5778
5953
  if (read.state === "unavailable") return jsx("p", {
@@ -5916,6 +6091,7 @@ function PeekRecord({ target, testid, row }) {
5916
6091
  function DeclaredPeekBody({ target, testid, record, frameRecord, def, capabilityFor, fieldVisible, view: View, builtin, open, openable, t }) {
5917
6092
  const host = useContext(PeekHostContext);
5918
6093
  const title = recordDisplayName(record, def?.displayField);
6094
+ const subtitle = useRecordSubtitle(target)(record) ?? void 0;
5919
6095
  const setLabel = host?.setLabel;
5920
6096
  useEffect(() => setLabel?.(title), [setLabel, title]);
5921
6097
  const controller = useFormController({
@@ -5951,6 +6127,7 @@ function DeclaredPeekBody({ target, testid, record, frameRecord, def, capability
5951
6127
  children: [builtin ? jsx(PeekTitle, {
5952
6128
  testid,
5953
6129
  title,
6130
+ subtitle,
5954
6131
  open: openable ? open : void 0
5955
6132
  }) : null, jsx(Suspense, {
5956
6133
  fallback: null,
@@ -5963,11 +6140,26 @@ function DeclaredPeekBody({ target, testid, record, frameRecord, def, capability
5963
6140
  })]
5964
6141
  });
5965
6142
  }
5966
- function PeekTitle({ testid, title, open }) {
5967
- if (open === void 0) return jsx("p", {
6143
+ function PeekTitle({ testid, title, subtitle, open }) {
6144
+ const heading = open === void 0 ? jsx("p", {
5968
6145
  className: "text-sm font-semibold",
5969
6146
  children: title
6147
+ }) : jsx(PeekTitleButton, {
6148
+ testid,
6149
+ title,
6150
+ open
6151
+ });
6152
+ if (subtitle === void 0) return heading;
6153
+ return jsxs("div", {
6154
+ className: "flex flex-col gap-0.5",
6155
+ children: [heading, jsx("p", {
6156
+ "data-testid": `${testid}:subtitle`,
6157
+ className: "text-xs text-muted-foreground",
6158
+ children: subtitle
6159
+ })]
5970
6160
  });
6161
+ }
6162
+ function PeekTitleButton({ testid, title, open }) {
5971
6163
  return jsx("button", {
5972
6164
  type: "button",
5973
6165
  "data-testid": `${testid}:title`,
@@ -5995,12 +6187,14 @@ function DefaultPeekBody({ target, testid, row, open }) {
5995
6187
  const projected = useProjectedRecord(target, dtoRow);
5996
6188
  const record = projected.value ?? {};
5997
6189
  const title = recordDisplayName(record, def?.displayField);
6190
+ const subtitleOf = useRecordSubtitle(target);
6191
+ const subtitle = projected.status === "ready" ? subtitleOf(record) ?? void 0 : void 0;
5998
6192
  const setLabel = host?.setLabel;
5999
6193
  useEffect(() => setLabel?.(title), [setLabel, title]);
6000
6194
  const columns = useMemo(() => {
6001
6195
  if (def === void 0) return [];
6002
6196
  const listed = def.views?.list?.columns;
6003
- return peekFields(def, listed ?? Object.keys(def.fields), capabilityFor, (name) => access[name]?.view === "visible");
6197
+ return peekFields(def, listed?.map(columnName) ?? Object.keys(def.fields), capabilityFor, (name) => access[name]?.view === "visible");
6004
6198
  }, [
6005
6199
  def,
6006
6200
  capabilityFor,
@@ -6026,6 +6220,7 @@ function DefaultPeekBody({ target, testid, row, open }) {
6026
6220
  children: [jsx(PeekTitle, {
6027
6221
  testid,
6028
6222
  title,
6223
+ subtitle,
6029
6224
  open
6030
6225
  }), columns.length === 0 ? null : jsx("dl", {
6031
6226
  className: "grid gap-1.5",
@@ -6040,13 +6235,21 @@ function DefaultPeekBody({ target, testid, row, open }) {
6040
6235
  children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
6041
6236
  }), jsx("dd", {
6042
6237
  className: "min-w-0 break-words",
6043
- children: jsx(ResolvedFieldDisplay, {
6044
- resource: ref,
6045
- name,
6046
- field,
6238
+ children: jsx(FieldValueFrame, {
6239
+ field: {
6240
+ ...field,
6241
+ name
6242
+ },
6047
6243
  value,
6048
- record,
6049
- fallback: toDisplayText(value).text
6244
+ surface: "display",
6245
+ children: jsx(ResolvedFieldDisplay, {
6246
+ resource: ref,
6247
+ name,
6248
+ field,
6249
+ value,
6250
+ record,
6251
+ fallback: toDisplayText(value).text
6252
+ })
6050
6253
  })
6051
6254
  })]
6052
6255
  }, name);
@@ -6069,16 +6272,25 @@ function QuickBodyOf({ entry }) {
6069
6272
  route: EMPTY_ROUTE
6070
6273
  });
6071
6274
  if (entry.recordId === void 0 || entry.recordId === "") return jsx(RecordState, { state: "not-found" });
6072
- return jsx(QuickRead, {
6275
+ return jsx(QuickReadRoute, {
6073
6276
  entry,
6074
6277
  recordId: entry.recordId
6075
6278
  });
6076
6279
  }
6077
- function QuickRead({ entry, recordId }) {
6280
+ function QuickReadRoute({ entry, recordId }) {
6281
+ const typed = useRouteRecordId(entry.resource, recordId);
6282
+ if (typed === void 0) return jsx(InvalidRouteId, { resource: entry.resource });
6283
+ return jsx(QuickRead, {
6284
+ entry,
6285
+ recordId,
6286
+ typedId: typed
6287
+ });
6288
+ }
6289
+ function QuickRead({ entry, recordId, typedId }) {
6078
6290
  const def = useApp().registry.get(entry.resource);
6079
6291
  const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
6080
6292
  const query = useOne(entry.resource, {
6081
- id: recordId,
6293
+ id: typedId,
6082
6294
  retry: retryRecordRead,
6083
6295
  ...projection === void 0 ? {} : { projection }
6084
6296
  });
@@ -6089,20 +6301,20 @@ function QuickRead({ entry, recordId }) {
6089
6301
  return jsx(QuickLoaded, {
6090
6302
  entry,
6091
6303
  recordId,
6304
+ typedId,
6092
6305
  row,
6093
6306
  isCurrentData: !query.isPlaceholderData
6094
6307
  });
6095
6308
  }
6096
- if (query.error != null) {
6097
- const state = recordErrorState(query.error);
6098
- return jsx(RecordState, {
6099
- state,
6100
- ...state === "error" ? { onRetry: () => void query.refetch() } : {}
6101
- });
6102
- }
6309
+ if (query.error != null) return jsx(LoadError, {
6310
+ surface: "record",
6311
+ resource: entry.resource,
6312
+ error: query.error,
6313
+ retry: () => void query.refetch()
6314
+ });
6103
6315
  return jsx(RecordState, { state: "loading" });
6104
6316
  }
6105
- function QuickLoaded({ entry, recordId, row, isCurrentData }) {
6317
+ function QuickLoaded({ entry, recordId, typedId, row, isCurrentData }) {
6106
6318
  const gate = useEntryGate();
6107
6319
  const quick = useQuickEntry();
6108
6320
  const answer = gate(entry.resource, entry.mode === "edit" ? "edit" : "show", row);
@@ -6122,16 +6334,16 @@ function QuickLoaded({ entry, recordId, row, isCurrentData }) {
6122
6334
  if (!open) return jsx(RecordState, { state: "not-found" });
6123
6335
  return jsx(QuickScreenOf, {
6124
6336
  entry,
6125
- route
6337
+ route,
6338
+ recordId: typedId
6126
6339
  });
6127
6340
  }
6128
6341
  function canonicalOrNull(name) {
6129
- if (typeof name !== "string") return null;
6130
6342
  try {
6131
- return canonicalResourceName(name);
6132
- } catch {
6133
- return null;
6134
- }
6343
+ if (typeof name === "string") return canonicalResourceName(name);
6344
+ if (name !== null && typeof name === "object") return routeResourceKey(name);
6345
+ } catch {}
6346
+ return null;
6135
6347
  }
6136
6348
  function absorbedRedirect(to, entry) {
6137
6349
  if (typeof to !== "object" || to === null) return null;
@@ -6145,7 +6357,7 @@ function closeEntry(quick, dismiss) {
6145
6357
  if (quick.stack.entries.length > 1) quick.stack.pop();
6146
6358
  else dismiss(quick.presentationId);
6147
6359
  }
6148
- function QuickScreenOf({ entry, route }) {
6360
+ function QuickScreenOf({ entry, route, recordId }) {
6149
6361
  const quick = useQuickEntry();
6150
6362
  const instance = useUIInstance();
6151
6363
  const runtime = useUIPresentationRuntime();
@@ -6197,7 +6409,7 @@ function QuickScreenOf({ entry, route }) {
6197
6409
  children: jsx(RecordScreen, {
6198
6410
  resource: entry.resource,
6199
6411
  mode: entry.mode,
6200
- ...entry.recordId === void 0 ? {} : { recordId: entry.recordId },
6412
+ ...recordId === void 0 ? {} : { recordId },
6201
6413
  ...entry.defaults === void 0 ? {} : { defaults: entry.defaults },
6202
6414
  ...entry.input === void 0 ? {} : { input: entry.input },
6203
6415
  ...callbacks
@@ -6219,7 +6431,7 @@ function useEntryTitle(entry) {
6219
6431
  const caps = useI18nCaps(entry.resource);
6220
6432
  const t = useOptionalT();
6221
6433
  if (entry.mode !== "create") return entry.title ?? "";
6222
- return t("pageHeader.createTitle", { params: { label: resolveResourceLabel(caps, entry.resource, "single", app.registry.get(entry.resource)) } });
6434
+ return resolveResourceTitle(caps, t, entry.resource, "createTitle", { label: resolveResourceLabel(caps, entry.resource, "single", app.registry.get(entry.resource)) });
6223
6435
  }
6224
6436
  const HEADING_CLASS = "min-w-0 flex-1 break-words text-lg font-semibold tracking-tight focus-visible:outline-hidden";
6225
6437
  function QuickHeader({ entries }) {
@@ -6476,11 +6688,25 @@ SheetPresentationHost.optionsSchema = Object.freeze({
6476
6688
  //#region src/ui-presentations/index.tsx
6477
6689
  function FlowScreen(props) {
6478
6690
  const loaded = useLoaded(loadFlowScreen);
6479
- if (loaded.error !== void 0) return jsx(FlowLoadFailed, { onRetry: loaded.retry });
6480
- if (loaded.module === void 0) return null;
6691
+ if (loaded.error !== void 0) return jsx(FlowLoadFailed, {
6692
+ label: props.label,
6693
+ onRetry: loaded.retry
6694
+ });
6695
+ if (loaded.module === void 0) return jsxs("div", {
6696
+ "data-buro-flow-step": "loading",
6697
+ "aria-busy": "true",
6698
+ className: "flex flex-col gap-4",
6699
+ children: [jsx(OwnTitleHeading, { label: props.label }), jsx(IconLoader2, {
6700
+ className: "h-5 w-5 animate-spin self-center",
6701
+ "aria-hidden": "true"
6702
+ })]
6703
+ });
6481
6704
  const Screen = loaded.module.FlowScreen;
6482
6705
  return jsx(FlowBoundary, {
6483
- fallback: (_error, reset) => jsx(FlowLoadFailed, { onRetry: reset }),
6706
+ fallback: (_error, reset) => jsx(FlowLoadFailed, {
6707
+ label: props.label,
6708
+ onRetry: reset
6709
+ }),
6484
6710
  children: jsx(Screen, { ...props })
6485
6711
  });
6486
6712
  }
@@ -6755,9 +6981,13 @@ function DefaultCommandPalette({ open, onOpenChange, actions, children }) {
6755
6981
  }
6756
6982
  //#endregion
6757
6983
  //#region src/shell/app-shell.tsx
6758
- function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, children }) {
6984
+ function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, navChrome, children }) {
6759
6985
  const enabled = palette !== false;
6760
- const { open, setOpen } = useCommandPalette({ disableShortcut: !enabled });
6986
+ const { open, setOpen, toggle } = useCommandPalette();
6987
+ useGlobalShortcut({
6988
+ key: "k",
6989
+ mod: true
6990
+ }, toggle, { enabled });
6761
6991
  const paletteState = useMemo(() => ({
6762
6992
  open,
6763
6993
  setOpen
@@ -6775,7 +7005,7 @@ function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel,
6775
7005
  sidebar: fill(sidebar, () => jsx(AppSidebar, {})),
6776
7006
  header: fill(header, () => jsx(AppHeader, {})),
6777
7007
  ...docPanel !== void 0 ? { docPanel } : {},
6778
- children
7008
+ children: navChrome === false ? children : jsx(NavChrome, { children })
6779
7009
  }), paletteNode]
6780
7010
  });
6781
7011
  }
@@ -6883,6 +7113,6 @@ function ScopeSwitcher({ options, value, onChange, label, extra, placeholder })
6883
7113
  const PACKAGE_NAME = "@burojs/ui-shadcn";
6884
7114
  const PACKAGE_VERSION = "0.0.0";
6885
7115
  //#endregion
6886
- 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, 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, 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, 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, useNavChromeBoundary, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useRelationPicker, useShell, useSidebar, useTheme, useThemePaletteActions, wrapperEnabled };
7116
+ 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 };
6887
7117
 
6888
7118
  //# sourceMappingURL=index.js.map