@burojs/ui-shadcn 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-BWSAS7k2.js +143 -0
  3. package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
  4. package/dist/bulk-summary-CrEij5vq.cjs +143 -0
  5. package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
  6. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CS_JZ4Fd.js} +3 -3
  7. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CS_JZ4Fd.js.map} +1 -1
  8. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-y5M6_oe4.cjs} +4 -4
  9. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-y5M6_oe4.cjs.map} +1 -1
  10. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-2j0gOd7x.js} +384 -108
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-C-NHgMST.cjs} +468 -107
  13. package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-DFu0JP-1.cjs} +923 -120
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-DaWHUZuV.js} +839 -108
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-Bcz9NF2V.cjs} +128 -87
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-DCdli6Ha.js} +128 -87
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +731 -906
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +455 -247
  33. package/dist/index.d.ts +455 -247
  34. package/dist/index.js +647 -828
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
  37. package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-Bt8lrEIu.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-CAScLPb2.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-BHUEpn0I.js} +36 -23
  45. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  47. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  48. package/dist/{place-node-C5SmIAYU.cjs → place-node-CNb3w-c7.cjs} +1869 -947
  49. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  50. package/dist/{place-node-NraNYKPx.js → place-node-CfzQvh4Y.js} +1824 -926
  51. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  52. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BIqqY69Y.js} +9 -4
  53. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  54. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  55. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  56. package/dist/{related-list-timeline-yrPsjA3F.js → related-list-timeline-CRh7xwLD.js} +6 -7
  57. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  58. package/dist/{related-list-timeline-CCzl71aA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  59. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  60. package/dist/{related-rows-o0_t-Yce.js → related-rows--uP2iKQC.js} +25 -14
  61. package/dist/related-rows--uP2iKQC.js.map +1 -0
  62. package/dist/{related-rows-ea43qhhy.cjs → related-rows-MPnLCmPF.cjs} +27 -16
  63. package/dist/related-rows-MPnLCmPF.cjs.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-BJhaEqxz.js} +203 -102
  69. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-DzuGJpEl.cjs} +211 -110
  71. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  72. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-D-tBBZW_.js} +23 -59
  73. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  74. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-DaIMzf2H.cjs} +29 -71
  75. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +133 -35
  77. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  78. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-DoEY8EkH.js} +134 -36
  79. package/dist/relation-picker-dialog-DoEY8EkH.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-PXfMW9nh.cjs → resource-page-router-C_kGW2o_.cjs} +543 -300
  85. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  86. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-Diw6S6TK.js} +498 -255
  87. package/dist/resource-page-router-Diw6S6TK.js.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/package.json +11 -11
  105. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  106. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  107. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  108. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  109. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  110. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  111. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  112. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  113. package/dist/dialog-B_rt9S1c.js.map +0 -1
  114. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  115. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  116. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  117. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  118. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  119. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  120. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  121. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  122. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  123. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  124. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  125. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  126. package/dist/place-node-NraNYKPx.js.map +0 -1
  127. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  128. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  129. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  130. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  131. package/dist/relation-create-IA40t1hi.cjs +0 -364
  132. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  133. package/dist/relation-create-h6F9B4C4.js +0 -339
  134. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  135. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  136. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  137. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  138. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  139. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  140. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  141. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  142. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  143. package/dist/search-access-CPPmfuvy.js.map +0 -1
  144. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  145. package/dist/shell-BPlk8hEa.js +0 -205
  146. package/dist/shell-BPlk8hEa.js.map +0 -1
  147. package/dist/shell-DxhlsPwm.cjs +0 -271
  148. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  149. package/dist/toaster-C02XbB9r.js.map +0 -1
  150. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  151. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  152. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
@@ -1,20 +1,22 @@
1
1
  import { l as cn } from "./use-theme-dnE3Od3I.js";
2
- import { d as PopoverTrigger, i as toFriendlyError, n as toast, o as useLinkComponent, r as friendlyStringsFrom, s as Popover, u as PopoverContent } from "./toaster-C02XbB9r.js";
3
- import { C as loadFailedText, D as preloadFlowRuntime, F as DropdownMenuGroup, I as DropdownMenuItem, K as DropdownMenuTrigger, L as DropdownMenuLabel, M as DropdownMenu, O as useLoaded, P as DropdownMenuContent, S as loadBulkSummary, T as loadInlineFlowHost, V as DropdownMenuSeparator, Z as DocInfo, _ as FlowBoundary, b as RuntimeBulkFlowRunner, c as useListSlot, d as useToBrowserHref, f as FlowActionButton, g as ActionInner, h as useFlowChrome, k as Icon, m as text, n as ActionButton, o as canonicalResourceName, p as FlowMenuItem, r as DisabledAction, t as CheckConfirmDialog, x as RuntimeFlowRunner, y as RuntimeAppFlowRunner } from "./check-confirm-dialog-bRT8lt0d.js";
2
+ import { i as isNetworkFailure, l as useLinkComponent, m as PopoverTrigger, n as toast, o as technicalTexts, p as PopoverContent, r as friendlyStringsFrom, s as toFriendlyError, u as Popover } from "./toaster-Ct_YHGs6.js";
3
+ import { B as DropdownMenuContent, C as loadBulkSummary, E as loadInlineFlowHost, F as Icon, H as DropdownMenuItem, O as preloadFlowRuntime, Q as DropdownMenuTrigger, R as DropdownMenu, S as RuntimeFlowRunner, U as DropdownMenuLabel, V as DropdownMenuGroup, _ as reasonText, b as RuntimeAppFlowRunner, c as useListSlot, d as useToBrowserHref, dt as DocInfo, f as FlowActionButton, g as ActionInner, k as useLoaded, m as useFlowChrome, n as ActionButton, o as canonicalResourceName, p as FlowMenuItem, q as DropdownMenuSeparator, r as DisabledAction, t as CheckConfirmDialog, v as FlowBoundary, w as loadFailedText, x as RuntimeBulkFlowRunner } from "./check-confirm-dialog-2j0gOd7x.js";
4
4
  import { t as Button } from "./button-MWVeZaqj.js";
5
5
  import { t as Input } from "./input-CUPStzZO.js";
6
- import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-B_rt9S1c.js";
7
- import { o as relationLinkClassName, p as ClaimedQuickLink } from "./search-access-CPPmfuvy.js";
6
+ import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
7
+ import { g as ClaimedQuickLink, l as relationLinkClassName, y as LoadError } from "./relation-create-C43cdPQt.js";
8
8
  import { t as Skeleton } from "./skeleton-U2noefj5.js";
9
- import { C as TableHeader, S as TableHead, T as Badge, _ as SelectValue, c as Select, d as SelectItem, g as SelectTrigger, l as SelectContent, t as FilterBuilder, v as Table$1, w as TableRow, x as TableCell, y as TableBody } from "./filter-builder-Bhn-dhZV.js";
9
+ import { A as TableRow, C as SelectValue, D as TableCell, F as ResolvedFieldInput, I as useFieldMetaDiagnostic, L as fieldDescription, N as MetaPlaceholder, O as TableHead, P as ResolvedFieldDisplay, R as lowerLegacyField, S as SelectTrigger, T as TableBody, _ as SelectItem, h as SelectContent, j as Badge, k as TableHeader, m as Select, t as FilterBuilder, w as Table$1 } from "./filter-builder-DaWHUZuV.js";
10
10
  import { t as Label } from "./label-66W1qbke.js";
11
- import { BULK_EACH_LIMIT, FORM_SLOT, LAYOUT_SIZES, builtinFrames, canonicalEntityNodeKey, compileFlow, compiledScreenFor, composeListFilter, declaredField, declaredText, dtoFields, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, exportFormatFieldKey, exportFormatLabelKey, exportFormatOptionKey, facts, fieldOptionText, fieldSensitivity, formatEntityRef, grantedPlacementKeys, hasRecordPage, humanizeKey, i18nPrefixToRef, i18nPrefixToRef as i18nPrefixToRef$1, inlineFlow, isBuiltinItemAction, isDataError, isLinkField, isRelationField, isScreenFailure, linkOf, normalizeEntityRef, planItemActions, projectDto, recordEntries, relatedList, relatedRecord, resolveActionDensity, resolveActionSurfaceMode, resolveContentSlot, resolveEnumLabel, resolveFieldLabel, resolveFieldOptionLabel, resolveFieldSlot, resolveNavLabel, resolveResourceLabel, resolveStaticAvailability, resolveTextSlot, resourceKey, resourceShowHref, savedViewKey, validatePlacementProps, whereEntryToFilter } from "@burojs/core";
12
- import { DismissGuardScope, FlowRowBatchContext, LOCAL_USER_SCOPE_ID, QUICK_ANCHOR_ATTRIBUTE, ResourceListProvider, RouteSourceContext, ScreenStateProvider, implementFrame, policyRecordOf, policyRowsOf, resolveColumnField, resolveRoute, resolveValidationMessage, resourceFlowsOn, useApp, useApplicationSnapshot, useAvailability, useAvailabilityResolver, useCapability, useCapabilityResolver, useComponent, useCtx, useCurrentUserId, useCustomQuery, useDataRuntime, useDismissGuard, useEntryGate, useEnumValues, useExportRuns, useFieldAccess, useFieldImplementation, useFieldMeta, useFieldMetaSources, useFlowRowBatch, useFlowRowResolver, useFormController, useFrameImplementations, useFramePiece, useFrameT, useI18nCaps, useItemActions, useListAccess, useLocale, useNavigationBlocker, useOne, useOptionalDataRuntime, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, usePageFlowGate, usePageScreen, usePathAccess, usePlacement, useProjectedRecord, useProjectedTitle, usePublishDocVariant, useQuick, useQuickEntry, useQuickOpener, useRecordFrame, useRecordTitle, useResource, useResourceExport, useResourceList, useResourceListContext, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow } from "@burojs/react";
13
- import { IconAlertCircle, IconAlertTriangle, IconArrowDown, IconArrowLeft, IconArrowRight, IconArrowUp, IconBookmark, IconCheck, IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconColumns3, IconCopy, IconDots, IconDownload, IconEyeOff, IconFilterX, IconInbox, IconLoader2, IconLock, IconMinus, IconPencil, IconPinned, IconPinnedOff, IconPlus, IconRefresh, IconSelector, IconSettings2, IconTrash, IconX } from "@tabler/icons-react";
11
+ import { n as useCandidateSelection, t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-CAScLPb2.js";
12
+ import { t as useRelationSelect } from "./relation-select-B8Fq4o-t.js";
13
+ import { BULK_EACH_LIMIT, FORM_SLOT, LAYOUT_SIZES, builtinFrames, canonicalEntityNodeKey, columnName, compileFlow, compiledScreenFor, declaredField, declaredText, displayClassOf, dtoFields, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, exportFormatFieldKey, exportFormatLabelKey, exportFormatOptionKey, facts, fieldOptionText, fieldSensitivity, flowRecordOf, formatEntityRef, grantedPlacementKeys, hasRecordPage, humanizeKey, i18nPrefixToRef, i18nPrefixToRef as i18nPrefixToRef$1, inlineFlow, isBuiltinItemAction, isColumnSortable, isRelationField, isScreenFailure, linkOf, normalizeDataError, normalizeEntityRef, planItemActions, projectDto, recordEntries, relatedList, relatedRecord, resolveActionDensity, resolveActionSurfaceMode, resolveColumnUi, resolveContentSlot, resolveEnumLabel, resolveFieldLabel, resolveFieldOptionLabel, resolveFieldSlot, resolveNavLabel, resolveResourceLabel, resolveStaticAvailability, resolveSurfaceUi, resolveTextSlot, resourceKey, resourceShowHref, reverseLinkOf, routeResourceKey, routeServerIssues, savedViewKey, validatePlacementProps, whereEntryToFilter } from "@burojs/core";
14
+ import { ActionRouteContext, DismissGuardScope, FlowRowBatchContext, LOCAL_USER_SCOPE_ID, QUICK_ANCHOR_ATTRIBUTE, RECORD_KIND_KEYS, RecordScopeChain, ResourceListProvider, RouteSourceContext, ScreenStateProvider, columnTypeWidth, implementFrame, kindErrorText, linkCapabilitiesOf, policyRecordOf, policyRowsOf, recordKeyOf, resolveColumnField, resolveErrorText, resolveRoute, resolveValidationMessage, resourceFlowsOn, specificErrorText, tableLayoutSetting, useApp, useApplicationSnapshot, useAvailability, useAvailabilityResolver, useCapability, useCapabilityResolver, useChromeT, useClipboard, useComponent, useCtx, useCurrentUserId, useCustomQuery, useDataRuntime, useDismissGuard, useEntryGate, useEnumValues, useExportRuns, useFieldAccess, useFieldImplementation, useFieldMeta, useFieldMetaSources, useFlowRowBatch, useFlowRowResolver, useFormController, useFrameImplementations, useFramePiece, useFrameT, useI18nCaps, useItemActions, useLinkRights, useLinkWriter, useLinkedKeys, useListAccess, useLocale, useNavigationBlocker, useOne, useOptionalDataRuntime, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, useOptionalUiSettings, usePageFlowGate, usePageScreen, usePathAccess, usePlacement, useProjectedRecord, useProjectedTitle, usePublishDocVariant, useQuick, useQuickEntry, useQuickOpener, useRecordFrame, useRecordScopeFrames, useRecordTitle, useResource, useResourceExport, useResourceList, useResourceListContext, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow } from "@burojs/react";
15
+ import { IconAlertCircle, IconAlertTriangle, IconArrowDown, IconArrowLeft, IconArrowRight, IconArrowUp, IconBookmark, IconCheck, IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconColumns3, IconCopy, IconDots, IconDownload, IconEyeOff, IconFilterX, IconInbox, IconLink, IconLoader2, IconLock, IconMinus, IconPencil, IconPinned, IconPinnedOff, IconPlus, IconRefresh, IconSelector, IconSettings2, IconTrash, IconUnlink, IconX } from "@tabler/icons-react";
14
16
  import * as React from "react";
15
17
  import { Component, Fragment, Suspense, createContext, createElement, lazy, memo, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
16
18
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
17
- import { isFlowAction } from "@burojs/core/internal";
19
+ import { actionWhenHolds, enumBindingKey, hasNullRef, isFlowAction, whereToFilter } from "@burojs/core/internal";
18
20
  import * as TabsPrimitive from "@radix-ui/react-tabs";
19
21
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
20
22
  import { flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, useReactTable } from "@tanstack/react-table";
@@ -117,428 +119,91 @@ function toDisplayText(value) {
117
119
  }
118
120
  }
119
121
  //#endregion
120
- //#region src/fields/legacy-field.ts
121
- const STRING_ITEM = Object.freeze({ type: "string" });
122
- const LEGACY_KIND_DECLARATIONS = Object.freeze({
123
- string: { type: "string" },
124
- text: {
125
- type: "string",
126
- format: "text"
127
- },
128
- textarea: {
129
- type: "string",
130
- format: "textarea"
131
- },
132
- code: {
133
- type: "string",
134
- format: "code"
135
- },
136
- email: {
137
- type: "string",
138
- format: "email"
139
- },
140
- url: {
141
- type: "string",
142
- format: "url"
143
- },
144
- uuid: {
145
- type: "string",
146
- format: "uuid"
147
- },
148
- number: { type: "number" },
149
- boolean: { type: "boolean" },
150
- date: {
151
- type: "string",
152
- format: "date"
153
- },
154
- dateTime: {
155
- type: "string",
156
- format: "date-time"
157
- },
158
- enum: {
159
- type: "string",
160
- format: "enum"
161
- },
162
- json: {
163
- type: "object",
164
- format: "json"
165
- },
166
- richText: {
167
- type: "string",
168
- format: "rich-text"
169
- },
170
- file: {
171
- type: "array",
172
- format: "file",
173
- items: STRING_ITEM
174
- },
175
- relation: { type: "relation" },
176
- relationMany: {
177
- type: "array",
178
- format: "relation",
179
- items: STRING_ITEM
180
- }
181
- });
182
- const SEMANTIC_KEYS = Object.freeze([
183
- "type",
184
- "format",
185
- "meta",
186
- "required",
187
- "nullable",
188
- "empty",
189
- "default",
190
- "constraints",
191
- "texts",
192
- "options",
193
- "items",
194
- "properties",
195
- "additionalProperties",
196
- "variants"
197
- ]);
198
- function ownData(value, key) {
199
- if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
200
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
201
- if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
202
- return {
203
- present: true,
204
- value: descriptor.value
205
- };
206
- }
207
- function ownString(value, key) {
208
- const candidate = ownData(value, key).value;
209
- return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
210
- }
211
- function projectUiSelection(input) {
212
- const selection = {};
213
- const inputId = ownString(input, "input");
214
- const displayId = ownString(input, "display");
215
- if (inputId !== void 0) selection.input = inputId;
216
- if (displayId !== void 0) selection.display = displayId;
217
- return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
218
- }
219
- function projectFieldDeclaration(input) {
220
- const type = ownString(input, "type");
221
- if (type === void 0) return void 0;
222
- const projected = { type };
223
- for (const key of SEMANTIC_KEYS) {
224
- if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
225
- const candidate = ownData(input, key);
226
- if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
227
- }
228
- const ui = projectUiSelection(ownData(input, "ui").value);
229
- if (ui !== void 0) projected.ui = ui;
230
- if (type === "array") {
231
- const item = projectFieldDeclaration(ownData(input, "items").value);
232
- if (item !== void 0) projected.items = item;
233
- }
234
- if (type === "object") {
235
- const properties = ownData(input, "properties").value;
236
- if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
237
- const output = {};
238
- for (const key of Object.keys(properties)) {
239
- const child = projectFieldDeclaration(ownData(properties, key).value);
240
- if (child !== void 0) output[key] = child;
241
- }
242
- projected.properties = Object.freeze(output);
243
- }
244
- }
245
- if (type === "union") {
246
- const variants = ownData(input, "variants").value;
247
- if (Array.isArray(variants)) {
248
- const output = [];
249
- for (let index = 0; index < variants.length; index += 1) {
250
- const child = projectFieldDeclaration(ownData(variants, index).value);
251
- if (child !== void 0) output.push(child);
252
- }
253
- projected.variants = Object.freeze(output);
254
- }
255
- }
256
- return Object.freeze(projected);
257
- }
258
- function projectUiHints(input) {
259
- if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
260
- const projected = {};
261
- for (const key of Object.keys(input)) {
262
- const candidate = ownData(input, key);
263
- if (candidate.present) projected[key] = candidate.value;
264
- }
265
- return Object.freeze(projected);
266
- }
267
- function lowerLegacyField(field) {
268
- const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
269
- if (declaration !== void 0) return declaration;
270
- const direct = projectFieldDeclaration(field);
271
- const kind = ownString(field, "kind");
272
- const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
273
- if (direct !== void 0) {
274
- const format = direct.format ?? mapping?.format ?? kind;
275
- return format === void 0 ? direct : Object.freeze({
276
- ...direct,
277
- format
278
- });
279
- }
280
- const ui = projectUiHints(ownData(field, "ui").value);
281
- const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
282
- const format = ownString(field, "format") ?? mapping?.format ?? kind;
283
- const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
284
- const meta = ownData(field, "meta");
285
- const texts = ownData(field, "texts");
286
- const required = ownData(field, "required");
287
- const nullable = ownData(field, "nullable");
288
- const opts = ownData(field, "opts").value;
289
- const optionRequired = ownData(opts, "required");
290
- const optionNullable = ownData(opts, "nullable");
291
- const requiredValue = required.present ? required.value : optionRequired.value;
292
- const nullableValue = nullable.present ? nullable.value : optionNullable.value;
293
- return Object.freeze({
294
- type,
295
- ...format === void 0 ? {} : { format },
296
- ...items === void 0 ? {} : { items },
297
- ...meta.present && meta.value !== void 0 ? { meta: meta.value } : {},
298
- ...texts.present && texts.value !== void 0 ? { texts: texts.value } : {},
299
- ...requiredValue === true ? { required: true } : {},
300
- ...nullableValue === true ? { nullable: true } : {}
301
- });
302
- }
303
- function lowerLegacyFieldSource(field) {
304
- const declaration = lowerLegacyField(field);
305
- const ui = projectUiHints(ownData(field, "ui").value);
306
- if (ui === void 0) return declaration;
307
- return Object.freeze({
308
- ...declaration,
309
- ui
310
- });
311
- }
312
- function fieldDescription(field) {
313
- const declaration = field?.declaration;
314
- return declaredText("field", declaration, "description") ?? declaredText("field", field, "description");
122
+ //#region src/data-table/annotate-if-clipped.ts
123
+ function annotateIfClipped(e) {
124
+ const el = e.currentTarget;
125
+ const text = el.scrollWidth > el.clientWidth || Array.from(el.querySelectorAll("*")).some((c) => c.scrollWidth > c.clientWidth) ? (el.textContent ?? "").trim() : "";
126
+ if (text) el.setAttribute("title", text);
127
+ else el.removeAttribute("title");
315
128
  }
316
129
  //#endregion
317
- //#region src/fields/resolved-field.tsx
318
- const EMPTY = Object.freeze({});
319
- const EMPTY_ISSUES$1 = Object.freeze([]);
320
- const EMPTY_OPTIONS = Object.freeze([]);
321
- function canonical(field) {
322
- return lowerLegacyField(field);
323
- }
324
- const warnedObjectLabels = new Set();
325
- function optionsFor(field, name) {
326
- if (Array.isArray(field.options)) return field.options;
327
- const candidate = field.meta?.options;
328
- if (!Array.isArray(candidate)) return EMPTY_OPTIONS;
329
- const output = [];
330
- for (const item of candidate) {
331
- if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
332
- const raw = item;
333
- if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
334
- const { value, label, description, ...legacyMeta } = raw;
335
- if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
336
- warnedObjectLabels.add(`${name}:${value}`);
337
- console.warn(`[buro/ui-shadcn] field '${name}': option '${value}' has a non-string label, so it renders its raw value. Declare a plain string label and put the translations under fields.${name}.options.${value} (see docs/i18n-guide.md).`);
338
- }
339
- output.push(Object.freeze({
340
- value,
341
- label: typeof label === "string" ? label : String(value),
342
- ...typeof description === "string" ? { description } : {},
343
- ...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
344
- }));
345
- }
346
- return Object.freeze(output);
347
- }
348
- const readScope = (ctx) => ctx.scope;
349
- function useFieldRuntimeContext(resource, record, values, metaSources) {
350
- const scope = useCtx(readScope);
351
- const locale = useLocale();
352
- const caps = useI18nCaps(formatEntityRef(resource));
353
- const metaSourcesOut = useFieldMetaSources({
354
- record,
355
- values,
356
- extra: metaSources
357
- });
358
- return useMemo(() => Object.freeze({
359
- i18n: Object.freeze({
360
- locale,
361
- has: caps.has,
362
- t: caps.t
363
- }),
364
- sources: Object.freeze({
365
- record: record ?? EMPTY,
366
- values: values ?? EMPTY,
367
- scope: scope ?? EMPTY
368
- }),
369
- metaSources: metaSourcesOut
370
- }), [
371
- caps,
372
- locale,
373
- record,
374
- scope,
375
- values,
376
- metaSourcesOut
377
- ]);
378
- }
379
- function useFieldMetaDiagnostic(state, resource, name, field, surface) {
380
- const reported = useRef(void 0);
381
- const reason = state.status === "failed" ? state.reason : void 0;
382
- useEffect(() => {
383
- if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
384
- reported.current = reason;
385
- const named = field.ui?.[surface];
386
- const implementation = typeof named === "string" ? named : field.format ?? field.type;
387
- console.error(`[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`);
388
- }, [
389
- reason,
390
- resource,
391
- name,
392
- field,
393
- surface
394
- ]);
395
- }
396
- function MetaPlaceholder({ state }) {
130
+ //#region src/fields/field-value-frame.tsx
131
+ const COPY_CLASSES = [
132
+ "string",
133
+ "number",
134
+ "date"
135
+ ];
136
+ const COPIED_MS = 2e3;
137
+ const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
138
+ const isEmpty = (value) => value === null || value === void 0 || value === "";
139
+ function copyText(value) {
140
+ if (value instanceof Date) return value.toISOString();
141
+ return typeof value === "string" ? value : String(value);
142
+ }
143
+ function FieldValueFrame({ field, value, surface, entry, children }) {
397
144
  const t = useOptionalT();
398
- if (state.status === "pending") return jsx(Skeleton, {
399
- "data-buro-skeleton": "",
400
- "data-buro-field-meta": "pending",
401
- className: "h-9 w-full"
402
- });
403
- return jsx("div", {
404
- role: "alert",
405
- "data-buro-field-meta": "failed",
406
- className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
407
- children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
408
- });
409
- }
410
- function useFieldImplementationDiagnostic(diagnostic) {
411
- const reported = useRef(void 0);
145
+ const clipboard = useClipboard();
146
+ const [copied, setCopied] = useState(false);
412
147
  useEffect(() => {
413
- if (diagnostic === void 0 || reported.current === diagnostic || process.env.NODE_ENV === "production") return;
414
- reported.current = diagnostic;
415
- console.warn("[buro/ui-shadcn] missing-field-implementation; rendering fallback.", diagnostic);
416
- }, [diagnostic]);
417
- }
418
- function ResolvedFieldDisplay({ resource, name, field: source, value, record, testId, fallback, presentation, metaSources }) {
419
- const sourceAtEdge = lowerLegacyFieldSource(source);
420
- const field = canonical(sourceAtEdge);
421
- const resolved = useFieldImplementation({
422
- field,
423
- surface: "display"
424
- });
425
- const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
426
- const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources);
427
- useFieldMetaDiagnostic(metaState, resource, name, field, "display");
428
- useFieldImplementationDiagnostic(resolved.diagnostic);
429
- if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
430
- if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
431
- const Component = resolved.Component;
432
- const context = {
433
- owner: resource,
434
- resource,
148
+ if (!copied) return;
149
+ const timer = setTimeout(() => setCopied(false), COPIED_MS);
150
+ return () => clearTimeout(timer);
151
+ }, [copied]);
152
+ const ui = isRecord$1(field.ui) ? field.ui : void 0;
153
+ const asksCopy = ui?.copy === true;
154
+ const valueClass = useMemo(() => asksCopy ? displayClassOf({
155
+ ...lowerLegacyField(field),
156
+ ...ui === void 0 ? {} : { ui }
157
+ }) : void 0, [
158
+ asksCopy,
435
159
  field,
436
- path: Object.freeze([name]),
437
- surface: "display",
438
- presentation: presentation ?? "default",
439
- record,
440
- meta: metaState.meta,
441
- ui: sourceAtEdge.ui ?? EMPTY,
442
- options: optionsFor(field, name),
443
- i18n: runtimeContext.i18n,
444
- sources: runtimeContext.sources,
445
- pending: false,
446
- touched: false,
447
- issues: EMPTY_ISSUES$1,
448
- accessibility: EMPTY,
449
- testIds: testId === void 0 ? EMPTY : Object.freeze({ display: testId })
450
- };
451
- if (resolved.multiple) return createElement(Component, {
452
- ...context,
453
- multiple: true,
454
- value: Array.isArray(value) ? value : []
455
- });
456
- return createElement(Component, {
457
- ...context,
458
- multiple: false,
459
- value
460
- });
461
- }
462
- function ResolvedFieldInput({ resource, name, field: source, value, record, form, testId, inputId, onChange, onBlur, disabled, required, placeholder, descriptionId, errorId, errorMessage, pending = false, touched = errorMessage != null, issues: suppliedIssues, relationSeeds, fixedFilters, metaSources, fallback }) {
463
- const sourceAtEdge = lowerLegacyFieldSource(source);
464
- const base = canonical(sourceAtEdge);
465
- const field = required === void 0 || required === base.required ? base : Object.freeze({
466
- ...base,
467
- required
468
- });
469
- const ui = Object.freeze({
470
- ...sourceAtEdge.ui ?? EMPTY,
471
- ...placeholder === void 0 ? {} : { placeholder }
472
- });
473
- const resolved = useFieldImplementation({
474
- field: {
475
- ...field,
476
- ui
477
- },
478
- surface: "input"
160
+ ui
161
+ ]);
162
+ const { overflow: declared } = resolveSurfaceUi(field, surface, entry);
163
+ const copyable = asksCopy && clipboard !== void 0 && !isEmpty(value) && valueClass !== void 0 && COPY_CLASSES.includes(valueClass);
164
+ if (!copyable && declared === void 0) return jsx(Fragment$1, { children });
165
+ const body = (declared ?? (surface === "column" ? "truncate" : "wrap")) === "wrap" ? jsx("span", {
166
+ className: "min-w-0 whitespace-normal [overflow-wrap:anywhere]",
167
+ "data-buro-overflow": "wrap",
168
+ children
169
+ }) : jsx("span", {
170
+ className: "inline-block min-w-0 max-w-full truncate align-bottom",
171
+ "data-buro-overflow": "truncate",
172
+ onPointerEnter: annotateIfClipped,
173
+ children
479
174
  });
480
- const runtimeContext = useFieldRuntimeContext(resource, record, form, metaSources);
481
- const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources);
482
- useFieldMetaDiagnostic(metaState, resource, name, field, "input");
483
- useFieldImplementationDiagnostic(resolved.diagnostic);
484
- if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
485
- if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
486
- const Component = resolved.Component;
487
- const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES$1 : Object.freeze([Object.freeze({
488
- code: "ui.field-error",
489
- message: {
490
- key: "ui.field-error",
491
- default: errorMessage
492
- },
493
- path: Object.freeze([]),
494
- source: "field",
495
- severity: "error"
496
- })]));
497
- const context = {
498
- owner: resource,
499
- resource,
500
- field,
501
- path: Object.freeze([name]),
502
- surface: "input",
503
- presentation: "default",
504
- record,
505
- form,
506
- meta: metaState.meta,
507
- ui,
508
- options: optionsFor(field, name),
509
- i18n: runtimeContext.i18n,
510
- sources: runtimeContext.sources,
511
- pending,
512
- touched,
513
- issues,
514
- accessibility: Object.freeze({
515
- input: inputId ?? name,
516
- description: descriptionId,
517
- error: errorId
518
- }),
519
- testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
175
+ if (!copyable) return jsx(Fragment$1, { children: body });
176
+ const label = t("field.copy", { defaultValue: "Copy" });
177
+ const copy = (event) => {
178
+ event.preventDefault();
179
+ event.stopPropagation();
180
+ clipboard(copyText(value)).then(() => setCopied(true), () => void 0);
520
181
  };
521
- if (resolved.multiple) return createElement(Component, {
522
- ...context,
523
- multiple: true,
524
- value: Array.isArray(value) ? value : [],
525
- onChange,
526
- onBlur,
527
- disabled,
528
- relationSeeds,
529
- fixedFilters,
530
- metaSources: runtimeContext.metaSources
531
- });
532
- return createElement(Component, {
533
- ...context,
534
- multiple: false,
535
- value,
536
- onChange,
537
- onBlur,
538
- disabled,
539
- relationSeeds,
540
- fixedFilters,
541
- metaSources: runtimeContext.metaSources
182
+ return jsxs("span", {
183
+ className: "inline-flex min-w-0 max-w-full items-center gap-1 align-bottom",
184
+ children: [
185
+ body,
186
+ jsx("button", {
187
+ type: "button",
188
+ "data-testid": `field-copy:${field.name}`,
189
+ "aria-label": label,
190
+ title: label,
191
+ onClick: copy,
192
+ className: "inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
193
+ children: copied ? jsx(IconCheck, {
194
+ "aria-hidden": "true",
195
+ className: "size-3.5"
196
+ }) : jsx(IconCopy, {
197
+ "aria-hidden": "true",
198
+ className: "size-3.5"
199
+ })
200
+ }),
201
+ jsx("span", {
202
+ "aria-live": "polite",
203
+ className: "sr-only",
204
+ children: copied ? t("field.copied", { defaultValue: "Copied" }) : ""
205
+ })
206
+ ]
542
207
  });
543
208
  }
544
209
  //#endregion
@@ -578,16 +243,26 @@ function Field({ resource, i18nPrefix, fallback, fixedFilters, field, doc, ...re
578
243
  });
579
244
  }
580
245
  function FieldDisplay({ resource, i18nPrefix, fallback, value, ...rest }) {
581
- return jsx(ResolvedFieldDisplay, {
582
- ...rest,
246
+ const frameField = useMemo(() => ({
247
+ ...rest.field,
248
+ name: rest.name
249
+ }), [rest.field, rest.name]);
250
+ return jsx(FieldValueFrame, {
251
+ field: frameField,
583
252
  value,
584
- resource: resolveRef(resource, i18nPrefix),
585
- fallback: fallback ?? jsx("span", { children: value == null || value === "" ? "—" : String(value) })
253
+ surface: "display",
254
+ children: jsx(ResolvedFieldDisplay, {
255
+ ...rest,
256
+ value,
257
+ resource: resolveRef(resource, i18nPrefix),
258
+ fallback: fallback ?? jsx("span", { children: value == null || value === "" ? "—" : String(value) })
259
+ })
586
260
  });
587
261
  }
588
262
  //#endregion
589
263
  //#region src/screen/field-leaf.tsx
590
264
  const SIZES = new Set(LAYOUT_SIZES);
265
+ const SHOWN_ISSUES = 3;
591
266
  const FOCUSABLE = "input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])";
592
267
  function useLeafSize(field) {
593
268
  const implementation = useFieldImplementation({
@@ -633,7 +308,7 @@ function MisconfiguredLeaf({ node }) {
633
308
  values: controller.values,
634
309
  extra: controller.sources
635
310
  });
636
- const state = useFieldMeta(field, resolved.implementation, sources);
311
+ const state = useFieldMeta(field, resolved.implementation, sources, resolved.params);
637
312
  useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
638
313
  const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
639
314
  const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
@@ -665,10 +340,7 @@ function RecordLeaf({ node }) {
665
340
  const t = useOptionalT();
666
341
  if (node.restricted === true) {
667
342
  const rawReason = node.restrictedReason;
668
- const reason = rawReason === void 0 || rawReason === "" ? void 0 : resolveContentSlot(caps, [rawReason], {
669
- resource,
670
- default: rawReason
671
- });
343
+ const reason = rawReason === void 0 || rawReason === "" ? void 0 : reasonText(caps, rawReason, resource);
672
344
  const restrictedId = tid.formFieldRestricted(resource, mode, name);
673
345
  return jsxs("div", {
674
346
  ...attributes,
@@ -731,10 +403,7 @@ function RecordLeaf({ node }) {
731
403
  });
732
404
  if (node.editable !== true) {
733
405
  const rawReason = node.lockReason;
734
- const reason = rawReason === void 0 ? void 0 : resolveContentSlot(caps, [rawReason], {
735
- resource,
736
- default: rawReason
737
- });
406
+ const reason = rawReason === void 0 ? void 0 : reasonText(caps, rawReason, resource);
738
407
  const locked = tid.formFieldLocked(resource, mode, name);
739
408
  return jsxs("div", {
740
409
  ...attributes,
@@ -786,7 +455,6 @@ function RecordLeaf({ node }) {
786
455
  function ActiveLeaf({ node, binding, label, attributes, inputId }) {
787
456
  const { resource, mode, controller, disabled: locked } = useScreenTree();
788
457
  const caps = useI18nCaps(resource);
789
- const t = useOptionalT();
790
458
  const tid = useTestIds();
791
459
  const name = binding.name;
792
460
  const record = controller.record;
@@ -802,24 +470,21 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
802
470
  const messageId = `${mode}-${name}-msg`;
803
471
  const placeholder = resolveFieldSlot(caps, name, "placeholder");
804
472
  const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
473
+ const chromeT = useChromeT();
805
474
  const fieldKey = `resources.${resource}.fields.${name}`;
806
- const errorText = binding.error === void 0 ? null : resolveValidationMessage({
807
- code: binding.error.code ?? "",
808
- message: binding.error.message,
809
- ...binding.error.params === void 0 ? {} : { params: binding.error.params }
810
- }, {
811
- t,
812
- fieldKey
813
- });
814
- const warning = errorText === null ? binding.warnings[0] : void 0;
815
- const warningText = warning === void 0 ? null : resolveValidationMessage({
816
- code: warning.code ?? "",
817
- message: warning.message,
818
- ...warning.params === void 0 ? {} : { params: warning.params }
475
+ const text = (issue) => resolveValidationMessage({
476
+ code: issue.code ?? "",
477
+ message: issue.message,
478
+ ...issue.check === void 0 ? {} : { check: issue.check }
819
479
  }, {
820
- t,
480
+ has: caps.has,
481
+ t: caps.t,
821
482
  fieldKey
822
483
  });
484
+ const shown = binding.issues.slice(0, SHOWN_ISSUES);
485
+ const more = binding.issues.length - shown.length;
486
+ const firstError = shown.find((issue) => issue.severity === "error");
487
+ const errorText = firstError === void 0 ? null : text(firstError);
823
488
  const disabled = binding.disabled || controller.isSubmitting || locked === true;
824
489
  const describedBy = errorText === null && hint ? messageId : void 0;
825
490
  const errorMessageId = errorText === null ? void 0 : messageId;
@@ -833,6 +498,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
833
498
  "data-buro-region": "field-label",
834
499
  className: "flex items-center gap-2",
835
500
  children: [jsx(Label, {
501
+ id: `${inputId}-label`,
836
502
  htmlFor: inputId,
837
503
  required: binding.required,
838
504
  children: label
@@ -848,6 +514,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
848
514
  resource: binding.resource,
849
515
  name,
850
516
  inputId,
517
+ labelId: `${inputId}-label`,
851
518
  field: binding.field,
852
519
  value: binding.value,
853
520
  onChange: binding.onChange,
@@ -861,8 +528,10 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
861
528
  ...describedBy === void 0 ? {} : { descriptionId: describedBy },
862
529
  ...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
863
530
  errorMessage: errorText,
531
+ issues: binding.issues,
864
532
  ...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
865
533
  ...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
534
+ ...binding.link === void 0 ? {} : { link: binding.link },
866
535
  fallback: jsx(Input, {
867
536
  id: inputId,
868
537
  name: inputId,
@@ -877,18 +546,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
877
546
  ...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
878
547
  onChange: (event) => binding.onChange(event.target.value)
879
548
  })
880
- }), errorText !== null ? jsx("p", {
881
- id: messageId,
882
- "aria-live": "polite",
883
- className: "text-sm text-destructive",
884
- "data-testid": tid.formFieldError(resource, mode, name),
885
- children: errorText
886
- }) : warningText !== null ? jsx("p", {
549
+ }), shown.length > 0 ? jsxs("div", {
887
550
  id: messageId,
888
551
  "aria-live": "polite",
889
- className: "text-sm text-warning-emphasis",
890
- "data-testid": `form-field-warning:${resource}:${mode}:${name}`,
891
- children: warningText
552
+ className: cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
553
+ "data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
554
+ children: [shown.map((issue, index) => jsx("p", {
555
+ "data-buro-severity": issue.severity,
556
+ className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
557
+ children: text(issue)
558
+ }, index)), more > 0 ? jsx("p", {
559
+ className: "text-muted-foreground",
560
+ "data-testid": `form-field-more:${resource}:${mode}:${name}`,
561
+ children: chromeT("validation.moreIssues", {
562
+ count: more,
563
+ defaultValue: "+{{count}} more"
564
+ })
565
+ }) : null]
892
566
  }) : hint ? jsx("p", {
893
567
  id: messageId,
894
568
  "data-testid": tid.formFieldHelp(resource, mode, name),
@@ -908,7 +582,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
908
582
  const docRef = i18nPrefixToRef(prefix);
909
583
  const docScope = docRef.namespace === "default" ? docRef.id : formatEntityRef(docRef);
910
584
  const caps = useI18nCaps(docScope);
911
- const t = useOptionalT();
585
+ const issueCaps = useI18nCaps();
912
586
  const uid = useId();
913
587
  const root = useRef(null);
914
588
  const inputId = `${inputIds ?? uid}-${name}`;
@@ -962,9 +636,10 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
962
636
  const text = (issue) => resolveValidationMessage({
963
637
  code: issue.code ?? "",
964
638
  message: issue.message,
965
- ...issue.params === void 0 ? {} : { params: issue.params }
639
+ ...issue.check === void 0 ? {} : { check: issue.check }
966
640
  }, {
967
- t,
641
+ has: issueCaps.has,
642
+ t: issueCaps.t,
968
643
  fieldKey
969
644
  });
970
645
  const defaultNode = jsx(Field, {
@@ -972,6 +647,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
972
647
  field: binding.field,
973
648
  metaSources: controller.sources,
974
649
  inputId,
650
+ labelId: `${inputId}-label`,
975
651
  i18nPrefix: prefix,
976
652
  disabled: disabled === true || binding.disabled
977
653
  });
@@ -987,6 +663,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
987
663
  "data-buro-region": "field-label",
988
664
  className: "flex items-center gap-2",
989
665
  children: [jsx(Label, {
666
+ id: `${inputId}-label`,
990
667
  htmlFor: inputId,
991
668
  required: binding.required,
992
669
  children: label
@@ -1027,9 +704,10 @@ function Notice({ children }) {
1027
704
  children
1028
705
  });
1029
706
  }
1030
- function FactsView({ props }) {
707
+ function FactsView({ props, empty }) {
1031
708
  const own = props;
1032
709
  const t = useOptionalT();
710
+ const chromeT = useChromeT();
1033
711
  const snapshot = useApplicationSnapshot();
1034
712
  const queryId = formatEntityRef(own.query);
1035
713
  const caps = useI18nCaps();
@@ -1046,7 +724,10 @@ function FactsView({ props }) {
1046
724
  kind: "custom-query",
1047
725
  ref: own.query
1048
726
  }), [own.query]);
1049
- const result = useCustomQuery(handle, blocked ? {} : input, { enabled: !blocked });
727
+ const result = useCustomQuery(handle, blocked ? {} : input, {
728
+ enabled: !blocked,
729
+ ...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
730
+ });
1050
731
  const shown = useMemo(() => {
1051
732
  const byName = new Map(fields.map((field) => [field.name, field]));
1052
733
  return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
@@ -1054,7 +735,7 @@ function FactsView({ props }) {
1054
735
  return field === void 0 ? [] : [field];
1055
736
  });
1056
737
  }, [fields, own.rows]);
1057
- if (blocked) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
738
+ if (blocked) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1058
739
  if (result.isError) return jsxs("div", {
1059
740
  role: "alert",
1060
741
  className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
@@ -1063,7 +744,7 @@ function FactsView({ props }) {
1063
744
  variant: "outline",
1064
745
  size: "sm",
1065
746
  onClick: () => void result.refetch(),
1066
- children: t("actions.retry", { defaultValue: "Retry" })
747
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
1067
748
  })]
1068
749
  });
1069
750
  if (result.data === void 0) return own.view === "list" ? jsx(Skeleton, {
@@ -1077,7 +758,7 @@ function FactsView({ props }) {
1077
758
  }, field.name))
1078
759
  });
1079
760
  const data = result.data;
1080
- if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
761
+ if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1081
762
  const pairs = shown.map((field) => {
1082
763
  const value = data[field.name];
1083
764
  const label = resolveContentSlot(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
@@ -1087,13 +768,18 @@ function FactsView({ props }) {
1087
768
  const body = value === null || value === void 0 ? jsx("span", {
1088
769
  className: "text-muted-foreground",
1089
770
  children: "—"
1090
- }) : jsx(ResolvedFieldDisplay, {
1091
- resource: own.query,
1092
- name: field.name,
771
+ }) : jsx(FieldValueFrame, {
1093
772
  field,
1094
773
  value,
1095
- record: data,
1096
- fallback: toDisplayText(value).text
774
+ surface: "display",
775
+ children: jsx(ResolvedFieldDisplay, {
776
+ resource: own.query,
777
+ name: field.name,
778
+ field,
779
+ value,
780
+ record: data,
781
+ fallback: toDisplayText(value).text
782
+ })
1097
783
  });
1098
784
  return {
1099
785
  name: field.name,
@@ -1147,6 +833,9 @@ const PlacementFrameContext = createContext(null);
1147
833
  function usePlacementFrame() {
1148
834
  return useContext(PlacementFrameContext);
1149
835
  }
836
+ function placementLayoutId(frame) {
837
+ return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
838
+ }
1150
839
  //#endregion
1151
840
  //#region src/flows/app-flow-button.tsx
1152
841
  function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
@@ -1266,7 +955,7 @@ function RecordInlineFlow({ name }) {
1266
955
  return jsx(InlineHostLoader, {
1267
956
  resource,
1268
957
  name: flowName,
1269
- record: row,
958
+ ...hostRecord(flowRecordOf(compiled, row)),
1270
959
  placementKey: key,
1271
960
  lease: kept,
1272
961
  stale: true,
@@ -1282,7 +971,7 @@ function RecordInlineFlow({ name }) {
1282
971
  if (control.state === "disabled" && kept !== void 0 && kept.dirty) return jsx(InlineHostLoader, {
1283
972
  resource,
1284
973
  name: flowName,
1285
- record: row,
974
+ ...hostRecord(flowRecordOf(compiled, row)),
1286
975
  placementKey: key,
1287
976
  lease: kept,
1288
977
  stale: true,
@@ -1294,11 +983,12 @@ function RecordInlineFlow({ name }) {
1294
983
  return jsx(InlineHostLoader, {
1295
984
  resource,
1296
985
  name: flowName,
1297
- record: control.record ?? row,
986
+ ...hostRecord(flowRecordOf(compiled, control.record ?? row)),
1298
987
  placementKey: key,
1299
988
  ...lease === void 0 ? {} : { lease }
1300
989
  });
1301
990
  }
991
+ const hostRecord = (record) => record === void 0 ? {} : { record };
1302
992
  function InlineHostLoader(props) {
1303
993
  const loaded = useLoaded(loadInlineFlowHost);
1304
994
  if (loaded.error !== void 0) return jsx("p", {
@@ -1514,6 +1204,7 @@ function placementSeeds(where, resolved, closure) {
1514
1204
  where.forEach((entry, index) => {
1515
1205
  const value = resolved[index]?.value;
1516
1206
  if (entry.op !== "eq" || value === null || value === void 0) return;
1207
+ if (typeof entry.field !== "string" || entry.field.includes(".")) return;
1517
1208
  if (resolved[index]?.field !== entry.field) return;
1518
1209
  const source = refSource(entry.value);
1519
1210
  if (source === null ? isLiteral(entry.value) : source === "record" || source === "user" || !UNTRUSTED.includes(source) && Object.hasOwn(closure, source) && !(closure[source] ?? []).some((input) => UNTRUSTED.includes(input))) seeds[entry.field] = value;
@@ -1531,7 +1222,8 @@ function BadgeBody({ icon, label }) {
1531
1222
  }) : null, label]
1532
1223
  });
1533
1224
  }
1534
- function EnumBadge({ enumName, value, testid, className, presentation }) {
1225
+ function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
1226
+ const enumName = enumBindingKey(enumInput) ?? "";
1535
1227
  const app = useApp();
1536
1228
  const caps = useI18nCaps();
1537
1229
  const def = app.registry.enums.get(enumName);
@@ -1677,6 +1369,7 @@ function useExportRunWatcher() {
1677
1369
  const runtime = useDataRuntime();
1678
1370
  const runs = useExportRuns();
1679
1371
  const t = useOptionalT();
1372
+ const caps = useI18nCaps();
1680
1373
  React.useEffect(() => {
1681
1374
  const announced = announcedRunsFor(runtime);
1682
1375
  const unsubscribes = runs.map((run) => {
@@ -1695,7 +1388,7 @@ function useExportRunWatcher() {
1695
1388
  label: t("export.toast.download"),
1696
1389
  onClick: () => downloadArtifact(artifact)
1697
1390
  } });
1698
- } else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: state.error.message } }));
1391
+ } else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: resolveErrorText(state.error, caps) } }));
1699
1392
  };
1700
1393
  check();
1701
1394
  return run.handle.subscribe(check);
@@ -1706,24 +1399,27 @@ function useExportRunWatcher() {
1706
1399
  }, [
1707
1400
  runs,
1708
1401
  runtime,
1709
- t
1402
+ t,
1403
+ caps
1710
1404
  ]);
1711
1405
  }
1712
1406
  //#endregion
1713
1407
  //#region src/actions/action-overflow.tsx
1714
1408
  const RowActionsCollapseContext = createContext(false);
1715
- function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
1716
- const t = useOptionalT();
1409
+ function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
1410
+ const resource = routeResourceKey(resourceInput);
1411
+ const chromeT = useChromeT();
1717
1412
  const tid = useTestIds();
1413
+ const recordKey = String(recordKeyOf(useApp().registry.get(resource), record) ?? "");
1718
1414
  const collapsedRows = useContext(RowActionsCollapseContext);
1719
1415
  const [flowFor, setFlowFor] = useState(null);
1720
- const actions = useShownItemActions(resource, record, surface, hideFlows);
1416
+ const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
1721
1417
  if (actions.length === 0) return null;
1722
1418
  const isRow = surface === "row";
1723
1419
  const folded = isRow && (collapse ?? collapsedRows);
1724
1420
  const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
1725
1421
  const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
1726
- const menuTestId = isRow ? tid.listRowActionsMenu(resource, record.id) : tid.formActionsMenu(resource, surface);
1422
+ const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
1727
1423
  const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
1728
1424
  const overlays = jsx(Fragment$1, { children: flowFor != null ? jsx(RuntimeFlowRunner, {
1729
1425
  resource,
@@ -1737,6 +1433,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1737
1433
  actionName: a.name,
1738
1434
  record,
1739
1435
  scope: surface,
1436
+ ...sources === void 0 ? {} : { sources },
1740
1437
  density: a.density
1741
1438
  }, a.name));
1742
1439
  return jsxs("div", {
@@ -1760,7 +1457,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1760
1457
  size: "icon",
1761
1458
  className: "size-7",
1762
1459
  "data-testid": menuTestId,
1763
- "aria-label": t("actions.more", { defaultValue: "More" }),
1460
+ "aria-label": chromeT("actions.more", { defaultValue: "More" }),
1764
1461
  children: jsx(IconDots, {
1765
1462
  className: "h-4 w-4",
1766
1463
  "aria-hidden": "true"
@@ -1774,15 +1471,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1774
1471
  children: [jsx(IconDots, {
1775
1472
  className: "h-4 w-4",
1776
1473
  "aria-hidden": "true"
1777
- }), t("actions.more", { defaultValue: "More" })]
1474
+ }), chromeT("actions.more", { defaultValue: "More" })]
1778
1475
  })
1779
1476
  }), jsx(DropdownMenuContent, {
1780
1477
  align: "end",
1781
- children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${record.id}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? jsx(ActionButton, {
1478
+ children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${recordKey}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? jsx(ActionButton, {
1782
1479
  resource,
1783
1480
  actionName: a.name,
1784
1481
  record,
1785
1482
  scope: surface,
1483
+ ...sources === void 0 ? {} : { sources },
1786
1484
  asMenuItem: true
1787
1485
  }) : jsx(FlowMenuItem, {
1788
1486
  resource,
@@ -1802,11 +1500,12 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1802
1500
  ]
1803
1501
  });
1804
1502
  }
1805
- function useShownItemActions(resource, record, surface, hideFlows) {
1503
+ function useShownItemActions(resource, record, surface, hideFlows, sources) {
1806
1504
  const resolved = useItemActions({
1807
1505
  resource,
1808
1506
  record,
1809
- surface
1507
+ surface,
1508
+ ...sources === void 0 ? {} : { sources }
1810
1509
  });
1811
1510
  const def = useApp().registry.get(resource);
1812
1511
  const shown = def !== void 0 && isBuiltinItemAction((def.actions?.item)?.find((action) => action.name === "edit"), "edit") && !recordEntries(def).page.edit && recordEntries(def).quick.edit === null ? resolved.filter((a) => a.name !== "edit") : resolved;
@@ -1899,8 +1598,14 @@ function DocumentTitleProvider({ routeKey, children }) {
1899
1598
  });
1900
1599
  }
1901
1600
  //#endregion
1601
+ //#region src/resource/resource-title.ts
1602
+ function resolveResourceTitle(caps, t, resource, slot, params) {
1603
+ return resolveContentSlot(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
1604
+ }
1605
+ //#endregion
1902
1606
  //#region src/resource/page-header.tsx
1903
- function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1607
+ function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1608
+ const resource = routeResourceKey(resourceInput);
1904
1609
  const app = useApp();
1905
1610
  const t = useT();
1906
1611
  const def = app.registry.get(resource);
@@ -1909,10 +1614,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1909
1614
  const resourceSingle = resolveResourceLabel(caps, resource, "single", def);
1910
1615
  const labelSingle = labelOverride ?? resourceSingle;
1911
1616
  const labelPlural = resolveResourceLabel(caps, resource, "plural", def);
1912
- const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? t("pageHeader.editTitleNamed", { params: {
1617
+ const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
1913
1618
  label: labelSingle,
1914
1619
  name: recordName
1915
- } }) : t("pageHeader.editTitle", { params: { label: labelSingle } }) : t("pageHeader.createTitle", { params: { label: labelSingle } }));
1620
+ }) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
1916
1621
  useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
1917
1622
  const showBack = backLink && mode !== "list" && !breadcrumbs;
1918
1623
  const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
@@ -1943,9 +1648,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1943
1648
  ]
1944
1649
  });
1945
1650
  }
1946
- function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1651
+ function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
1652
+ const resource = routeResourceKey(resourceInput);
1947
1653
  const def = useApp().registry.get(resource);
1948
- const recordName = useProjectedTitle(resource, record?.id, record);
1654
+ const recordName = useProjectedTitle(resource, recordKeyOf(def, record), record);
1949
1655
  usePublishDocVariant(resource, record);
1950
1656
  if (!def) return null;
1951
1657
  return jsx(PageHeader, {
@@ -1960,16 +1666,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1960
1666
  }), extraActions] })
1961
1667
  });
1962
1668
  }
1963
- function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
1669
+ function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
1670
+ const resource = routeResourceKey(resourceInput);
1964
1671
  const app = useApp();
1965
1672
  const def = app.registry.get(resource);
1966
1673
  const caps = useI18nCaps(resource);
1967
- const recordName = useProjectedTitle(resource, record?.id, record);
1674
+ const recordKey = recordKeyOf(def, record);
1675
+ const recordName = useProjectedTitle(resource, recordKey, record);
1968
1676
  return jsx(PageHeader, {
1969
1677
  resource,
1970
1678
  mode: "edit",
1971
1679
  recordName,
1972
- backHref: editBackHref(app.registry, resource, String(record.id), backHref),
1680
+ backHref: editBackHref(app.registry, resource, String(recordKey ?? ""), backHref),
1973
1681
  breadcrumbs,
1974
1682
  backLabel: recordName ? `${resolveResourceLabel(caps, resource, "single", def)}: ${recordName}` : void 0,
1975
1683
  actions: jsx(ActionOverflow, {
@@ -2169,7 +1877,8 @@ function ItemNode({ node, row, fallback }) {
2169
1877
  const viewProps = {
2170
1878
  props,
2171
1879
  data: {},
2172
- t: placement.viewProps?.t ?? ((name) => name)
1880
+ t: placement.viewProps?.t ?? ((name) => name),
1881
+ ...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
2173
1882
  };
2174
1883
  return jsx(View, { ...viewProps });
2175
1884
  }
@@ -2251,6 +1960,7 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
2251
1960
  const t = useOptionalT();
2252
1961
  return jsxs("section", {
2253
1962
  ...nodeAttributes,
1963
+ ...title === void 0 ? {} : { "aria-labelledby": titleId },
2254
1964
  children: [
2255
1965
  title === void 0 && info === null && !collapsible ? null : jsxs("div", {
2256
1966
  "data-buro-region": "panel-title",
@@ -2310,7 +2020,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
2310
2020
  }), info]
2311
2021
  }), tabs.map((tab) => jsx(TabsContent, {
2312
2022
  value: tab.key,
2313
- forceMount: true,
2314
2023
  "data-buro-region": "tab-panel",
2315
2024
  "data-buro-key": tab.key,
2316
2025
  children: tab.panel
@@ -2398,17 +2107,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
2398
2107
  //#region src/primitives/checkbox.tsx
2399
2108
  const Checkbox = React.forwardRef(({ className, checked, ...props }, ref) => jsx(CheckboxPrimitive.Root, {
2400
2109
  ref,
2401
- className: cn("peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
2110
+ className: cn("peer inline-flex h-4 w-4 shrink-0 items-center justify-center align-middle rounded-[4px] border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
2402
2111
  checked,
2403
2112
  ...props,
2404
2113
  children: jsx(CheckboxPrimitive.Indicator, {
2405
2114
  className: cn("flex items-center justify-center text-current"),
2406
2115
  children: checked === "indeterminate" ? jsx(IconMinus, {
2407
2116
  "data-icon": "minus",
2408
- className: "h-4 w-4"
2117
+ className: "size-3.5"
2409
2118
  }) : jsx(IconCheck, {
2410
2119
  "data-icon": "check",
2411
- className: "h-4 w-4"
2120
+ className: "size-3.5"
2412
2121
  })
2413
2122
  })
2414
2123
  }));
@@ -2433,6 +2142,17 @@ function useContainerNarrow(maxRem = 40) {
2433
2142
  return [setElement, narrow];
2434
2143
  }
2435
2144
  //#endregion
2145
+ //#region src/data-table/fill-column.ts
2146
+ function fillColumnId(columns) {
2147
+ const eligible = columns.filter((c) => !c.pinned && !c.service);
2148
+ const declared = eligible.find((c) => c.fill);
2149
+ if (declared !== void 0) return declared.id;
2150
+ for (let i = eligible.length - 1; i >= 0; i--) {
2151
+ const c = eligible[i];
2152
+ if (c.resize && c.max === void 0) return c.id;
2153
+ }
2154
+ }
2155
+ //#endregion
2436
2156
  //#region src/data-table/data-table.tsx
2437
2157
  function pinEdgeClass(isLastLeft, isFirstRight) {
2438
2158
  if (isLastLeft) return "after:pointer-events-none after:absolute after:inset-y-0 after:left-full after:z-10 after:w-1.5 after:bg-gradient-to-r after:from-overlay/[0.07] after:to-transparent dark:after:from-overlay/30";
@@ -2444,7 +2164,8 @@ function pinEdgeClassFor(column, overflowing) {
2444
2164
  if (!pinned || !overflowing) return "";
2445
2165
  return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
2446
2166
  }
2447
- function getPinStyles(column, overflowing) {
2167
+ function getPinStyles(column, overflowing, fill = false) {
2168
+ if (fill) return {};
2448
2169
  const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
2449
2170
  const pinned = column.getIsPinned();
2450
2171
  if (!pinned || !overflowing) return explicitWidth;
@@ -2469,21 +2190,35 @@ function columnMetaNoRowNav(meta) {
2469
2190
  function columnMetaNoClip(meta) {
2470
2191
  return meta?.noClip === true;
2471
2192
  }
2472
- function annotateIfClipped(e) {
2473
- const el = e.currentTarget;
2474
- const text = el.scrollWidth > el.clientWidth || Array.from(el.querySelectorAll("*")).some((c) => c.scrollWidth > c.clientWidth) ? (el.textContent ?? "").trim() : "";
2475
- if (text) el.setAttribute("title", text);
2476
- else el.removeAttribute("title");
2193
+ function columnMetaWrap(meta) {
2194
+ return meta?.wrap === true;
2195
+ }
2196
+ function columnMetaFill(meta) {
2197
+ return meta?.fill === true;
2477
2198
  }
2478
- function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, noRowNav }) {
2199
+ function tableFillColumnId(table) {
2200
+ if (table.options.enableColumnResizing !== true) return void 0;
2201
+ return fillColumnId(table.getVisibleLeafColumns().map((c) => {
2202
+ const max = c.columnDef.maxSize;
2203
+ return {
2204
+ id: c.id,
2205
+ fill: columnMetaFill(c.columnDef.meta),
2206
+ resize: c.getCanResize(),
2207
+ ...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
2208
+ pinned: c.getIsPinned() !== false,
2209
+ service: c.id.startsWith("__")
2210
+ };
2211
+ }));
2212
+ }
2213
+ function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
2479
2214
  const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
2480
2215
  const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
2481
- const noClip = columnMetaNoClip(cell.column.columnDef.meta);
2216
+ const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
2482
2217
  return jsx(TableCell, {
2483
2218
  "data-testid": testId,
2484
2219
  "data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
2485
2220
  onClick: onCellClick,
2486
- style: getPinStyles(liveColumn, overflowing),
2221
+ style: getPinStyles(liveColumn, overflowing, fill),
2487
2222
  className: cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
2488
2223
  children: resizable && !noClip ? jsx("div", {
2489
2224
  className: "truncate",
@@ -2495,7 +2230,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
2495
2230
  function MemoCellInner(props) {
2496
2231
  return renderCellMarkup(props);
2497
2232
  }
2498
- const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.testId === next.testId);
2233
+ const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.fill === next.fill && prev.testId === next.testId);
2499
2234
  function SelectionCellInner(props) {
2500
2235
  const row = props.cell.row;
2501
2236
  const toggle = (e) => {
@@ -2509,7 +2244,7 @@ function SelectionCellInner(props) {
2509
2244
  });
2510
2245
  }
2511
2246
  const SelectionCell = memo(SelectionCellInner);
2512
- function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2247
+ function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2513
2248
  const clickable = rowHref !== void 0 || onRowOpen !== void 0;
2514
2249
  return jsx(TableRow, {
2515
2250
  "data-state": isSelected ? "selected" : void 0,
@@ -2522,6 +2257,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2522
2257
  isSelected,
2523
2258
  resizable,
2524
2259
  overflowing,
2260
+ fill: cell.column.id === fillColumnId,
2525
2261
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2526
2262
  testId: getCellTestId?.(row.original, cell.column.id)
2527
2263
  }, cell.id) : jsx(MemoCell, {
@@ -2529,13 +2265,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2529
2265
  rowOriginal: row.original,
2530
2266
  resizable,
2531
2267
  overflowing,
2268
+ fill: cell.column.id === fillColumnId,
2532
2269
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2533
2270
  testId: getCellTestId?.(row.original, cell.column.id)
2534
2271
  }, cell.id))
2535
2272
  });
2536
2273
  }
2537
- const MemoRow = memo(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
2538
- function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2274
+ const MemoRow = memo(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
2275
+ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2539
2276
  return jsx(TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => jsx(TableRow, {
2540
2277
  "aria-busy": "true",
2541
2278
  children: Array.from({ length: Math.max(1, visibleColumnCount) }).map((__, j) => jsx(TableCell, { children: jsx("div", { className: "h-4 w-3/4 animate-pulse rounded bg-muted" }) }, `skeleton-${i}-${j}`))
@@ -2552,6 +2289,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2552
2289
  resizable,
2553
2290
  overflowing,
2554
2291
  columnLayoutKey,
2292
+ fillColumnId,
2555
2293
  selectionColumnId,
2556
2294
  getCellTestId,
2557
2295
  getRowTestId,
@@ -2563,7 +2301,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2563
2301
  }
2564
2302
  const DataTableBody = memo(DataTableBodyInner, (prev, next) => {
2565
2303
  if (!next.isResizing) return false;
2566
- return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId;
2304
+ return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId;
2567
2305
  });
2568
2306
  function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
2569
2307
  const t = useOptionalT();
@@ -2593,8 +2331,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2593
2331
  const rows = table.getRowModel().rows;
2594
2332
  const headerGroups = table.getHeaderGroups();
2595
2333
  const visibleColumnCount = table.getVisibleLeafColumns().length;
2334
+ const fillId = tableFillColumnId(table);
2596
2335
  const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
2597
2336
  const resizable = table.options.enableColumnResizing === true;
2337
+ const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
2598
2338
  const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
2599
2339
  const sizingState = table.getState().columnSizing;
2600
2340
  const sizingInfo = table.getState().columnSizingInfo;
@@ -2686,7 +2426,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2686
2426
  className,
2687
2427
  style: resizable ? {
2688
2428
  ...columnSizeVars,
2689
- minWidth: "100%",
2429
+ width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
2690
2430
  tableLayout: "fixed"
2691
2431
  } : void 0,
2692
2432
  children: [jsx(TableHeader, { children: headerGroups.map((hg) => jsx(TableRow, { children: hg.headers.map((header) => {
@@ -2697,7 +2437,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2697
2437
  return jsxs(TableHead, {
2698
2438
  colSpan: header.colSpan,
2699
2439
  "aria-sort": ariaSort,
2700
- style: getPinStyles(header.column, overflowing),
2440
+ style: getPinStyles(header.column, overflowing, header.column.id === fillId),
2701
2441
  className: cn("relative", resizable && !columnMetaNoClip(header.column.columnDef.meta) && (pinEdgeClassFor(header.column, overflowing) ? "[&>*:not([data-testid^=col-resize])]:truncate" : "overflow-hidden [&>*:not([data-testid^=col-resize])]:truncate"), canReorder && "cursor-grab", pinEdgeClassFor(header.column, overflowing), columnMetaClassName(header.column.columnDef.meta)),
2702
2442
  draggable: canReorder || void 0,
2703
2443
  onDragStart: canReorder ? (e) => {
@@ -2756,6 +2496,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2756
2496
  resizable,
2757
2497
  overflowing,
2758
2498
  columnLayoutKey,
2499
+ fillColumnId: fillId,
2759
2500
  selectionColumnId,
2760
2501
  isResizing: Boolean(sizingInfo.isResizingColumn),
2761
2502
  getCellTestId,
@@ -2979,63 +2720,247 @@ function resolveUpdater(updater, prev) {
2979
2720
  }
2980
2721
  //#endregion
2981
2722
  //#region src/resource/list-error-state.tsx
2982
- function ListErrorState({ error, onRetry, resource, title, className }) {
2983
- const t = useT();
2984
- const raw = error.message ?? "";
2985
- const friendly = toFriendlyError(raw, "", friendlyStringsFrom(t));
2986
- const friendlyError = friendly.replaced ? friendly.detail : raw;
2987
- return jsxs("div", {
2988
- role: "alert",
2989
- className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-destructive/40 bg-destructive/5 text-center", className),
2990
- "data-testid": "error",
2991
- children: [
2992
- jsx("div", {
2993
- className: "rounded-full bg-destructive/10 p-3 text-destructive",
2994
- children: jsx(IconAlertCircle, {
2995
- className: "size-6",
2996
- "aria-hidden": "true"
2997
- })
2998
- }),
2999
- jsxs("div", {
3000
- className: "space-y-1",
3001
- children: [jsx("h3", {
3002
- className: "text-base font-semibold tracking-tight text-destructive",
3003
- children: title ?? t("list.errorTitle")
3004
- }), jsx("p", {
3005
- className: "max-w-md text-sm text-muted-foreground",
3006
- children: friendlyError || t("list.errorBody")
3007
- })]
3008
- }),
3009
- raw && raw !== friendlyError ? jsxs("details", {
3010
- className: "max-w-md text-left",
3011
- children: [jsx("summary", {
3012
- className: "cursor-pointer text-xs text-muted-foreground hover:text-foreground",
3013
- children: t("list.errorDetails")
3014
- }), jsx("p", {
3015
- className: "mt-1.5 break-all font-mono text-xs text-muted-foreground",
3016
- "data-testid": "list-error-detail",
3017
- children: raw
3018
- })]
3019
- }) : null,
3020
- onRetry && jsx("div", {
3021
- className: "flex justify-center pt-2",
3022
- children: jsxs(Button, {
3023
- type: "button",
3024
- variant: "outline",
3025
- size: "sm",
3026
- "data-testid": resource ? `list-error-retry:${resource}` : "list-error-retry",
3027
- onClick: onRetry,
3028
- className: "gap-1.5",
3029
- children: [jsx(IconRefresh, {
3030
- className: "size-3.5",
3031
- "aria-hidden": "true"
3032
- }), t("actions.retry")]
3033
- })
3034
- })
3035
- ]
2723
+ function ListErrorState(props) {
2724
+ const { error, onRetry, resource, title, className } = props;
2725
+ return jsx(LoadError, {
2726
+ surface: "list",
2727
+ error,
2728
+ ...onRetry === void 0 ? {} : { retry: onRetry },
2729
+ ...resource === void 0 ? {} : { resource },
2730
+ ...title === void 0 ? {} : { title },
2731
+ ...className === void 0 ? {} : { className }
3036
2732
  });
3037
2733
  }
3038
2734
  //#endregion
2735
+ //#region src/resource/use-table-layout.ts
2736
+ const WRITE_DEBOUNCE_MS = 200;
2737
+ function mutate(run, ok, then) {
2738
+ try {
2739
+ run().then((result) => {
2740
+ if (ok(result.status)) then();
2741
+ }, () => void 0);
2742
+ } catch {}
2743
+ }
2744
+ function resolve(updater, current) {
2745
+ return typeof updater === "function" ? updater(current) : updater;
2746
+ }
2747
+ function sameNames(a, b) {
2748
+ return a.length === b.length && a.every((name, index) => name === b[index]);
2749
+ }
2750
+ function readSizes(sizes, rule) {
2751
+ const out = {};
2752
+ for (const [column, size] of Object.entries(sizes)) {
2753
+ const of = rule(column);
2754
+ if (of === void 0 || !of.resize) continue;
2755
+ out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
2756
+ }
2757
+ return out;
2758
+ }
2759
+ function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
2760
+ const params = useMemo(() => layoutId === void 0 ? null : {
2761
+ resource,
2762
+ list: layoutId
2763
+ }, [resource, layoutId]);
2764
+ const settings = useOptionalUiSettings(tableLayoutSetting, params);
2765
+ const settingsRef = useRef(settings);
2766
+ settingsRef.current = settings;
2767
+ const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
2768
+ const fallback = useMemo(() => ({
2769
+ sizes: {},
2770
+ order: [],
2771
+ pinning: defaultPinning
2772
+ }), [defaultPinning]);
2773
+ const [local, setLocal] = useState();
2774
+ const stored = settings?.snapshot.entries[0]?.value;
2775
+ const settled = settings === void 0 || !settings.snapshot.pending;
2776
+ const held = useRef(null);
2777
+ const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
2778
+ const latest = useRef(layout);
2779
+ latest.current = layout;
2780
+ const sizing = useMemo(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
2781
+ const sizingRef = useRef(sizing);
2782
+ sizingRef.current = sizing;
2783
+ const generation = useRef(0);
2784
+ const timer = useRef(null);
2785
+ const pending = useRef(null);
2786
+ const confirm = useCallback((sent) => {
2787
+ if (generation.current === sent && pending.current === null) setLocal(void 0);
2788
+ }, []);
2789
+ const write = useCallback((target, value) => {
2790
+ const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
2791
+ if (scopeId === void 0) return;
2792
+ const sent = generation.current;
2793
+ mutate(() => target.set({
2794
+ scopeId,
2795
+ value
2796
+ }), (status) => status === "created" || status === "updated", () => confirm(sent));
2797
+ }, [confirm]);
2798
+ const store = useCallback((target, next) => {
2799
+ if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
2800
+ write(target, next);
2801
+ return;
2802
+ }
2803
+ const entry = target.snapshot.entries[0];
2804
+ if (entry === void 0) {
2805
+ setLocal(void 0);
2806
+ return;
2807
+ }
2808
+ const sent = generation.current;
2809
+ mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
2810
+ }, [
2811
+ defaultPinning,
2812
+ write,
2813
+ confirm
2814
+ ]);
2815
+ const flush = useCallback(() => {
2816
+ if (timer.current !== null) clearTimeout(timer.current);
2817
+ timer.current = null;
2818
+ const due = pending.current;
2819
+ pending.current = null;
2820
+ if (due !== null) write(due.target, due.value);
2821
+ }, [write]);
2822
+ const change = useCallback((next, debounced, touch) => {
2823
+ latest.current = next;
2824
+ generation.current += 1;
2825
+ setLocal({
2826
+ key,
2827
+ value: next
2828
+ });
2829
+ const target = settingsRef.current;
2830
+ if (target === void 0) return;
2831
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2832
+ const edits = held.current !== null && held.current.key === key ? held.current : {
2833
+ key,
2834
+ resetAll: false,
2835
+ resetSizes: false,
2836
+ sizes: new Map()
2837
+ };
2838
+ held.current = edits;
2839
+ touch(edits);
2840
+ return;
2841
+ }
2842
+ pending.current = {
2843
+ target,
2844
+ value: next
2845
+ };
2846
+ if (!debounced) {
2847
+ flush();
2848
+ return;
2849
+ }
2850
+ if (timer.current !== null) clearTimeout(timer.current);
2851
+ timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
2852
+ }, [key, flush]);
2853
+ useEffect(() => flush, [key]);
2854
+ useEffect(() => {
2855
+ const edits = held.current;
2856
+ if (edits === null) return;
2857
+ if (edits.key !== key) {
2858
+ held.current = null;
2859
+ return;
2860
+ }
2861
+ const target = settingsRef.current;
2862
+ if (!settled || target === void 0) return;
2863
+ held.current = null;
2864
+ const base = edits.resetAll ? fallback : stored ?? fallback;
2865
+ const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
2866
+ for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
2867
+ else sizes[column] = size;
2868
+ const next = {
2869
+ sizes,
2870
+ order: edits.order ?? base.order,
2871
+ pinning: edits.pinning ?? base.pinning
2872
+ };
2873
+ latest.current = next;
2874
+ generation.current += 1;
2875
+ setLocal({
2876
+ key,
2877
+ value: next
2878
+ });
2879
+ store(target, next);
2880
+ }, [settled, key]);
2881
+ const resetToken = resetLayout?.token ?? 0;
2882
+ const seenToken = useRef(resetToken);
2883
+ useEffect(() => {
2884
+ if (resetToken === seenToken.current) return;
2885
+ seenToken.current = resetToken;
2886
+ const all = resetLayout?.mode === "all";
2887
+ const next = all ? fallback : {
2888
+ ...latest.current,
2889
+ sizes: {}
2890
+ };
2891
+ if (timer.current !== null) clearTimeout(timer.current);
2892
+ timer.current = null;
2893
+ pending.current = null;
2894
+ latest.current = next;
2895
+ generation.current += 1;
2896
+ setLocal({
2897
+ key,
2898
+ value: next
2899
+ });
2900
+ const target = settingsRef.current;
2901
+ if (target === void 0) return;
2902
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2903
+ const prior = held.current !== null && held.current.key === key ? held.current : void 0;
2904
+ held.current = all ? {
2905
+ key,
2906
+ resetAll: true,
2907
+ resetSizes: false,
2908
+ sizes: new Map()
2909
+ } : {
2910
+ key,
2911
+ resetAll: prior?.resetAll ?? false,
2912
+ resetSizes: true,
2913
+ sizes: new Map(),
2914
+ ...prior?.order === void 0 ? {} : { order: prior.order },
2915
+ ...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
2916
+ };
2917
+ return;
2918
+ }
2919
+ store(target, next);
2920
+ }, [resetToken]);
2921
+ const setColumnSizing = useCallback((updater) => {
2922
+ const before = sizingRef.current;
2923
+ const sizes = resolve(updater, before);
2924
+ sizingRef.current = sizes;
2925
+ change({
2926
+ ...latest.current,
2927
+ sizes
2928
+ }, true, (edits) => {
2929
+ for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
2930
+ });
2931
+ }, [change]);
2932
+ const setColumnOrder = useCallback((updater) => {
2933
+ const order = resolve(updater, latest.current.order);
2934
+ change({
2935
+ ...latest.current,
2936
+ order
2937
+ }, false, (edits) => {
2938
+ edits.order = order;
2939
+ });
2940
+ }, [change]);
2941
+ const setColumnPinning = useCallback((updater) => {
2942
+ const resolved = resolve(updater, latest.current.pinning);
2943
+ const pinning = {
2944
+ left: resolved.left ?? [],
2945
+ right: resolved.right ?? []
2946
+ };
2947
+ change({
2948
+ ...latest.current,
2949
+ pinning
2950
+ }, false, (edits) => {
2951
+ edits.pinning = pinning;
2952
+ });
2953
+ }, [change]);
2954
+ return {
2955
+ columnSizing: sizing,
2956
+ setColumnSizing,
2957
+ columnOrder: layout.order,
2958
+ setColumnOrder,
2959
+ columnPinning: layout.pinning,
2960
+ setColumnPinning
2961
+ };
2962
+ }
2963
+ //#endregion
3039
2964
  //#region src/list-kit/cursor-load-more.tsx
3040
2965
  function CursorLoadMore({ hasMore, loading, onMore }) {
3041
2966
  const t = useT();
@@ -3077,6 +3002,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
3077
3002
  }
3078
3003
  const SELECT_COL_ID = "__select__";
3079
3004
  const ACTIONS_COL_ID = "__actions__";
3005
+ const DEFAULT_PINNING = Object.freeze({
3006
+ left: Object.freeze([SELECT_COL_ID, "id"]),
3007
+ right: Object.freeze([ACTIONS_COL_ID])
3008
+ });
3080
3009
  const ROW_ACTION_CONTROL_PX = 28;
3081
3010
  const ROW_ACTION_GAP_PX = 4;
3082
3011
  const ACTIONS_CELL_PADDING_PX = 16;
@@ -3088,29 +3017,30 @@ function actionsColumnSize(items) {
3088
3017
  const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
3089
3018
  return Math.max(ACTIONS_COL_MIN_PX, strip);
3090
3019
  }
3091
- const COLUMN_SIZE_BY_TYPE = {
3092
- boolean: 96,
3093
- number: 112
3020
+ const ALIGN_CLASS = {
3021
+ start: "text-start",
3022
+ center: "text-center",
3023
+ end: "text-end"
3094
3024
  };
3095
- const COLUMN_SIZE_BY_FORMAT = {
3096
- date: 132,
3097
- "date-time": 132
3098
- };
3099
- function columnSize(field) {
3100
- if (field?.format !== void 0 && COLUMN_SIZE_BY_FORMAT[field.format] !== void 0) return COLUMN_SIZE_BY_FORMAT[field.format];
3101
- if (field?.type !== void 0 && COLUMN_SIZE_BY_TYPE[field.type] !== void 0) return COLUMN_SIZE_BY_TYPE[field.type];
3102
- if (field?.kind === "date" || field?.kind === "dateTime") return COLUMN_SIZE_BY_FORMAT[field.kind === "dateTime" ? "date-time" : "date"];
3103
- return field?.kind === void 0 ? void 0 : COLUMN_SIZE_BY_TYPE[field.kind];
3104
- }
3105
- function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
3025
+ const STANDALONE_COLUMN_UI = Object.freeze({
3026
+ default: 160,
3027
+ min: 90,
3028
+ align: "start",
3029
+ resize: true,
3030
+ fill: false,
3031
+ display: Object.freeze({ params: Object.freeze({}) })
3032
+ });
3033
+ function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
3106
3034
  10,
3107
3035
  25,
3108
3036
  50,
3109
3037
  100
3110
- ], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }) {
3038
+ ], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, sortable, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, columnUi, layoutId, tableRef }) {
3039
+ const resource = routeResourceKey(resourceInput);
3111
3040
  const app = useApp();
3112
3041
  const tid = useTestIds();
3113
3042
  const t = useOptionalT();
3043
+ const chromeT = useChromeT();
3114
3044
  const locale = useLocale();
3115
3045
  const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
3116
3046
  const i18nCaps = useI18nCaps(resource);
@@ -3132,23 +3062,6 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3132
3062
  useEffect(() => {
3133
3063
  emittedRef.current = null;
3134
3064
  }, [controlledSelection]);
3135
- const layoutKey = `buro:table-layout:${resource}`;
3136
- const [columnSizing, setColumnSizing] = usePersistedState(`${layoutKey}:sizing`, {}, 200);
3137
- const [columnOrder, setColumnOrder] = usePersistedState(`${layoutKey}:order`, []);
3138
- const DEFAULT_PINNING = {
3139
- left: [SELECT_COL_ID, "id"],
3140
- right: [ACTIONS_COL_ID]
3141
- };
3142
- const [columnPinning, setColumnPinning] = usePersistedState(`${layoutKey}:pinning`, DEFAULT_PINNING);
3143
- const resetToken = resetLayout?.token ?? 0;
3144
- useEffect(() => {
3145
- if (!resetToken) return;
3146
- setColumnSizing({});
3147
- if (resetLayout?.mode === "all") {
3148
- setColumnOrder([]);
3149
- setColumnPinning(DEFAULT_PINNING);
3150
- }
3151
- }, [resetToken]);
3152
3065
  useEffect(() => {
3153
3066
  if (controlled) return;
3154
3067
  setOwnSelection({});
@@ -3158,7 +3071,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3158
3071
  const accessPending = listAccess.status === "pending" || pathAccess.pending;
3159
3072
  const declaredColumnKeys = useMemo(() => {
3160
3073
  if (!def) return [];
3161
- return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
3074
+ return [...columnsOverride ?? def.views?.list?.columns?.map(columnName) ?? Object.keys(def.fields)];
3162
3075
  }, [def, columnsOverride]);
3163
3076
  const visibleColumnKeys = useMemo(() => accessPending ? [] : declaredColumnKeys.filter((col) => listAccess.columnVisible(col) && (columnTarget(app.registry, resource, col) === void 0 || listAccess.columnVisible(col.split(".")[0]) && pathAccess.visible(col))), [
3164
3077
  accessPending,
@@ -3208,10 +3121,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3208
3121
  const resourceLabel = resolveResourceLabel(i18nCaps, resource, "plural", def);
3209
3122
  const recordSingular = resolveResourceLabel(i18nCaps, resource, "single", def);
3210
3123
  const ariaLabel = resourceLabel;
3211
- const actionsHeader = t("actions.column", { defaultValue: "Actions" });
3124
+ const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
3212
3125
  const viewRow = useViewRow(resource);
3213
3126
  const volatileRef = useRef({
3214
3127
  onSort,
3128
+ sortable,
3215
3129
  onHideColumn,
3216
3130
  requiredColumns,
3217
3131
  renderCell,
@@ -3223,6 +3137,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3223
3137
  });
3224
3138
  volatileRef.current = {
3225
3139
  onSort,
3140
+ sortable,
3226
3141
  onHideColumn,
3227
3142
  requiredColumns,
3228
3143
  renderCell,
@@ -3246,6 +3161,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3246
3161
  }, [rowLinkKey]);
3247
3162
  const stableRowClassName = useCallback((row) => volatileRef.current.rowHref?.(volatileRef.current.viewRow(row.original)) || (volatileRef.current.rowQuick?.(row.original) ?? null) !== null ? "cursor-pointer" : void 0, [rowLinkKey]);
3248
3163
  const [listRoot, narrow] = useContainerNarrow();
3164
+ const columnUiOf = useMemo(() => {
3165
+ const entries = new Map();
3166
+ for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
3167
+ const name = columnName(entry);
3168
+ if (!entries.has(name)) entries.set(name, entry);
3169
+ }
3170
+ return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : resolveColumnUi(def, entries.get(col) ?? col, app.registry, columnTypeWidth));
3171
+ }, [
3172
+ app,
3173
+ def,
3174
+ columnUi
3175
+ ]);
3176
+ const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
3177
+ resource,
3178
+ layoutId,
3179
+ rule: useMemo(() => {
3180
+ const known = new Set([
3181
+ ...declaredColumnKeys,
3182
+ ...(def?.views?.list?.columns ?? []).map(columnName),
3183
+ ...(def?.views?.list?.optionalColumns ?? []).map(columnName),
3184
+ ...Object.keys(def?.fields ?? {})
3185
+ ]);
3186
+ return (col) => {
3187
+ if (!known.has(col)) return void 0;
3188
+ const ui = columnUiOf(col);
3189
+ return {
3190
+ min: ui.min,
3191
+ max: ui.max,
3192
+ resize: ui.resize
3193
+ };
3194
+ };
3195
+ }, [
3196
+ declaredColumnKeys,
3197
+ def,
3198
+ columnUiOf
3199
+ ]),
3200
+ defaultPinning: DEFAULT_PINNING,
3201
+ resetLayout
3202
+ });
3249
3203
  const tableColumns = useMemo(() => {
3250
3204
  const cols = [];
3251
3205
  if (selectable) cols.push({
@@ -3268,19 +3222,32 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3268
3222
  });
3269
3223
  for (const col of visibleColumnKeys) {
3270
3224
  const target = columnTarget(app.registry, resource, col);
3271
- const field = target === void 0 ? def?.fields?.[col] : target.field;
3225
+ const field = target !== void 0 ? target.field : def === void 0 ? void 0 : declaredField(def, col);
3272
3226
  const declaredLabel = declaredText("field", field, "label");
3273
3227
  const headerLabel = target === void 0 ? resolveFieldLabel(volatileRef.current.i18nCaps, col, declaredLabel) : resolveFieldLabel(capsOf(volatileRef.current.i18nCaps, target.resource), target.name, declaredLabel);
3274
- const kindSize = columnSize(field);
3275
- const declared = target === void 0 && def !== void 0 ? declaredField(def, col) : void 0;
3276
- const linkColumn = declared !== null && typeof declared === "object" && isLinkField(declared);
3228
+ const ui = columnUiOf(col);
3229
+ const frameField = {
3230
+ ...field ?? { type: "unknown" },
3231
+ name: target === void 0 ? col : target.name
3232
+ };
3233
+ const cellEntry = { display: ui.display };
3234
+ const wraps = ui.display.overflow === "wrap";
3235
+ const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && isColumnSortable(def, col, app, app.registry));
3277
3236
  cols.push({
3278
3237
  id: col,
3279
3238
  ...columnAccessor(col),
3280
- ...kindSize !== void 0 ? { size: kindSize } : {},
3281
- enableSorting: !linkColumn && Boolean(volatileRef.current.onSort),
3239
+ size: ui.default,
3240
+ minSize: ui.min,
3241
+ maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
3242
+ enableResizing: ui.resize,
3243
+ enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
3282
3244
  enableHiding: true,
3283
- meta: { testid: col },
3245
+ meta: {
3246
+ testid: col,
3247
+ className: ALIGN_CLASS[ui.align],
3248
+ ...wraps ? { wrap: true } : {},
3249
+ ...ui.fill ? { fill: true } : {}
3250
+ },
3284
3251
  header: ({ column, table }) => {
3285
3252
  const v = volatileRef.current;
3286
3253
  const pinSide = column.getIsPinned();
@@ -3311,9 +3278,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3311
3278
  };
3312
3279
  return jsx(ColumnHeaderMenu, {
3313
3280
  label: headerLabel,
3314
- sortable: !linkColumn && Boolean(v.onSort),
3281
+ sortable: columnSortable && Boolean(v.onSort),
3315
3282
  sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
3316
- onSort: !linkColumn && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3283
+ onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3317
3284
  pinned: column.getIsPinned(),
3318
3285
  onPin: (side) => column.pin(side),
3319
3286
  canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
@@ -3335,12 +3302,21 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3335
3302
  field: field ?? { type: "unknown" },
3336
3303
  value,
3337
3304
  record,
3305
+ uiSurface: "column",
3306
+ columnEntry: cellEntry,
3338
3307
  fallback: jsx("span", { children: String(value ?? "") })
3339
3308
  });
3309
+ const framed = (node) => customCell !== void 0 ? node : jsx(FieldValueFrame, {
3310
+ field: frameField,
3311
+ value,
3312
+ surface: "column",
3313
+ entry: cellEntry,
3314
+ children: node
3315
+ });
3340
3316
  const href = volatileRef.current.rowHref?.(record);
3341
3317
  const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
3342
3318
  const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
3343
- if (quick !== null) return href ? jsx(ClaimedQuickLink, {
3319
+ if (quick !== null) return framed(href ? jsx(ClaimedQuickLink, {
3344
3320
  href,
3345
3321
  className: relationLinkClassName,
3346
3322
  onOpen: quick,
@@ -3354,13 +3330,13 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3354
3330
  quick();
3355
3331
  },
3356
3332
  children: cellInner
3357
- });
3358
- return isFirstContentCell && href ? jsx(RouterLink, {
3333
+ }));
3334
+ return framed(isFirstContentCell && href ? jsx(RouterLink, {
3359
3335
  href,
3360
3336
  className: relationLinkClassName,
3361
3337
  onClick: (e) => e.stopPropagation(),
3362
3338
  children: cellInner
3363
- }) : cellInner;
3339
+ }) : cellInner);
3364
3340
  }
3365
3341
  });
3366
3342
  }
@@ -3369,7 +3345,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3369
3345
  id: ACTIONS_COL_ID,
3370
3346
  enableSorting: false,
3371
3347
  enableResizing: false,
3372
- size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
3348
+ size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
3373
3349
  enableHiding: false,
3374
3350
  header: () => jsx("span", {
3375
3351
  className: "sr-only",
@@ -3380,7 +3356,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3380
3356
  noRowNav: true,
3381
3357
  noClip: true
3382
3358
  },
3383
- cell: ({ row }) => jsx(ActionOverflow, {
3359
+ cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : jsx(ActionOverflow, {
3384
3360
  resource,
3385
3361
  record: row.original,
3386
3362
  surface: "row"
@@ -3400,7 +3376,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3400
3376
  actionsHeader,
3401
3377
  i18nCaps,
3402
3378
  rowLinkKey,
3403
- narrow
3379
+ narrow,
3380
+ columnUiOf
3404
3381
  ]);
3405
3382
  const handleSelectionChange = (updater) => {
3406
3383
  if (controlled) {
@@ -3503,10 +3480,25 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3503
3480
  const isWrongPage = page > 1;
3504
3481
  const EmptyIcon = isFiltered ? IconFilterX : IconInbox;
3505
3482
  const items = resourceLabel.toLowerCase();
3506
- const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", { params: { items } }) : t("list.emptyTitle", { params: { items } });
3507
- const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", { params: { items } }) : createHref ? t("list.emptyBody", { params: { item: recordSingular.toLowerCase() } }) : t("list.emptyBodyReadOnly", { params: { items } });
3508
- return jsxs(Fragment$1, { children: [jsxs("div", {
3509
- className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
3483
+ const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
3484
+ resource,
3485
+ params: { items }
3486
+ }) : t("list.emptyTitle", {
3487
+ resource,
3488
+ params: { items }
3489
+ });
3490
+ const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
3491
+ resource,
3492
+ params: { items }
3493
+ }) : createHref ? t("list.emptyBody", {
3494
+ resource,
3495
+ params: { item: recordSingular.toLowerCase() }
3496
+ }) : t("list.emptyBodyReadOnly", {
3497
+ resource,
3498
+ params: { items }
3499
+ });
3500
+ return jsxs(Fragment$1, { children: [jsxs("div", {
3501
+ className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
3510
3502
  "data-testid": tid.listEmpty(resource),
3511
3503
  children: [
3512
3504
  jsx("div", {
@@ -3529,7 +3521,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3529
3521
  jsxs("div", {
3530
3522
  className: "flex justify-center gap-2 pt-2",
3531
3523
  children: [
3532
- isFiltered && onClearFilter && jsx(Button, {
3524
+ onClearFilter && jsx(Button, {
3533
3525
  type: "button",
3534
3526
  variant: "outline",
3535
3527
  size: "sm",
@@ -3717,43 +3709,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3717
3709
  })
3718
3710
  });
3719
3711
  }
3720
- function usePersistedState(key, initial, debounceMs = 0) {
3721
- const [state, setState] = useState(() => {
3722
- if (typeof window === "undefined") return initial;
3723
- try {
3724
- const raw = window.localStorage.getItem(key);
3725
- return raw ? JSON.parse(raw) : initial;
3726
- } catch {
3727
- return initial;
3728
- }
3712
+ //#endregion
3713
+ //#region src/blocks/related-list-unbound.tsx
3714
+ function UnboundRelatedList(props) {
3715
+ const t = useOptionalT();
3716
+ return jsxs("div", {
3717
+ className: "flex flex-col gap-2",
3718
+ "data-testid": `list-unbound:${props.resource}`,
3719
+ children: [props.title === void 0 ? null : jsx("h3", {
3720
+ className: "text-sm font-semibold",
3721
+ children: props.title
3722
+ }), jsx("p", {
3723
+ className: "text-sm text-muted-foreground",
3724
+ children: props.empty ?? t("table.empty", { defaultValue: "No records" })
3725
+ })]
3729
3726
  });
3730
- useEffect(() => {
3731
- try {
3732
- if (window.localStorage.getItem(key) == null) window.localStorage.setItem(key, JSON.stringify(initial));
3733
- } catch {}
3734
- }, [key]);
3735
- const writeTimer = useRef(null);
3736
- const write = (value) => {
3737
- const doWrite = () => {
3738
- try {
3739
- window.localStorage.setItem(key, JSON.stringify(value));
3740
- } catch {}
3741
- };
3742
- if (debounceMs <= 0) {
3743
- doWrite();
3744
- return;
3745
- }
3746
- if (writeTimer.current) clearTimeout(writeTimer.current);
3747
- writeTimer.current = setTimeout(doWrite, debounceMs);
3748
- };
3749
- const set = (updater) => {
3750
- setState((prev) => {
3751
- const next = typeof updater === "function" ? updater(prev) : updater;
3752
- write(next);
3753
- return next;
3754
- });
3755
- };
3756
- return [state, set];
3727
+ }
3728
+ function placementScope(scope, owner) {
3729
+ return scope ?? (owner === void 0 ? "session" : "none");
3730
+ }
3731
+ //#endregion
3732
+ //#region src/list-kit/pieces/bulk-reach.tsx
3733
+ function useBulkFlowReachable(resource) {
3734
+ const app = useApp();
3735
+ const control = useFlowRowResolver(resource);
3736
+ return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => isFlowAction(action) && control(action.name, void 0).state !== "absent");
3737
+ }
3738
+ function ExportReach(props) {
3739
+ const { available } = useResourceExport(props.resource);
3740
+ const capability = useCapability("run", {
3741
+ resource: props.resource,
3742
+ target: {
3743
+ kind: "action",
3744
+ name: "export"
3745
+ },
3746
+ mode: "list"
3747
+ });
3748
+ return props.children(available && capability.allowed);
3749
+ }
3750
+ function BulkExportReach(props) {
3751
+ return useOptionalDataRuntime() !== null ? jsx(ExportReach, { ...props }) : props.children(false);
3757
3752
  }
3758
3753
  //#endregion
3759
3754
  //#region src/list-kit/pieces/piece.tsx
@@ -3821,15 +3816,47 @@ function ForceTableView(props) {
3821
3816
  }
3822
3817
  //#endregion
3823
3818
  //#region src/list-kit/pieces/list-body.tsx
3819
+ function LinkDetachRowButton(props) {
3820
+ const { resource, linkEditing } = useListSurface();
3821
+ const t = useOptionalT();
3822
+ const [busy, setBusy] = useState(false);
3823
+ const detach = linkEditing?.detach;
3824
+ if (detach === void 0) return null;
3825
+ const label = t("field.link.detach", { defaultValue: "Detach" });
3826
+ return jsx(Button, {
3827
+ type: "button",
3828
+ size: "icon",
3829
+ variant: "ghost",
3830
+ className: "size-7",
3831
+ "aria-label": label,
3832
+ title: label,
3833
+ disabled: busy,
3834
+ "data-testid": `list-link-detach:${resource}:${props.row.id}`,
3835
+ onClick: (event) => {
3836
+ event.stopPropagation();
3837
+ setBusy(true);
3838
+ detach.onDetach([props.row]).finally(() => setBusy(false));
3839
+ },
3840
+ children: jsx(IconUnlink, {
3841
+ className: "size-4",
3842
+ "aria-hidden": "true"
3843
+ })
3844
+ });
3845
+ }
3846
+ const linkDetachRow = (row) => jsx(LinkDetachRowButton, { row });
3824
3847
  function ListBodyView() {
3825
3848
  usePieceMount("body");
3826
3849
  const list = useResourceListContext();
3827
- const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly } = useListSurface();
3850
+ const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
3851
+ const detachable = readOnly && linkEditing?.detach !== void 0;
3852
+ const flowReachable = useBulkFlowReachable(resource);
3853
+ const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
3828
3854
  const item = useContext(ListItemContext);
3829
3855
  const rowsView = useMemo(() => rowsViewFor?.(item), [rowsViewFor, item]);
3830
3856
  const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
3831
3857
  const rows = policyRowsOf(list, list.rows);
3832
- return jsx(ResourceList, {
3858
+ if (query.unbound) return jsx(UnboundRelatedList, { resource });
3859
+ const view = (exportReachable) => jsx(ResourceList, {
3833
3860
  resource,
3834
3861
  data: rows,
3835
3862
  total: pagination.total,
@@ -3846,29 +3873,50 @@ function ListBodyView() {
3846
3873
  onRequestCount: count.available ? count.request : void 0,
3847
3874
  isCountingRows: count.isLoading,
3848
3875
  columns: columns.visible,
3876
+ columnUi: columns.ui,
3849
3877
  isLoading: query.isLoading,
3850
3878
  error: query.error,
3851
3879
  onRetry: query.refetch,
3852
3880
  createHref: route && createAvailable && createPage ? routing.createHref : void 0,
3853
3881
  ...rowProps,
3854
3882
  ...rowsView === void 0 ? {} : { rowsView },
3855
- selectable: tableView && !readOnly,
3856
- rowActions: !readOnly,
3883
+ selectable: selectable(exportReachable),
3884
+ rowActions: detachable ? linkDetachRow : !readOnly,
3857
3885
  selectedIds: selection.ids,
3858
3886
  onSelectionChange: selection.set,
3859
3887
  isFiltered: filters.isFiltered,
3860
- onClearFilter: filters.clearAll,
3888
+ onClearFilter: filters.canReset ? filters.reset : void 0,
3861
3889
  emptyState,
3862
3890
  sort: sorting.value ?? [],
3863
3891
  requiredColumns: columns.required,
3864
3892
  resetLayout: columns.resetLayout,
3893
+ layoutId,
3865
3894
  onHideColumn: columns.hide,
3866
3895
  onSort: sorting.toggle,
3896
+ sortable: sorting.sortable,
3867
3897
  renderCell
3868
3898
  });
3899
+ return jsx(BulkExportReach, {
3900
+ resource,
3901
+ children: view
3902
+ });
3869
3903
  }
3870
3904
  const ListBody = listPiece("ListBody", ListBodyView);
3871
3905
  //#endregion
3906
+ //#region src/resource/form-context.tsx
3907
+ const FormControlContext = createContext(null);
3908
+ const FormControlProvider = FormControlContext.Provider;
3909
+ function useFormControlOf(controller) {
3910
+ const latest = useRef(controller);
3911
+ latest.current = controller;
3912
+ return useMemo(() => ({ setValues: (patch) => {
3913
+ for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
3914
+ } }), []);
3915
+ }
3916
+ function useFormControl() {
3917
+ return useContext(FormControlContext);
3918
+ }
3919
+ //#endregion
3872
3920
  //#region src/record/route-screen-context.ts
3873
3921
  const RouteScreenClaimContext = createContext(false);
3874
3922
  const RouteScreenContext = createContext(false);
@@ -3992,7 +4040,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
3992
4040
  region,
3993
4041
  form,
3994
4042
  labelledBy: title === void 0 ? void 0 : titleId,
3995
- children: renderChildren(node.children)
4043
+ children: renderChildren(node.children, node)
3996
4044
  }) : null,
3997
4045
  ...title === void 0 ? {} : { title },
3998
4046
  ...description === void 0 ? {} : { description },
@@ -4027,10 +4075,12 @@ function TabsHost({ node, renderChildren }) {
4027
4075
  const state = useScreenState();
4028
4076
  const text = useNodeText();
4029
4077
  const panels = node.children.filter((child) => child.state !== "absent");
4030
- const open = panels.find((child) => child.open === true) ?? panels[0];
4078
+ const declared = compiled.nodes[node.index].options.default;
4079
+ const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
4080
+ const open = panels.find((child) => child.open === true) ?? effectiveDefault;
4031
4081
  const urlSelect = useTabsUrlSelect();
4032
4082
  const select = (panel, reveal) => {
4033
- if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === panels[0], reveal);
4083
+ if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
4034
4084
  else state?.openPanel(node.id, panelValue(panel));
4035
4085
  };
4036
4086
  const onSelect = (key) => {
@@ -4042,7 +4092,7 @@ function TabsHost({ node, renderChildren }) {
4042
4092
  key: panelValue(panel),
4043
4093
  title: titleOf(panel),
4044
4094
  errorCount: panel.key === void 0 ? 0 : controller.errorsIn(panel.key),
4045
- panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children) }) : null
4095
+ panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children, panel) }) : null
4046
4096
  }));
4047
4097
  const { view: View, id } = useNodeView(node, "tabs");
4048
4098
  const t = useFrameT(id);
@@ -4153,7 +4203,7 @@ function WizardHost({ node, renderChildren }) {
4153
4203
  region: "step-panel",
4154
4204
  form,
4155
4205
  labelledBy: titleId,
4156
- children: renderChildren(panel.children)
4206
+ children: renderChildren(panel.children, panel)
4157
4207
  }),
4158
4208
  ...description === void 0 ? {} : { description },
4159
4209
  titleId,
@@ -4199,18 +4249,23 @@ function WizardHost({ node, renderChildren }) {
4199
4249
  //#region src/screen/nodes.tsx
4200
4250
  function FailedNode({ node }) {
4201
4251
  const { controller } = useScreenTree();
4202
- const t = useOptionalT();
4203
- return jsxs("div", {
4204
- ...nodeAttributes(node),
4205
- role: "alert",
4206
- className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
4207
- children: [jsx("span", { children: t("screen.failed", { defaultValue: "This section could not be loaded." }) }), jsx(Button, {
4208
- type: "button",
4209
- variant: "outline",
4210
- size: "sm",
4211
- onClick: () => controller.retry(node.failedOn),
4212
- children: t("actions.retry", { defaultValue: "Retry" })
4213
- })]
4252
+ let error;
4253
+ for (const name of node.failedOn) {
4254
+ const source = controller.sources[name];
4255
+ if (source?.status === "failed") {
4256
+ error = source.error;
4257
+ break;
4258
+ }
4259
+ }
4260
+ return jsx(LoadError, {
4261
+ surface: "tab",
4262
+ error,
4263
+ retry: () => controller.retry(node.failedOn),
4264
+ fallback: {
4265
+ key: "screen.failed",
4266
+ default: "This section could not be loaded."
4267
+ },
4268
+ attributes: nodeAttributes(node)
4214
4269
  });
4215
4270
  }
4216
4271
  function failedOnlyOnMeta(node) {
@@ -4223,8 +4278,17 @@ function drawn(node, hiddenFields) {
4223
4278
  if (node.state === "absent") return false;
4224
4279
  return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
4225
4280
  }
4226
- function SectionChildren({ nodes }) {
4227
- const { hiddenFields } = useScreenTree();
4281
+ function SectionChildren({ nodes, owner }) {
4282
+ const { compiled, hiddenFields } = useScreenTree();
4283
+ const text = useNodeText();
4284
+ if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
4285
+ const empty = text(compiled.nodes[owner.index]?.options.empty);
4286
+ return empty === void 0 || empty === "" ? null : jsx("p", {
4287
+ "data-buro-region": "empty",
4288
+ className: "text-sm text-muted-foreground",
4289
+ children: empty
4290
+ });
4291
+ }
4228
4292
  const out = [];
4229
4293
  let run = [];
4230
4294
  const flush = () => {
@@ -4246,8 +4310,11 @@ function SectionChildren({ nodes }) {
4246
4310
  flush();
4247
4311
  return out;
4248
4312
  }
4249
- function renderSection(nodes) {
4250
- return jsx(SectionChildren, { nodes });
4313
+ function renderSection(nodes, owner) {
4314
+ return jsx(SectionChildren, {
4315
+ nodes,
4316
+ owner
4317
+ });
4251
4318
  }
4252
4319
  function Layout({ node }) {
4253
4320
  return jsx("div", {
@@ -4369,17 +4436,14 @@ function pageHost(page) {
4369
4436
  }
4370
4437
  //#endregion
4371
4438
  //#region src/forms/declarative-form.tsx
4372
- function translateOrFallback(t, key, fallback) {
4373
- const translated = t(key);
4374
- return translated === key ? fallback : translated;
4375
- }
4376
- function issueText(issue, t, i18nPrefix) {
4439
+ function issueText(issue, caps, i18nPrefix) {
4377
4440
  return resolveValidationMessage({
4378
4441
  code: issue.code ?? "",
4379
4442
  message: issue.message,
4380
- ...issue.params === void 0 ? {} : { params: issue.params }
4443
+ ...issue.check === void 0 ? {} : { check: issue.check }
4381
4444
  }, {
4382
- t,
4445
+ has: caps.has,
4446
+ t: caps.t,
4383
4447
  ...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
4384
4448
  });
4385
4449
  }
@@ -4446,7 +4510,9 @@ function DeclarativeFormBody(props) {
4446
4510
  ...props.surface === void 0 ? {} : { surface: props.surface },
4447
4511
  ...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
4448
4512
  });
4449
- const t = useOptionalT();
4513
+ const formControl = useFormControlOf(form);
4514
+ const chromeT = useChromeT();
4515
+ const caps = useI18nCaps();
4450
4516
  const disabled = props.disabled === true;
4451
4517
  const uid = useId();
4452
4518
  const leafIds = useMemo(() => ({
@@ -4456,8 +4522,8 @@ function DeclarativeFormBody(props) {
4456
4522
  const formErrors = form.errors.filter((issue) => issue.field === "_form");
4457
4523
  const retryableChecks = [];
4458
4524
  for (const issue of form.checkOutcome?.blockers ?? []) {
4459
- const checkId = issue.params?.checkId;
4460
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
4525
+ const checkId = issue.check?.id;
4526
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
4461
4527
  if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
4462
4528
  }
4463
4529
  const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
@@ -4470,7 +4536,7 @@ function DeclarativeFormBody(props) {
4470
4536
  formErrors.length > 0 && jsx("div", {
4471
4537
  role: "alert",
4472
4538
  className: "mb-4 flex flex-col gap-1 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
4473
- children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, t, prefix) }, index))
4539
+ children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
4474
4540
  }),
4475
4541
  retryableChecks.length > 0 && jsx("div", {
4476
4542
  className: "mb-4 flex flex-wrap gap-2",
@@ -4480,25 +4546,28 @@ function DeclarativeFormBody(props) {
4480
4546
  size: "sm",
4481
4547
  "data-testid": `form-check-retry:${checkId}`,
4482
4548
  onClick: () => void form.revalidate(),
4483
- children: translateOrFallback(t, "actions.retry", "Retry")
4549
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4484
4550
  }, checkId))
4485
4551
  }),
4486
4552
  formWarnings.length > 0 && jsx("div", {
4487
4553
  role: "status",
4488
4554
  "aria-live": "polite",
4489
4555
  className: "mb-4 flex flex-col gap-1 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning-emphasis",
4490
- children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, t, prefix) }, index))
4556
+ children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
4491
4557
  }),
4492
- jsx(ScreenTree, {
4493
- region: "form-body",
4494
- resource: prefix,
4495
- mode: "create",
4496
- compiled: form.screen,
4497
- controller: form,
4498
- leafIds,
4499
- ...props.renderField === void 0 ? {} : { renderField: props.renderField },
4500
- disabled,
4501
- ...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
4558
+ jsx(FormControlProvider, {
4559
+ value: formControl,
4560
+ children: jsx(ScreenTree, {
4561
+ region: "form-body",
4562
+ resource: prefix,
4563
+ mode: "create",
4564
+ compiled: form.screen,
4565
+ controller: form,
4566
+ leafIds,
4567
+ ...props.renderField === void 0 ? {} : { renderField: props.renderField },
4568
+ disabled,
4569
+ ...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
4570
+ })
4502
4571
  }),
4503
4572
  props.slots?.footer === void 0 ? jsx("div", {
4504
4573
  className: "mt-4 flex justify-end",
@@ -4506,7 +4575,7 @@ function DeclarativeFormBody(props) {
4506
4575
  type: "submit",
4507
4576
  disabled: !form.canSubmit || disabled,
4508
4577
  onMouseDown: (event) => event.preventDefault(),
4509
- children: props.submitLabel ?? translateOrFallback(t, "actions.save", "Save")
4578
+ children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
4510
4579
  })
4511
4580
  }) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
4512
4581
  ]
@@ -4591,7 +4660,8 @@ function ExportOptionsDirty({ dirty }) {
4591
4660
  return null;
4592
4661
  }
4593
4662
  function getNoRunState() {}
4594
- function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount, scope }) {
4663
+ function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
4664
+ const resource = routeResourceKey(resourceInput);
4595
4665
  const t = useOptionalT();
4596
4666
  const dialogTestId = useTestIds().exportDialog(resource);
4597
4667
  const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
@@ -4713,14 +4783,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4713
4783
  })] }), jsx(ExportProgressView, {
4714
4784
  state: runState,
4715
4785
  dialogTestId,
4716
- t
4786
+ t,
4787
+ caps
4717
4788
  })] }) : jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: titleText }), jsx(DialogDescription, {
4718
4789
  className: "sr-only",
4719
4790
  children: titleText
4720
4791
  })] }), !available ? jsx("p", {
4721
4792
  role: "alert",
4722
4793
  "data-testid": `${dialogTestId}:unavailable`,
4723
- children: error?.message ?? t("export.dialog.unavailable")
4794
+ children: error === void 0 || error === null ? t("export.dialog.unavailable") : resolveErrorText(error, caps)
4724
4795
  }) : jsxs(Fragment$1, { children: [
4725
4796
  loading ? jsx("p", {
4726
4797
  role: "status",
@@ -4799,7 +4870,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4799
4870
  })
4800
4871
  });
4801
4872
  }
4802
- function ExportProgressView({ state, dialogTestId, t }) {
4873
+ function ExportProgressView({ state, dialogTestId, t, caps }) {
4803
4874
  if (state === void 0) return null;
4804
4875
  if (state.status === "running") {
4805
4876
  const total = state.progress?.total;
@@ -4820,7 +4891,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4820
4891
  if (state.status === "failed") return jsx("p", {
4821
4892
  role: "alert",
4822
4893
  "data-testid": `${dialogTestId}:error`,
4823
- children: `${t("export.dialog.failed")}: ${state.error.message}`
4894
+ children: `${t("export.dialog.failed")}: ${resolveErrorText(state.error, caps)}`
4824
4895
  });
4825
4896
  if (state.status === "canceled") return jsx("p", {
4826
4897
  "data-testid": `${dialogTestId}:canceled`,
@@ -4833,6 +4904,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4833
4904
  function BulkSummary(props) {
4834
4905
  const loaded = useLoaded(loadBulkSummary);
4835
4906
  const t = useOptionalT();
4907
+ const chromeT = useChromeT();
4836
4908
  const failed = (retry) => jsxs("div", {
4837
4909
  role: "alert",
4838
4910
  className: "flex items-center gap-2 text-sm",
@@ -4841,7 +4913,7 @@ function BulkSummary(props) {
4841
4913
  size: "sm",
4842
4914
  variant: "outline",
4843
4915
  onClick: retry,
4844
- children: text(t, "actions.retry", "Retry")
4916
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4845
4917
  })]
4846
4918
  });
4847
4919
  if (loaded.error !== void 0) return failed(loaded.retry);
@@ -4859,13 +4931,14 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4859
4931
  const control = useFlowRowResolver(resource)(actionName, void 0);
4860
4932
  const chrome = useFlowChrome(resource, actionName);
4861
4933
  const def = app.registry.get(resource);
4862
- const each = useMemo(() => {
4934
+ const compiled = useMemo(() => {
4863
4935
  try {
4864
- return def === void 0 ? false : compileFlow(def, actionName).each;
4936
+ return def === void 0 ? void 0 : compileFlow(def, actionName);
4865
4937
  } catch {
4866
- return false;
4938
+ return;
4867
4939
  }
4868
4940
  }, [def, actionName]);
4941
+ const each = compiled?.each ?? false;
4869
4942
  const [running, setRunning] = useState(false);
4870
4943
  const [summary, setSummary] = useState(null);
4871
4944
  const testid = tid.bulkAction(resource, actionName);
@@ -4873,7 +4946,10 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4873
4946
  const variant = chrome.destructive ? "destructive" : "outline";
4874
4947
  const overLimit = each && ids.length > BULK_EACH_LIMIT;
4875
4948
  if (overLimit || control.state === "disabled") return jsx(DisabledAction, {
4876
- reason: overLimit ? text(t, "flow.bulkLimit", `Select at most ${BULK_EACH_LIMIT}.`, { count: BULK_EACH_LIMIT }) : control.reason,
4949
+ reason: overLimit ? t("flow.bulkLimit", {
4950
+ defaultValue: `Select at most ${BULK_EACH_LIMIT}.`,
4951
+ count: BULK_EACH_LIMIT
4952
+ }) : control.reason,
4877
4953
  label: chrome.label,
4878
4954
  icon: chrome.icon,
4879
4955
  testid,
@@ -4935,14 +5011,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4935
5011
  actionName,
4936
5012
  label: chrome.labelText,
4937
5013
  total: summary.total,
4938
- progress: summary.progress
5014
+ progress: summary.progress,
5015
+ texts: compiled?.flow.texts
4939
5016
  }),
4940
5017
  jsx(DialogFooter, { children: jsx(Button, {
4941
5018
  type: "button",
4942
5019
  variant: "outline",
4943
5020
  "data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
4944
5021
  onClick: () => setSummary(null),
4945
- children: text(t, "flow.close", "Close")
5022
+ children: t("flow.close", { defaultValue: "Close" })
4946
5023
  }) })
4947
5024
  ]
4948
5025
  })
@@ -4958,10 +5035,12 @@ function orderBulkActions(actions) {
4958
5035
  seq: index
4959
5036
  })).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
4960
5037
  }
4961
- function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope }) {
5038
+ function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
5039
+ const resource = routeResourceKey(resourceInput);
4962
5040
  const app = useApp();
4963
5041
  const tid = useTestIds();
4964
5042
  const t = useOptionalT();
5043
+ const chromeT = useChromeT();
4965
5044
  const def = app.registry.get(resource);
4966
5045
  const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
4967
5046
  const ids = selectedIds ?? records.map((record) => record.id);
@@ -4994,7 +5073,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
4994
5073
  className: "mx-0.5 h-6 w-px bg-border",
4995
5074
  "aria-hidden": "true"
4996
5075
  }),
4997
- bulkActions.map((action) => jsx(BulkFlowButton, {
5076
+ actions !== void 0 ? actions : null,
5077
+ actions === void 0 && bulkActions.map((action) => jsx(BulkFlowButton, {
4998
5078
  resource,
4999
5079
  actionName: action.name,
5000
5080
  ids,
@@ -5003,7 +5083,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5003
5083
  else if (next.length === 0) handleClear();
5004
5084
  }
5005
5085
  }, action.name)),
5006
- jsx(BulkExportButton, {
5086
+ actions === void 0 && jsx(BulkExportButton, {
5007
5087
  resource,
5008
5088
  ids,
5009
5089
  visibleColumns: visibleColumns ?? [],
@@ -5020,7 +5100,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5020
5100
  "aria-hidden": "true"
5021
5101
  }), jsx("span", {
5022
5102
  className: "hidden sm:inline",
5023
- children: t("actions.clearSelection", { defaultValue: "Clear" })
5103
+ children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
5024
5104
  })]
5025
5105
  })
5026
5106
  ]
@@ -5080,12 +5160,36 @@ function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
5080
5160
  }
5081
5161
  //#endregion
5082
5162
  //#region src/list-kit/pieces/list-bulk-bar.tsx
5163
+ function LinkDetachBulkButton(props) {
5164
+ const { selection } = useResourceListContext();
5165
+ const { resource } = useListSurface();
5166
+ const t = useOptionalT();
5167
+ const [busy, setBusy] = useState(false);
5168
+ return jsxs(Button, {
5169
+ type: "button",
5170
+ size: "sm",
5171
+ variant: "ghost",
5172
+ className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
5173
+ disabled: busy,
5174
+ "data-testid": `list-link-detach-bulk:${resource}`,
5175
+ onClick: () => {
5176
+ setBusy(true);
5177
+ props.onDetach(selection.records).finally(() => setBusy(false));
5178
+ },
5179
+ children: [jsx(IconUnlink, {
5180
+ className: "size-4",
5181
+ "aria-hidden": "true"
5182
+ }), t("field.link.detach", { defaultValue: "Detach" })]
5183
+ });
5184
+ }
5083
5185
  function ListBulkBarView() {
5084
5186
  usePieceMount("bulk-bar");
5085
5187
  const { selection, columns, scope } = useResourceListContext();
5086
- const { resource, tableView, readOnly } = useListSurface();
5087
- if (!tableView || readOnly || selection.ids.length === 0) return null;
5188
+ const { resource, tableView, readOnly, linkEditing } = useListSurface();
5189
+ const detach = readOnly ? linkEditing?.detach : void 0;
5190
+ if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
5088
5191
  return jsx(BulkActionToolbar, {
5192
+ ...detach === void 0 ? {} : { actions: jsx(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
5089
5193
  resource,
5090
5194
  selectedRecords: selection.records,
5091
5195
  selectedIds: selection.ids,
@@ -5174,7 +5278,8 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
5174
5278
  }
5175
5279
  //#endregion
5176
5280
  //#region src/list-kit/columns-toggle.tsx
5177
- function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5281
+ function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5282
+ const resource = routeResourceKey(resourceInput);
5178
5283
  const t = useT();
5179
5284
  const columnsCaps = useI18nCaps(resource);
5180
5285
  const app = useApp();
@@ -5281,12 +5386,16 @@ function ListColumnsView() {
5281
5386
  const ListColumns = listPiece("ListColumns", ListColumnsView);
5282
5387
  //#endregion
5283
5388
  //#region src/resource/create-button.tsx
5284
- function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
5389
+ function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
5390
+ const resource = routeResourceKey(resourceInput);
5285
5391
  const t = useOptionalT();
5286
5392
  const Link = useLinkComponent();
5287
5393
  const availability = useAvailability(resource, "create");
5288
5394
  const gate = useEntryGate();
5289
- const text = label ?? t("actions.create", { defaultValue: "Create" });
5395
+ const text = label ?? t("actions.create", {
5396
+ resource,
5397
+ defaultValue: "Create"
5398
+ });
5290
5399
  if (onOpen !== void 0) {
5291
5400
  const answer = gate(resource, "create");
5292
5401
  if (answer.state === "disabled") return jsx(DisabledAction, {
@@ -5357,7 +5466,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
5357
5466
  //#endregion
5358
5467
  //#region src/export/export-button.tsx
5359
5468
  const warnedProviderQueryResources = new Set();
5360
- function ExportButton({ resource, className }) {
5469
+ function ExportButton({ resource: resourceInput, className }) {
5470
+ const resource = routeResourceKey(resourceInput);
5361
5471
  if (!(useOptionalDataRuntime() !== null)) return null;
5362
5472
  return jsx(ExportButtonActive, {
5363
5473
  resource,
@@ -5378,36 +5488,19 @@ function ExportButtonActive({ resource, className }) {
5378
5488
  const tid = useTestIds();
5379
5489
  const t = useOptionalT();
5380
5490
  const [open, setOpen] = React.useState(false);
5381
- const searchContext = React.useMemo(() => ({
5382
- resource,
5383
- def: list.def
5384
- }), [resource, list.def]);
5491
+ const sent = list.query.sent;
5385
5492
  const target = React.useMemo(() => {
5386
- const filter = composeListFilter({
5387
- fixed: list.filters.fixed,
5388
- pinned: list.filters.value,
5389
- group: list.filters.group,
5390
- search: list.filters.search,
5391
- searchDecl: list.filters.searchEnabled ? list.filters.searchDecl ?? list.def?.views?.list?.search : void 0,
5392
- searchContext
5393
- });
5394
- const sort = list.sorting.value;
5493
+ if (sent === null) return null;
5494
+ const { filter, sort } = sent;
5395
5495
  return {
5396
5496
  kind: "filter",
5397
5497
  ...filter === void 0 ? {} : { filter },
5398
5498
  ...sort.length > 0 ? { sort } : {}
5399
5499
  };
5400
- }, [
5401
- list.filters.fixed,
5402
- list.filters.value,
5403
- list.filters.group,
5404
- list.filters.search,
5405
- list.filters.searchDecl,
5406
- list.filters.searchEnabled,
5407
- list.def,
5408
- searchContext,
5409
- list.sorting.value
5410
- ]);
5500
+ }, [sent]);
5501
+ const lastTarget = React.useRef(target);
5502
+ if (target !== null) lastTarget.current = target;
5503
+ const dialogTarget = target ?? lastTarget.current;
5411
5504
  React.useEffect(() => {
5412
5505
  if (!available || !capability.allowed) return;
5413
5506
  if (list.providerQuery.value === void 0) return;
@@ -5421,7 +5514,9 @@ function ExportButtonActive({ resource, className }) {
5421
5514
  resource
5422
5515
  ]);
5423
5516
  if (!available || !capability.allowed || list.isForbidden) return null;
5424
- return jsxs(Fragment$1, { children: [jsx(Button, {
5517
+ if (target === null && !open) return null;
5518
+ if (dialogTarget === null) return null;
5519
+ return jsxs(Fragment$1, { children: [target === null ? null : jsx(Button, {
5425
5520
  type: "button",
5426
5521
  variant: "outline",
5427
5522
  size: "icon",
@@ -5440,7 +5535,7 @@ function ExportButtonActive({ resource, className }) {
5440
5535
  open,
5441
5536
  onOpenChange: setOpen,
5442
5537
  resource,
5443
- target,
5538
+ target: dialogTarget,
5444
5539
  visibleColumns: list.columns.visible,
5445
5540
  estimatedCount: list.pagination.total,
5446
5541
  scope: list.scope
@@ -5458,8 +5553,9 @@ function ListExportView() {
5458
5553
  const ListExport = listPiece("ListExport", ListExportView);
5459
5554
  //#endregion
5460
5555
  //#region src/list-kit/list-search-input.tsx
5461
- function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5462
- const t = useT();
5556
+ function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
5557
+ const resource = routeResourceKey(resourceInput);
5558
+ const caps = useI18nCaps(resource);
5463
5559
  const [text, setText] = useState(value);
5464
5560
  const lastPushedRef = useRef(value);
5465
5561
  const timerRef = useRef(null);
@@ -5474,7 +5570,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5474
5570
  }, []);
5475
5571
  return jsx(Input, {
5476
5572
  type: "search",
5477
- placeholder: placeholder ?? t("toolbar.searchBy", { params: { name: label } }),
5573
+ placeholder: placeholder ?? resolveContentSlot(caps, ["list.search"]) ?? "Search",
5478
5574
  value: text,
5479
5575
  "data-testid": `list-search:${resource}`,
5480
5576
  className: "h-8 w-full min-[420px]:w-72",
@@ -5492,24 +5588,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5492
5588
  //#endregion
5493
5589
  //#region src/list-kit/filter-builder/filter-region.tsx
5494
5590
  function FilterRegion({ search } = {}) {
5495
- const { resource, def, filters, pagination, access } = useResourceListContext();
5496
- const t = useT();
5497
- const listCaps = useI18nCaps(resource);
5591
+ const { resource, def, filters, pagination } = useResourceListContext();
5592
+ const chromeT = useChromeT();
5498
5593
  const testIdBase = `list-filter:${resource}`;
5594
+ const caps = useI18nCaps(resource);
5595
+ const placeholder = search === void 0 ? resolveContentSlot(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
5499
5596
  const searchDecl = search === void 0 ? def?.views?.list?.search : search ?? void 0;
5500
- const searchField = searchDecl !== void 0 && typeof searchDecl !== "function" ? (() => {
5501
- return searchDecl.fields.map((entry) => typeof entry === "string" ? entry : entry.field).find((name) => access.fieldVisible(name));
5502
- })() : void 0;
5503
- const searchGenericLabel = resolveContentSlot(listCaps, ["list.search"]) ?? "Search";
5504
- const searchLabel = searchField ? resolveFieldLabel(listCaps, searchField, declaredText("field", def?.fields[searchField], "label")) : searchGenericLabel;
5505
- const searchPlaceholder = searchField ? t("toolbar.searchBy", { params: { name: searchLabel } }) : searchGenericLabel;
5506
5597
  return jsxs("div", {
5507
5598
  className: "flex flex-wrap items-center gap-2",
5508
5599
  children: [
5509
5600
  searchDecl !== void 0 && filters.searchEnabled && jsx(ListSearchInput, {
5510
5601
  resource,
5511
- label: searchLabel,
5512
- placeholder: searchPlaceholder,
5602
+ ...placeholder === void 0 ? {} : { placeholder },
5513
5603
  value: filters.search,
5514
5604
  onCommit: (v) => {
5515
5605
  pagination.setPage(1);
@@ -5521,19 +5611,21 @@ function FilterRegion({ search } = {}) {
5521
5611
  setGroup: filters.setGroup,
5522
5612
  available: filters.available,
5523
5613
  fixed: filters.fixed,
5614
+ presets: filters.presets,
5615
+ setPreset: filters.setPreset,
5524
5616
  pinnedFields: filters.pinnedFields,
5525
5617
  pinnedValue: filters.value,
5526
5618
  setPinnedValue: filters.set,
5527
5619
  setPage: pagination.setPage,
5528
5620
  testIdBase
5529
5621
  }),
5530
- filters.isFiltered && jsx(Button, {
5622
+ filters.canReset && jsx(Button, {
5531
5623
  type: "button",
5532
5624
  variant: "ghost",
5533
5625
  size: "sm",
5534
5626
  "data-testid": `${testIdBase}:clear-all`,
5535
- onClick: filters.clearAll,
5536
- children: t("filters.clearAll")
5627
+ onClick: filters.reset,
5628
+ children: chromeT("filters.clearAll")
5537
5629
  })
5538
5630
  ]
5539
5631
  });
@@ -5663,6 +5755,7 @@ function ViewItem({ view, caps, resource, onClick, testid }) {
5663
5755
  });
5664
5756
  }
5665
5757
  function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
5758
+ const chromeT = useChromeT();
5666
5759
  const canRename = entry.permissions.update && onRename !== void 0;
5667
5760
  const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
5668
5761
  const canDelete = entry.permissions.remove && onDelete !== void 0;
@@ -5708,8 +5801,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
5708
5801
  canDelete && jsx("button", {
5709
5802
  type: "button",
5710
5803
  onClick: onDelete,
5711
- "aria-label": tr("actions.delete", "Delete"),
5712
- title: tr("actions.delete", "Delete"),
5804
+ "aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
5805
+ title: chromeT("actions.delete", { defaultValue: "Delete" }),
5713
5806
  "data-testid": deleteTestId,
5714
5807
  className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
5715
5808
  children: jsx(IconTrash, {
@@ -5749,7 +5842,8 @@ function groupByScope(entries) {
5749
5842
  }
5750
5843
  return order.map((id) => byId.get(id));
5751
5844
  }
5752
- function SavedViewsDropdown({ resource, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
5845
+ function SavedViewsDropdown({ resource: resourceInput, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
5846
+ const resource = resourceInput === void 0 ? void 0 : routeResourceKey(resourceInput);
5753
5847
  const t = useOptionalT();
5754
5848
  const caps = useI18nCaps(resource);
5755
5849
  const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
@@ -5963,9 +6057,10 @@ function useOptionalFrameChrome() {
5963
6057
  //#endregion
5964
6058
  //#region src/record/frame-pieces.tsx
5965
6059
  function useFrameId() {
5966
- const { recordId, record } = useRecordFrame();
6060
+ const { resource, recordId, record } = useRecordFrame();
6061
+ const def = useApp().registry.get(resource);
5967
6062
  if (recordId !== void 0) return recordId;
5968
- const id = record?.id;
6063
+ const id = recordKeyOf(def, record);
5969
6064
  return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
5970
6065
  }
5971
6066
  function RecordTitleView() {
@@ -5976,7 +6071,7 @@ function RecordTitleView() {
5976
6071
  const t = useOptionalT();
5977
6072
  const title = useRecordTitle(resource, mode === "create" ? void 0 : id, record);
5978
6073
  if (useFrameChrome().hostTitle === true) return null;
5979
- const text = mode === "create" ? t("pageHeader.createTitle", { params: { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) } }) : title;
6074
+ const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) }) : title;
5980
6075
  if (text === "") return null;
5981
6076
  return jsx("h2", {
5982
6077
  className: "min-w-0 break-words text-xl font-semibold tracking-tight",
@@ -6000,6 +6095,7 @@ function RecordActionsView() {
6000
6095
  resource,
6001
6096
  record: row,
6002
6097
  surface: mode,
6098
+ sources: controller.sources,
6003
6099
  ...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
6004
6100
  }), mode === "show" ? extraActions : null]
6005
6101
  });
@@ -6029,8 +6125,15 @@ function FormActionsView() {
6029
6125
  const app = useApp();
6030
6126
  const navigate = useRouteNavigate();
6031
6127
  const t = useOptionalT();
6128
+ const chromeT = useChromeT();
6129
+ const caps = useI18nCaps();
6130
+ const labelCaps = useI18nCaps(resource);
6032
6131
  const tid = useTestIds();
6033
6132
  const id = useFrameId();
6133
+ const failedIssues = chrome.serverIssues;
6134
+ const livePlan = controller.plan;
6135
+ const alertIssues = useMemo(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : routeServerIssues({ issues: failedIssues }, (segment) => segment, (field) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active")).form, [failedIssues, livePlan]);
6136
+ const fieldDefs = app.registry.get(resource)?.fields;
6034
6137
  if (mode === "show") return null;
6035
6138
  const busy = controller.isSubmitting;
6036
6139
  const cancel = () => {
@@ -6054,21 +6157,43 @@ function FormActionsView() {
6054
6157
  const text = (issue) => resolveValidationMessage({
6055
6158
  code: issue.code ?? "",
6056
6159
  message: issue.message,
6057
- ...issue.params === void 0 ? {} : { params: issue.params }
6058
- }, { t });
6160
+ ...issue.check === void 0 ? {} : { check: issue.check }
6161
+ }, {
6162
+ has: caps.has,
6163
+ t: caps.t
6164
+ });
6059
6165
  const retryable = [];
6060
6166
  for (const issue of controller.checkOutcome?.blockers ?? []) {
6061
- const checkId = issue.params?.checkId;
6062
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
6167
+ const checkId = issue.check?.id;
6168
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
6063
6169
  if (!retryable.includes(checkId)) retryable.push(checkId);
6064
6170
  }
6065
- const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t);
6171
+ const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
6172
+ const serverIssues = bySeverity(alertIssues).map((issue) => {
6173
+ const head = issue.path[0];
6174
+ const name = typeof head === "string" ? head : void 0;
6175
+ const declared = name === void 0 ? void 0 : fieldDefs?.[name];
6176
+ const message = resolveValidationMessage({
6177
+ code: issue.code,
6178
+ message: issue.message,
6179
+ ...issue.check === void 0 ? {} : { check: issue.check }
6180
+ }, {
6181
+ has: caps.has,
6182
+ t: caps.t,
6183
+ ...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
6184
+ });
6185
+ return {
6186
+ label: declared === void 0 || name === void 0 ? void 0 : resolveFieldLabel(labelCaps, name, declaredText("field", declared, "label")),
6187
+ message,
6188
+ severity: issue.severity
6189
+ };
6190
+ });
6066
6191
  const steps = controller.steps;
6067
6192
  const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
6068
6193
  return jsxs("div", {
6069
6194
  className: "flex flex-col gap-3",
6070
6195
  children: [
6071
- failure === void 0 && formErrors.length === 0 ? null : jsxs("div", {
6196
+ failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : jsxs("div", {
6072
6197
  role: "alert",
6073
6198
  className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
6074
6199
  children: [
@@ -6078,10 +6203,22 @@ function FormActionsView() {
6078
6203
  }),
6079
6204
  jsxs("div", {
6080
6205
  className: "leading-snug",
6081
- children: [failure === void 0 ? null : jsx("p", {
6082
- className: "font-medium",
6083
- children: failure
6084
- }), formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))]
6206
+ children: [
6207
+ failure === void 0 ? null : jsx("p", {
6208
+ className: "font-medium",
6209
+ children: failure
6210
+ }),
6211
+ serverIssues.map((issue, index) => jsxs("p", {
6212
+ "data-testid": `form-server-issue:${resource}:${mode}:${index}`,
6213
+ "data-buro-severity": issue.severity,
6214
+ className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
6215
+ children: [issue.label === void 0 ? null : jsxs("span", {
6216
+ className: "font-medium",
6217
+ children: [issue.label, ": "]
6218
+ }), issue.message]
6219
+ }, index)),
6220
+ formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))
6221
+ ]
6085
6222
  }),
6086
6223
  failure !== void 0 && steps !== null ? jsx(Button, {
6087
6224
  type: "button",
@@ -6090,7 +6227,7 @@ function FormActionsView() {
6090
6227
  className: "ml-auto",
6091
6228
  onMouseDown: (event) => event.preventDefault(),
6092
6229
  onClick: () => void chrome.submit(),
6093
- children: t("actions.retry", { defaultValue: "Retry" })
6230
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6094
6231
  }) : null
6095
6232
  ]
6096
6233
  }),
@@ -6102,7 +6239,7 @@ function FormActionsView() {
6102
6239
  size: "sm",
6103
6240
  "data-testid": `form-check-retry:${checkId}`,
6104
6241
  onClick: () => void controller.revalidate(),
6105
- children: t("actions.retry", { defaultValue: "Retry" })
6242
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6106
6243
  }, checkId))
6107
6244
  }),
6108
6245
  formWarnings.length === 0 ? null : jsxs("div", {
@@ -6128,7 +6265,7 @@ function FormActionsView() {
6128
6265
  children: [jsx(IconLoader2, {
6129
6266
  className: "h-3.5 w-3.5 animate-spin",
6130
6267
  "aria-hidden": "true"
6131
- }), jsx("span", { children: t("actions.validating", { defaultValue: "Validating…" }) })]
6268
+ }), jsx("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
6132
6269
  }) : null,
6133
6270
  jsx(Button, {
6134
6271
  type: "button",
@@ -6136,7 +6273,7 @@ function FormActionsView() {
6136
6273
  onClick: cancel,
6137
6274
  onMouseDown: (event) => event.preventDefault(),
6138
6275
  "data-testid": tid.formCancel(resource, mode),
6139
- children: t("actions.cancel", { defaultValue: "Cancel" })
6276
+ children: chromeT("actions.cancel", { defaultValue: "Cancel" })
6140
6277
  }),
6141
6278
  midSteps ? null : jsxs(Button, {
6142
6279
  type: "submit",
@@ -6153,37 +6290,71 @@ function FormActionsView() {
6153
6290
  children: [busy ? jsx(IconLoader2, {
6154
6291
  className: "mr-2 h-4 w-4 animate-spin",
6155
6292
  "aria-hidden": "true"
6156
- }) : null, busy ? mode === "edit" ? t("actions.saving", { defaultValue: "Saving…" }) : t("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? t("actions.save", { defaultValue: "Save" }) : t("actions.create", { defaultValue: "Create" }))]
6293
+ }) : null, busy ? mode === "edit" ? chromeT("actions.saving", { defaultValue: "Saving…" }) : chromeT("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? chromeT("actions.save", { defaultValue: "Save" }) : chromeT("actions.create", { defaultValue: "Create" }))]
6157
6294
  })
6158
6295
  ]
6159
6296
  })
6160
6297
  ]
6161
6298
  });
6162
6299
  }
6300
+ const NO_ALERT_ISSUES = Object.freeze([]);
6301
+ const SEVERITY_RANK = {
6302
+ error: 0,
6303
+ warning: 1,
6304
+ info: 2
6305
+ };
6306
+ function bySeverity(issues) {
6307
+ return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
6308
+ }
6163
6309
  function firstPreferred(issues) {
6164
- const first = issues.find((issue) => issue.params?.checkId === void 0) ?? issues[0];
6310
+ const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
6165
6311
  return first === void 0 ? [] : [first];
6166
6312
  }
6167
6313
  function issueKey(issue) {
6168
6314
  return JSON.stringify([
6169
6315
  issue.code ?? "",
6170
6316
  issue.message.key,
6171
- issue.params ?? null
6317
+ issue.check?.id ?? null
6172
6318
  ]);
6173
6319
  }
6174
- function failureText(error, t) {
6175
- const friendly = toFriendlyError(safeMessage(error), void 0, friendlyStringsFrom(t));
6176
- return friendly.replaced ? friendly.title : t("errors.failed", { defaultValue: "Failed" });
6320
+ function failureText(error, settled, t, caps) {
6321
+ const texts = technicalTexts(error).filter((text) => text.length <= 1024);
6322
+ const strings = friendlyStringsFrom(t);
6323
+ const friendly = texts.map((text) => toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? toFriendlyError(texts[0], void 0, strings);
6324
+ const applied = settled ?? ownDataProperty(error, "applied");
6325
+ if (applied === "partial" || applied === "unknown") {
6326
+ const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
6327
+ const sentence = caps.has(key) ? caps.t(key) : applied === "partial" ? "Part of this may have been saved. Check the record before trying again." : "It may have been saved. Check the record before trying again.";
6328
+ const lead = texts.some(isNetworkFailure) ? strings.offline : "";
6329
+ return lead === "" ? sentence : `${lead} ${sentence}`;
6330
+ }
6331
+ const code = ownDataProperty(error, "code");
6332
+ const kind = ownDataProperty(error, "kind");
6333
+ const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(RECORD_KIND_KEYS, kind);
6334
+ const message = ownDataProperty(error, "message");
6335
+ if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? specificErrorText({
6336
+ code,
6337
+ ...isMessageReference(message) ? { message } : {}
6338
+ }, caps, { record: true }) : void 0) ?? friendly.title;
6339
+ if (typeof code !== "string") return strings.title;
6340
+ const reference = {
6341
+ code,
6342
+ ...isMessageReference(message) ? { message } : {}
6343
+ };
6344
+ return specificErrorText(reference, caps, { record: true }) ?? (typeof kind === "string" ? kindErrorText(kind, caps, { record: true }) : void 0) ?? strings.title;
6177
6345
  }
6178
- function safeMessage(error) {
6346
+ function ownDataProperty(error, name) {
6179
6347
  if (error === null || typeof error !== "object") return void 0;
6180
6348
  try {
6181
- const descriptor = Object.getOwnPropertyDescriptor(error, "message");
6182
- return descriptor !== void 0 && "value" in descriptor && typeof descriptor.value === "string" && descriptor.value.length <= 1024 ? descriptor.value : void 0;
6349
+ const descriptor = Object.getOwnPropertyDescriptor(error, name);
6350
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
6183
6351
  } catch {
6184
6352
  return;
6185
6353
  }
6186
6354
  }
6355
+ function isMessageReference(value) {
6356
+ return value !== null && typeof value === "object" && typeof value.key === "string";
6357
+ }
6187
6358
  function inlineFlowNames(screen, plan) {
6188
6359
  if (screen === null) return [];
6189
6360
  const names = [];
@@ -6456,31 +6627,35 @@ function PlannedFrame(props) {
6456
6627
  })
6457
6628
  }, name);
6458
6629
  const { PlacementScope } = page;
6630
+ const claimedRoute = useContext(RouteSourceContext);
6459
6631
  const itemNode = page.plan.slots.item?.[0];
6460
6632
  const item = useMemo(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
6461
6633
  return jsx(RouteSourceContext.Provider, {
6462
6634
  value: null,
6463
- children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
6464
- resource,
6465
- mode: "show",
6466
- compiled,
6467
- page,
6468
- children: jsx(ListItemContext.Provider, {
6469
- value: item,
6470
- children: jsx(FrameView, {
6471
- resource,
6472
- compiled,
6473
- slots
6635
+ children: jsx(ActionRouteContext.Provider, {
6636
+ value: claimedRoute,
6637
+ children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
6638
+ resource,
6639
+ mode: "show",
6640
+ compiled,
6641
+ page,
6642
+ children: jsx(ListItemContext.Provider, {
6643
+ value: item,
6644
+ children: jsx(FrameView, {
6645
+ resource,
6646
+ compiled,
6647
+ slots
6648
+ })
6474
6649
  })
6475
- })
6476
- }) })
6650
+ }) })
6651
+ })
6477
6652
  });
6478
6653
  }
6479
6654
  function ListFrameHost({ resource }) {
6480
6655
  const compiled = useCompiledListScreen(resource);
6481
6656
  const hasDataRuntime = useOptionalDataRuntime() !== null;
6482
6657
  const { def } = useResourceListContext();
6483
- const rowsView = (def?.views?.list)?.view ?? "table";
6658
+ const rowsView = def?.views?.list?.initial?.view ?? "table";
6484
6659
  if (compiled === null) return jsx(Arrangement, {});
6485
6660
  if (compiled.nodes.length === 0 && rowsView === "table") {
6486
6661
  const empty = {};
@@ -6499,8 +6674,12 @@ function ListFrameHost({ resource }) {
6499
6674
  }
6500
6675
  //#endregion
6501
6676
  //#region src/list-kit/resource-list-surface.tsx
6502
- const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BWZ9GfOG.js")).RelatedCards }));
6503
- const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-yrPsjA3F.js")).RelatedTimeline }));
6677
+ function declaresDisplay(field) {
6678
+ return field !== void 0 && resolveSurfaceUi(field, "column").use !== void 0;
6679
+ }
6680
+ const NO_INITIAL_FILTERS = Object.freeze({});
6681
+ const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BIqqY69Y.js")).RelatedCards }));
6682
+ const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-CRh7xwLD.js")).RelatedTimeline }));
6504
6683
  function discriminatorOptionLabel(field, value) {
6505
6684
  const text = fieldOptionText(field, value);
6506
6685
  if (text !== void 0) return text;
@@ -6508,10 +6687,17 @@ function discriminatorOptionLabel(field, value) {
6508
6687
  const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
6509
6688
  return typeof label === "string" ? label : value;
6510
6689
  }
6511
- function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize, search: searchField, columns: columnNames, open, title, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, exportable, meta, renderCell, emptyState }) {
6690
+ function declaredInitialSort(def) {
6691
+ return def?.views?.list?.initial?.sort;
6692
+ }
6693
+ function declaredRouteView(def) {
6694
+ return def?.views?.list?.initial?.view;
6695
+ }
6696
+ function ResourceListSurface({ resource, variant, state, fixedFilters, initial, search: searchField, columns: columnEntries, open, title, empty, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, linkEditing, exportable, layoutId, meta, renderCell, emptyState }) {
6512
6697
  const route = variant === "route";
6513
6698
  const t = useT();
6514
6699
  const optionalT = useOptionalT();
6700
+ const chromeT = useChromeT();
6515
6701
  const tid = useTestIds();
6516
6702
  const listCaps = useI18nCaps(resource);
6517
6703
  const RouteFiltersSlot = useListSlot("filters", resource);
@@ -6526,17 +6712,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6526
6712
  console.error(`[burojs] list.body-removed: list.body.${canonicalResourceName(resource)} is no longer rendered; a whole-body override is a list frame (views.list.frame).`);
6527
6713
  }, [removedBody, resource]);
6528
6714
  const searchDecl = useMemo(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
6529
- const list = useResourceList(resource, route ? { meta } : {
6715
+ const appRegistry = useApp().registry;
6716
+ const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
6717
+ const timeline = effectiveView === "timeline";
6718
+ const initialKey = JSON.stringify(initial ?? null);
6719
+ const placementInitial = useMemo(() => route ? void 0 : {
6720
+ ...initial,
6721
+ filters: initial?.filters ?? NO_INITIAL_FILTERS
6722
+ }, [route, initialKey]);
6723
+ const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
6724
+ const columnNames = useMemo(() => columnEntries?.map(columnName), [columnEntries]);
6725
+ const list = useResourceList(resource, route ? {
6726
+ meta,
6727
+ ...timeline ? { timeline } : {}
6728
+ } : {
6729
+ ...timeline ? { timeline } : {},
6530
6730
  ...state === void 0 ? {} : { state },
6531
6731
  ...fixedFilters === void 0 ? {} : { fixedFilters },
6532
- ...pageSize === void 0 ? {} : { pageSize },
6732
+ ...placementInitial === void 0 ? {} : { initial: placementInitial },
6533
6733
  ...searchDecl === void 0 ? {} : { search: searchDecl },
6534
- ...columnNames === void 0 ? {} : { columns: columnNames },
6734
+ ...columnEntries === void 0 ? {} : { columns: columnEntries },
6535
6735
  ...scope === void 0 ? {} : { scope },
6536
6736
  ...countPolicy === void 0 ? {} : { countPolicy }
6537
6737
  });
6538
6738
  const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
6539
6739
  policyRowsOf(list, list.rows);
6740
+ const selectionRef = useRef(selection);
6741
+ selectionRef.current = selection;
6742
+ const onDetach = linkEditing?.detach?.onDetach;
6743
+ const surfaceLinkEditing = useMemo(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
6744
+ const left = await onDetach(detached);
6745
+ if (left.length > 0) {
6746
+ const gone = new Set(left);
6747
+ const current = selectionRef.current;
6748
+ if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
6749
+ }
6750
+ return left;
6751
+ } } }, [onDetach]);
6752
+ const onAttach = linkEditing?.attach?.onAttach;
6753
+ const attachBusy = linkEditing?.attach?.busy === true;
6540
6754
  const hasDataRuntime = useOptionalDataRuntime() !== null;
6541
6755
  const scopeLabel = (scope) => scope.id === LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
6542
6756
  const createAvailable = useAvailability(resource, "create").state === "available";
@@ -6584,7 +6798,6 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6584
6798
  resource,
6585
6799
  routing.rowHref
6586
6800
  ]);
6587
- const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
6588
6801
  const tableView = effectiveView === "table";
6589
6802
  const rowsViewFor = useMemo(() => tableView ? void 0 : (planned) => ({
6590
6803
  skeleton: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
@@ -6596,11 +6809,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6596
6809
  item: route ? planned : item,
6597
6810
  columns: route ? columns.visible : columnNames,
6598
6811
  rowQuick,
6599
- rowHref
6812
+ rowHref,
6813
+ ...empty === void 0 ? {} : { empty }
6600
6814
  };
6601
6815
  return jsx(Suspense, {
6602
6816
  fallback: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
6603
- children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, { ...common })
6817
+ children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
6818
+ ...common,
6819
+ axis: timelineAxis
6820
+ })
6604
6821
  });
6605
6822
  }
6606
6823
  }), [
@@ -6613,7 +6830,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6613
6830
  columns.visible,
6614
6831
  columnNames,
6615
6832
  rowQuick,
6616
- rowHref
6833
+ rowHref,
6834
+ empty,
6835
+ timelineAxis
6617
6836
  ]);
6618
6837
  const pageTitle = resolveResourceLabel(listCaps, resource, "plural", def);
6619
6838
  const [saveOpen, setSaveOpen] = useState(false);
@@ -6695,6 +6914,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6695
6914
  toast.error(t("toolbar.viewsForkFailed"));
6696
6915
  }
6697
6916
  };
6917
+ const columnNamesDisplay = (col, field) => {
6918
+ const resolved = list.columns.ui[col];
6919
+ return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
6920
+ };
6698
6921
  const renderCellLadder = (col, value, row) => {
6699
6922
  if (def?.types && col === def.types.by) {
6700
6923
  const raw = typeof value === "string" ? value : "";
@@ -6713,13 +6936,14 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6713
6936
  children: label
6714
6937
  });
6715
6938
  }
6939
+ const custom = (renderCell ?? CellSlot)?.(col, value, row);
6940
+ if (custom !== void 0) return custom;
6716
6941
  const fieldDef = def?.fields[col];
6717
- const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
6718
- if (enumName && typeof value === "string") return jsx(EnumBadge, {
6942
+ const enumName = enumBindingKey(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
6943
+ if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return jsx(EnumBadge, {
6719
6944
  enumName,
6720
6945
  value
6721
6946
  });
6722
- return (renderCell ?? CellSlot)?.(col, value, row);
6723
6947
  };
6724
6948
  const surface = {
6725
6949
  resource,
@@ -6747,10 +6971,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6747
6971
  }
6748
6972
  },
6749
6973
  rowsViewFor,
6750
- emptyState,
6974
+ emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? jsx("p", {
6975
+ "data-buro-region": "empty",
6976
+ className: "text-sm text-muted-foreground",
6977
+ children: empty
6978
+ }) : emptyState,
6751
6979
  renderCell: renderCellLadder,
6752
6980
  readOnly: readOnly === true,
6753
- exportable: exportable !== false
6981
+ exportable: exportable !== false,
6982
+ ...layoutId === void 0 ? {} : { layoutId },
6983
+ ...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
6754
6984
  };
6755
6985
  return jsx(ResourceListProvider, {
6756
6986
  value: list,
@@ -6763,22 +6993,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6763
6993
  resource,
6764
6994
  mode: "list",
6765
6995
  title: pageTitle
6766
- }) : title === void 0 && onCreate === void 0 ? null : jsxs("div", {
6996
+ }) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : jsxs("div", {
6767
6997
  className: "flex items-center justify-between gap-2",
6768
- children: [title === void 0 ? jsx("span", {}) : jsx("h3", {
6769
- className: "text-sm font-semibold",
6770
- children: title
6771
- }), onCreate === void 0 ? null : jsxs(Button, {
6772
- type: "button",
6773
- size: "sm",
6774
- variant: "outline",
6775
- "data-testid": `list-create:${resource}`,
6776
- onClick: onCreate,
6777
- children: [jsx(IconPlus, {
6778
- className: "mr-1 h-4 w-4",
6779
- "aria-hidden": "true"
6780
- }), optionalT("actions.create", { defaultValue: "Create" })]
6781
- })]
6998
+ children: [
6999
+ title === void 0 ? jsx("span", {}) : jsx("h3", {
7000
+ className: "text-sm font-semibold",
7001
+ children: title
7002
+ }),
7003
+ onAttach === void 0 ? null : jsxs(Button, {
7004
+ type: "button",
7005
+ size: "sm",
7006
+ variant: "outline",
7007
+ "data-testid": `list-link-attach:${resource}`,
7008
+ onClick: onAttach,
7009
+ disabled: attachBusy,
7010
+ "aria-busy": attachBusy,
7011
+ children: [attachBusy ? jsx(IconLoader2, {
7012
+ className: "mr-1 h-4 w-4 animate-spin",
7013
+ "aria-hidden": "true"
7014
+ }) : jsx(IconLink, {
7015
+ className: "mr-1 h-4 w-4",
7016
+ "aria-hidden": "true"
7017
+ }), optionalT("field.link.attach", { defaultValue: "Attach" })]
7018
+ }),
7019
+ onCreate === void 0 ? null : jsxs(Button, {
7020
+ type: "button",
7021
+ size: "sm",
7022
+ variant: "outline",
7023
+ "data-testid": `list-create:${resource}`,
7024
+ onClick: onCreate,
7025
+ children: [jsx(IconPlus, {
7026
+ className: "mr-1 h-4 w-4",
7027
+ "aria-hidden": "true"
7028
+ }), optionalT("actions.create", {
7029
+ resource,
7030
+ defaultValue: "Create"
7031
+ })]
7032
+ })
7033
+ ]
6782
7034
  }),
6783
7035
  hasDataRuntime && jsx(ExportRunWatcherMount, {}),
6784
7036
  droppedPredicates > 0 && jsx("p", {
@@ -6827,13 +7079,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6827
7079
  variant: "outline",
6828
7080
  onClick: () => setSaveOpen(false),
6829
7081
  "data-testid": `list-saved-view-save-cancel:${resource}`,
6830
- children: t("actions.cancel")
7082
+ children: chromeT("actions.cancel")
6831
7083
  }), jsx(Button, {
6832
7084
  type: "button",
6833
7085
  onClick: handleSaveConfirm,
6834
7086
  disabled: saveLabel.trim().length === 0,
6835
7087
  "data-testid": `list-saved-view-save-confirm:${resource}`,
6836
- children: t("actions.save")
7088
+ children: chromeT("actions.save")
6837
7089
  })] })
6838
7090
  ]
6839
7091
  })
@@ -6870,7 +7122,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6870
7122
  variant: "outline",
6871
7123
  onClick: () => handleDeleteUserView(entry.id),
6872
7124
  "data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
6873
- children: t("actions.delete")
7125
+ children: chromeT("actions.delete")
6874
7126
  }) : jsx("span", {
6875
7127
  className: "shrink-0 text-xs text-muted-foreground",
6876
7128
  children: t("toolbar.viewsReadOnly")
@@ -6882,7 +7134,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6882
7134
  variant: "outline",
6883
7135
  onClick: () => setManageOpen(false),
6884
7136
  "data-testid": `list-saved-view-manage-close:${resource}`,
6885
- children: t("actions.close")
7137
+ children: chromeT("actions.close")
6886
7138
  }) })
6887
7139
  ]
6888
7140
  })
@@ -6915,13 +7167,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6915
7167
  setRenameTarget(null);
6916
7168
  },
6917
7169
  "data-testid": `list-saved-view-rename-cancel:${resource}`,
6918
- children: t("actions.cancel")
7170
+ children: chromeT("actions.cancel")
6919
7171
  }), jsx(Button, {
6920
7172
  type: "button",
6921
7173
  onClick: handleRenameConfirm,
6922
7174
  disabled: renameLabel.trim().length === 0,
6923
7175
  "data-testid": `list-saved-view-rename-confirm:${resource}`,
6924
- children: t("actions.save")
7176
+ children: chromeT("actions.save")
6925
7177
  })] })
6926
7178
  ]
6927
7179
  })
@@ -6956,7 +7208,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6956
7208
  setForkTarget(null);
6957
7209
  },
6958
7210
  "data-testid": `list-saved-view-fork-cancel:${resource}`,
6959
- children: t("actions.cancel")
7211
+ children: chromeT("actions.cancel")
6960
7212
  }), jsx(Button, {
6961
7213
  type: "button",
6962
7214
  onClick: handleForkConfirm,
@@ -6979,6 +7231,582 @@ function ExportRunWatcherMount() {
6979
7231
  return null;
6980
7232
  }
6981
7233
  //#endregion
7234
+ //#region src/fields/use-relation-picker.tsx
7235
+ const noop = () => void 0;
7236
+ function safeKey(input) {
7237
+ if (input === void 0 || input === null) return void 0;
7238
+ try {
7239
+ return resourceKey(input);
7240
+ } catch {
7241
+ return;
7242
+ }
7243
+ }
7244
+ function sessionIdentity(runtime) {
7245
+ const state = runtime.dataSession.getState();
7246
+ const snapshot = state.status === "ready" ? state.snapshot : state.target;
7247
+ const selection = snapshot.scope?.selection;
7248
+ const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
7249
+ let scopeKey;
7250
+ try {
7251
+ scopeKey = JSON.stringify(partition) ?? "";
7252
+ } catch {
7253
+ scopeKey = String(snapshot.scopeKey);
7254
+ }
7255
+ return {
7256
+ sessionKey: snapshot.sessionKey,
7257
+ scopeKey
7258
+ };
7259
+ }
7260
+ function PickerHost(props) {
7261
+ const { register } = props;
7262
+ React.useEffect(() => register(), [register]);
7263
+ return jsx(Fragment$1, { children: props.children });
7264
+ }
7265
+ function useRelationPicker(options) {
7266
+ const app = useApp();
7267
+ const runtime = useDataRuntime();
7268
+ const frame = useOptionalRecordFrame();
7269
+ const frames = useRecordScopeFrames();
7270
+ const multiple = options.multiple === true;
7271
+ const fieldName = options.field;
7272
+ let to;
7273
+ let declaredBy;
7274
+ let declaredCandidates;
7275
+ if (fieldName !== void 0) {
7276
+ const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
7277
+ to = safeKey(meta?.to);
7278
+ declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
7279
+ declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
7280
+ } else to = safeKey(options.to);
7281
+ const target = to ?? "";
7282
+ const by = options.valueField ?? declaredBy;
7283
+ const scope = options.scope ?? "context";
7284
+ const selection = useCandidateSelection({
7285
+ to,
7286
+ candidates: options.candidates ?? declaredCandidates,
7287
+ sources: frame?.controller.sources,
7288
+ resource: frame?.resource ?? "",
7289
+ fieldName: fieldName ?? target,
7290
+ fixedFilters: options.fixedFilters
7291
+ });
7292
+ const held = selection.gate !== void 0;
7293
+ const pendingRef = React.useRef(null);
7294
+ const [active, setActive] = React.useState(null);
7295
+ const activeRef = React.useRef(null);
7296
+ activeRef.current = active;
7297
+ const rowsRef = React.useRef(new Map());
7298
+ const createdRowRef = React.useRef(void 0);
7299
+ const alive = React.useRef(true);
7300
+ const mounts = React.useRef(0);
7301
+ const sequence = React.useRef(0);
7302
+ const latest = React.useRef({
7303
+ multiple,
7304
+ select: options.select,
7305
+ idField: "id",
7306
+ valueField: "id",
7307
+ available: false,
7308
+ frames
7309
+ });
7310
+ const project = React.useCallback((row) => {
7311
+ const select = latest.current.select;
7312
+ if (select === void 0) return { ...row };
7313
+ const out = {};
7314
+ const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
7315
+ if (key !== void 0) out.id = key;
7316
+ const { valueField } = latest.current;
7317
+ if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
7318
+ for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
7319
+ return out;
7320
+ }, []);
7321
+ const settle = React.useCallback((result) => {
7322
+ const pending = pendingRef.current;
7323
+ if (pending === null) return;
7324
+ pendingRef.current = null;
7325
+ pending.detach();
7326
+ if (alive.current) setActive(null);
7327
+ pending.resolve(result);
7328
+ }, []);
7329
+ const toggle = React.useCallback((key, row) => {
7330
+ setActive((current) => {
7331
+ if (current === null) return current;
7332
+ const has = current.selected.some((k) => String(k) === String(key));
7333
+ if (has) rowsRef.current.delete(String(key));
7334
+ else if (row !== void 0) rowsRef.current.set(String(key), row);
7335
+ const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
7336
+ const next = {
7337
+ ...current,
7338
+ selected
7339
+ };
7340
+ activeRef.current = next;
7341
+ return next;
7342
+ });
7343
+ }, []);
7344
+ const onValue = React.useCallback((value) => {
7345
+ const row = createdRowRef.current;
7346
+ createdRowRef.current = void 0;
7347
+ if (latest.current.multiple) {
7348
+ const current = activeRef.current;
7349
+ if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
7350
+ return;
7351
+ }
7352
+ settle({
7353
+ kind: "selected",
7354
+ keys: [value],
7355
+ rows: row === void 0 ? [] : [project(row)]
7356
+ });
7357
+ }, [
7358
+ project,
7359
+ settle,
7360
+ toggle
7361
+ ]);
7362
+ const wiring = useRelationSelect({
7363
+ to: target,
7364
+ by,
7365
+ fixedFilters: selection.fixedFilters,
7366
+ scope,
7367
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7368
+ held,
7369
+ onValue
7370
+ });
7371
+ latest.current = {
7372
+ multiple,
7373
+ select: options.select,
7374
+ idField: wiring.idField,
7375
+ valueField: wiring.valueField,
7376
+ available: to !== void 0 && wiring.targetDef !== void 0 && !held,
7377
+ frames
7378
+ };
7379
+ const { create: shared } = wiring;
7380
+ const create = React.useMemo(() => ({
7381
+ ...shared,
7382
+ start: (openCreate) => shared.start(() => {
7383
+ const opened = openCreate();
7384
+ opened.then((row) => {
7385
+ createdRowRef.current = row === null ? void 0 : { ...row };
7386
+ }, noop);
7387
+ return opened;
7388
+ })
7389
+ }), [shared]);
7390
+ React.useEffect(() => runtime.dataSession.subscribe(() => {
7391
+ const pending = pendingRef.current;
7392
+ if (pending === null) return;
7393
+ const now = sessionIdentity(runtime);
7394
+ if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
7395
+ kind: "cancelled",
7396
+ reason: "session-changed"
7397
+ });
7398
+ }), [runtime, settle]);
7399
+ React.useEffect(() => {
7400
+ alive.current = true;
7401
+ return () => {
7402
+ alive.current = false;
7403
+ settle({
7404
+ kind: "cancelled",
7405
+ reason: "unmounted"
7406
+ });
7407
+ };
7408
+ }, [settle]);
7409
+ React.useEffect(() => {
7410
+ if (held) settle({
7411
+ kind: "cancelled",
7412
+ reason: "unavailable"
7413
+ });
7414
+ }, [held, settle]);
7415
+ const register = React.useCallback(() => {
7416
+ mounts.current += 1;
7417
+ return () => {
7418
+ mounts.current -= 1;
7419
+ if (mounts.current === 0) settle({
7420
+ kind: "cancelled",
7421
+ reason: "unmounted"
7422
+ });
7423
+ };
7424
+ }, [settle]);
7425
+ const open = React.useCallback((opts = {}) => new Promise((resolve) => {
7426
+ settle({
7427
+ kind: "cancelled",
7428
+ reason: "superseded"
7429
+ });
7430
+ if (mounts.current === 0) {
7431
+ 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.");
7432
+ resolve({
7433
+ kind: "cancelled",
7434
+ reason: "not-mounted"
7435
+ });
7436
+ return;
7437
+ }
7438
+ const { signal } = opts;
7439
+ if (signal?.aborted === true) {
7440
+ resolve({
7441
+ kind: "cancelled",
7442
+ reason: "aborted"
7443
+ });
7444
+ return;
7445
+ }
7446
+ if (!latest.current.available) {
7447
+ resolve({
7448
+ kind: "cancelled",
7449
+ reason: "unavailable"
7450
+ });
7451
+ return;
7452
+ }
7453
+ sequence.current += 1;
7454
+ const id = sequence.current;
7455
+ const onAbort = () => {
7456
+ if (pendingRef.current?.id === id) settle({
7457
+ kind: "cancelled",
7458
+ reason: "aborted"
7459
+ });
7460
+ };
7461
+ signal?.addEventListener("abort", onAbort, { once: true });
7462
+ pendingRef.current = {
7463
+ id,
7464
+ ...sessionIdentity(runtime),
7465
+ resolve,
7466
+ detach: () => signal?.removeEventListener("abort", onAbort)
7467
+ };
7468
+ rowsRef.current = new Map();
7469
+ createdRowRef.current = void 0;
7470
+ const next = {
7471
+ id,
7472
+ frames: latest.current.frames,
7473
+ selected: opts.selected ?? []
7474
+ };
7475
+ activeRef.current = next;
7476
+ setActive(next);
7477
+ }), [runtime, settle]);
7478
+ const onSelectRow = React.useCallback((row) => {
7479
+ const field = wiring.valueField;
7480
+ const value = field === "id" ? row.id : row[field];
7481
+ if (typeof value !== "string" && typeof value !== "number") return;
7482
+ settle({
7483
+ kind: "selected",
7484
+ keys: [value],
7485
+ rows: [project(row)]
7486
+ });
7487
+ }, [
7488
+ wiring.valueField,
7489
+ settle,
7490
+ project
7491
+ ]);
7492
+ const onDone = React.useCallback(() => {
7493
+ const keys = activeRef.current?.selected ?? [];
7494
+ const rows = keys.flatMap((key) => {
7495
+ const row = rowsRef.current.get(String(key));
7496
+ return row === void 0 ? [] : [project(row)];
7497
+ });
7498
+ settle({
7499
+ kind: "selected",
7500
+ keys,
7501
+ rows
7502
+ });
7503
+ }, [settle, project]);
7504
+ const onOpenChange = React.useCallback((next) => {
7505
+ if (!next) settle({
7506
+ kind: "cancelled",
7507
+ reason: "dismissed"
7508
+ });
7509
+ }, [settle]);
7510
+ return {
7511
+ open,
7512
+ element: jsx(PickerHost, {
7513
+ register,
7514
+ children: active === null || held ? null : jsxs(RecordScopeChain, {
7515
+ frames: active.frames,
7516
+ children: [jsx(LazyRelationPickerDialog, {
7517
+ to: target,
7518
+ open: true,
7519
+ onOpenChange,
7520
+ onSelect: noop,
7521
+ onSelectRow,
7522
+ valueField: wiring.valueField,
7523
+ ...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
7524
+ scope,
7525
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7526
+ ...options.create === void 0 ? {} : {
7527
+ create,
7528
+ ...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
7529
+ },
7530
+ fieldName: fieldName ?? target,
7531
+ ...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
7532
+ ...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
7533
+ ...multiple ? {
7534
+ multiple: true,
7535
+ selected: active.selected,
7536
+ onToggle: (value, row) => toggle(value, row),
7537
+ onDone
7538
+ } : {}
7539
+ }), wiring.reader]
7540
+ }, active.id)
7541
+ })
7542
+ };
7543
+ }
7544
+ //#endregion
7545
+ //#region src/blocks/link-write-summary.tsx
7546
+ function LinkWriteSummary(props) {
7547
+ const { report, titles, onClose } = props;
7548
+ const testid = props.testid ?? "link-write-summary";
7549
+ const t = useOptionalT();
7550
+ const caps = useI18nCaps();
7551
+ const chromeT = useChromeT();
7552
+ const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
7553
+ return jsx(Dialog, {
7554
+ open: true,
7555
+ onOpenChange: (open) => {
7556
+ if (!open) onClose();
7557
+ },
7558
+ children: jsxs(DialogContent, {
7559
+ "data-testid": testid,
7560
+ className: "flex max-h-[85vh] flex-col overflow-hidden",
7561
+ children: [
7562
+ jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), jsx(DialogDescription, {
7563
+ className: "sr-only",
7564
+ children: heading
7565
+ })] }),
7566
+ jsx("ul", {
7567
+ className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
7568
+ children: report.failed.map(({ id, error }) => jsxs("li", {
7569
+ "data-testid": `${testid}:item:${id}`,
7570
+ className: "text-sm",
7571
+ children: [jsx("span", {
7572
+ className: "font-medium",
7573
+ children: titles[id] ?? id
7574
+ }), jsx("span", {
7575
+ className: "text-destructive",
7576
+ children: `: ${resolveErrorText(error, caps)}`
7577
+ })]
7578
+ }, id))
7579
+ }),
7580
+ jsx(DialogFooter, { children: jsx(Button, {
7581
+ type: "button",
7582
+ onClick: onClose,
7583
+ "data-testid": `${testid}:close`,
7584
+ children: chromeT("actions.close", { defaultValue: "Close" })
7585
+ }) })
7586
+ ]
7587
+ })
7588
+ });
7589
+ }
7590
+ //#endregion
7591
+ //#region src/blocks/link-attach.tsx
7592
+ const NO_ISSUES = Object.freeze([]);
7593
+ function useLinkPanelWrites(options) {
7594
+ const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
7595
+ const app = useApp();
7596
+ const runtime = useDataRuntime();
7597
+ const t = useOptionalT();
7598
+ const rights = useLinkRights(owner, link, {
7599
+ surface: "panel",
7600
+ record,
7601
+ ...placement === void 0 ? {} : { placement }
7602
+ });
7603
+ const writer = useLinkWriter(owner, link);
7604
+ const linkedKeys = useLinkedKeys(owner, link);
7605
+ const reverse = useMemo(() => reverseLinkOf((name) => app.registry.get(name)?.fields, owner, link), [
7606
+ app.registry,
7607
+ owner,
7608
+ link
7609
+ ]);
7610
+ let liveHides = false;
7611
+ if (reverse !== void 0) try {
7612
+ const { providerId } = runtime.app.resolveDataBinding(target);
7613
+ liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && linkCapabilitiesOf(runtime, providerId)?.links?.filter.none !== false;
7614
+ } catch {
7615
+ liveHides = false;
7616
+ }
7617
+ const liveHidesRef = useRef(liveHides);
7618
+ liveHidesRef.current = liveHides;
7619
+ const [frozenHides, setFrozenHides] = useState(null);
7620
+ const hides = frozenHides ?? liveHides;
7621
+ const ownerKeyId = JSON.stringify(ownerKey ?? null);
7622
+ const hidden = useMemo(() => hides && reverse !== void 0 ? [{
7623
+ field: reverse,
7624
+ op: "hasNone",
7625
+ value: [ownerKey]
7626
+ }] : void 0, [
7627
+ hides,
7628
+ reverse,
7629
+ ownerKeyId
7630
+ ]);
7631
+ const disabledKeys = useCallback((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
7632
+ const disabledLabel = useCallback((count) => t("field.link.alreadyLinked", {
7633
+ count,
7634
+ defaultValue: `${count} already linked`
7635
+ }), [t]);
7636
+ const picker = useRelationPicker({
7637
+ to: target,
7638
+ multiple: true,
7639
+ valueField,
7640
+ scope,
7641
+ ...hidden === void 0 ? {
7642
+ disabledKeys,
7643
+ disabledLabel
7644
+ } : { fixedFilters: hidden }
7645
+ });
7646
+ const openPicker = picker.open;
7647
+ const [summary, setSummary] = useState(null);
7648
+ const finish = useCallback((report, titles, done) => {
7649
+ if (report.outcome !== "success") {
7650
+ setSummary({
7651
+ report,
7652
+ titles
7653
+ });
7654
+ return;
7655
+ }
7656
+ const count = report.succeeded.length;
7657
+ if (count === 0) return;
7658
+ toast.success(t(`field.link.${done}`, {
7659
+ count,
7660
+ defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
7661
+ }));
7662
+ }, [t]);
7663
+ const attaching = useRef(false);
7664
+ const [attachWriting, setAttachWriting] = useState(false);
7665
+ const onAttach = useCallback(() => {
7666
+ if (attaching.current) return;
7667
+ attaching.current = true;
7668
+ (async () => {
7669
+ try {
7670
+ setFrozenHides(liveHidesRef.current);
7671
+ let result;
7672
+ try {
7673
+ result = await openPicker();
7674
+ } finally {
7675
+ setFrozenHides(null);
7676
+ }
7677
+ if (result.kind !== "selected" || result.keys.length === 0) return;
7678
+ const titles = {};
7679
+ for (const row of result.rows) {
7680
+ const key = valueField === "id" ? row.id : row[valueField];
7681
+ const title = row[displayField];
7682
+ if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
7683
+ }
7684
+ setAttachWriting(true);
7685
+ try {
7686
+ finish(await writer.attach(result.keys, record), titles, "attached");
7687
+ } finally {
7688
+ setAttachWriting(false);
7689
+ }
7690
+ } finally {
7691
+ attaching.current = false;
7692
+ }
7693
+ })();
7694
+ }, [
7695
+ displayField,
7696
+ finish,
7697
+ openPicker,
7698
+ record,
7699
+ valueField,
7700
+ writer
7701
+ ]);
7702
+ const pending = useRef(null);
7703
+ const [confirming, setConfirming] = useState(null);
7704
+ const detaching = useRef(false);
7705
+ const [busy, setBusy] = useState(false);
7706
+ const onDetach = useCallback((rows) => new Promise((resolve) => {
7707
+ pending.current?.resolve([]);
7708
+ pending.current = {
7709
+ rows,
7710
+ resolve
7711
+ };
7712
+ setConfirming(rows.length);
7713
+ }), []);
7714
+ useEffect(() => () => {
7715
+ pending.current?.resolve([]);
7716
+ pending.current = null;
7717
+ }, []);
7718
+ const cancel = useCallback(() => {
7719
+ if (detaching.current) return;
7720
+ pending.current?.resolve([]);
7721
+ pending.current = null;
7722
+ setConfirming(null);
7723
+ }, []);
7724
+ const proceed = useCallback(() => {
7725
+ const current = pending.current;
7726
+ if (current === null || detaching.current) return;
7727
+ detaching.current = true;
7728
+ setBusy(true);
7729
+ (async () => {
7730
+ try {
7731
+ const titles = {};
7732
+ for (const row of current.rows) {
7733
+ const id = row[junctionIdField];
7734
+ if (id === void 0 || id === null) continue;
7735
+ const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
7736
+ if (title !== void 0 && title !== null) titles[String(id)] = String(title);
7737
+ }
7738
+ const report = await writer.detach(current.rows, record);
7739
+ pending.current = null;
7740
+ setConfirming(null);
7741
+ finish(report, titles, "detached");
7742
+ const left = new Set(report.succeeded);
7743
+ current.resolve(current.rows.filter((row) => {
7744
+ const id = row[junctionIdField];
7745
+ return id !== void 0 && id !== null && left.has(String(id));
7746
+ }).map((row) => String(row.id)));
7747
+ } finally {
7748
+ detaching.current = false;
7749
+ setBusy(false);
7750
+ }
7751
+ })();
7752
+ }, [
7753
+ displayField,
7754
+ finish,
7755
+ junctionIdField,
7756
+ record,
7757
+ targetKey,
7758
+ writer
7759
+ ]);
7760
+ const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
7761
+ const count = confirming ?? 0;
7762
+ const gate = {
7763
+ resource: junction,
7764
+ open: confirming !== null,
7765
+ busy,
7766
+ header: t("field.link.confirmDetach", {
7767
+ count,
7768
+ defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
7769
+ }),
7770
+ blockers: NO_ISSUES,
7771
+ confirmable: NO_ISSUES,
7772
+ proceed,
7773
+ cancel
7774
+ };
7775
+ const attachAllowed = rights.attach.allowed;
7776
+ const detachAllowed = rights.detach.allowed;
7777
+ return {
7778
+ linkEditing: useMemo(() => !attachAllowed && !detachAllowed ? void 0 : {
7779
+ ...attachAllowed ? { attach: {
7780
+ onAttach,
7781
+ busy: attachWriting
7782
+ } } : {},
7783
+ ...detachAllowed ? { detach: { onDetach } } : {}
7784
+ }, [
7785
+ attachAllowed,
7786
+ attachWriting,
7787
+ detachAllowed,
7788
+ onAttach,
7789
+ onDetach
7790
+ ]),
7791
+ element: jsxs(Fragment$1, { children: [
7792
+ picker.element,
7793
+ jsx(CheckConfirmDialog, {
7794
+ state: gate,
7795
+ testid: `link-detach-confirm:${junction}`,
7796
+ title: detachLabel,
7797
+ proceedLabel: detachLabel,
7798
+ tone: "destructive"
7799
+ }),
7800
+ summary === null ? null : jsx(LinkWriteSummary, {
7801
+ report: summary.report,
7802
+ titles: summary.titles,
7803
+ onClose: () => setSummary(null),
7804
+ testid: `link-write-summary:${junction}`
7805
+ })
7806
+ ] })
7807
+ };
7808
+ }
7809
+ //#endregion
6982
7810
  //#region src/quick/peek-filter.ts
6983
7811
  function peekFieldDefinition(def, name) {
6984
7812
  return declaredField(def, name);
@@ -7002,32 +7830,12 @@ function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
7002
7830
  return peekRow(def, row, idField, capabilityFor, fieldVisible);
7003
7831
  }
7004
7832
  //#endregion
7005
- //#region src/blocks/related-list-unbound.tsx
7006
- function UnboundRelatedList(props) {
7007
- const t = useOptionalT();
7008
- return jsxs("div", {
7009
- className: "flex flex-col gap-2",
7010
- "data-testid": `list-unbound:${props.resource}`,
7011
- children: [props.title === void 0 ? null : jsx("h3", {
7012
- className: "text-sm font-semibold",
7013
- children: props.title
7014
- }), jsx("p", {
7015
- className: "text-sm text-muted-foreground",
7016
- children: t("table.empty", { defaultValue: "No records" })
7017
- })]
7018
- });
7019
- }
7020
- function placementScope(scope, owner) {
7021
- return scope ?? (owner === void 0 ? "session" : "none");
7022
- }
7023
- //#endregion
7024
7833
  //#region src/blocks/link-panel.tsx
7025
7834
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7026
7835
  const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
7027
- function qualify(ref, owner) {
7836
+ function qualify(ref) {
7028
7837
  try {
7029
- const namespace = normalizeEntityRef(owner).namespace;
7030
- return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref, { defaultNamespace: namespace }));
7838
+ return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref));
7031
7839
  } catch {
7032
7840
  return;
7033
7841
  }
@@ -7046,7 +7854,7 @@ function linkField(registry, owner, link) {
7046
7854
  function linkTarget(registry, owner, link) {
7047
7855
  const found = linkField(registry, owner, link);
7048
7856
  if (found === void 0 || owner === void 0) return void 0;
7049
- return qualify(found.field.meta?.to, owner);
7857
+ return qualify(found.field.meta?.to);
7050
7858
  }
7051
7859
  function idFieldOf(def) {
7052
7860
  const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
@@ -7062,37 +7870,47 @@ function targetColumns(registry, target, def) {
7062
7870
  const source = def;
7063
7871
  const views = isRecord(def.views) ? def.views : void 0;
7064
7872
  const list = views !== void 0 && isRecord(views.list) ? views.list : void 0;
7065
- return (list !== void 0 && Array.isArray(list.columns) ? list.columns.filter((name) => typeof name === "string") : dtoFields(source, "list")).filter((name) => {
7873
+ return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
7874
+ const name = isRecord(entry) ? entry.field : entry;
7875
+ return typeof name === "string" ? [name] : [];
7876
+ }) : dtoFields(source, "list")).filter((name) => {
7066
7877
  if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && resolveColumnField(registry, target, name) !== void 0;
7067
7878
  const field = peekFieldDefinition(source, name);
7068
7879
  return isRecord(field) && !isRelationField(field) && fieldSensitivity(field) === void 0;
7069
7880
  });
7070
7881
  }
7071
7882
  function LinkPanel(props) {
7072
- const { link, owner, own } = props;
7883
+ const { link, owner, own, empty } = props;
7073
7884
  const app = useApp();
7074
7885
  const frame = usePlacementFrame();
7075
7886
  const routeScreen = useIsRouteScreen();
7076
7887
  const record = useOptionalRecordFrame()?.record;
7888
+ const runtime = useOptionalDataRuntime();
7077
7889
  const storage = useMemo(() => {
7078
7890
  const found = linkField(app.registry, owner, link);
7079
7891
  if (found === void 0) return void 0;
7080
7892
  const { declaration } = found;
7081
- const junction = qualify(declaration.junction, owner);
7082
- const target = qualify(found.field.meta?.to, owner);
7893
+ const junction = qualify(declaration.junction);
7894
+ const target = qualify(found.field.meta?.to);
7083
7895
  if (junction === void 0 || target === void 0) return void 0;
7084
7896
  const junctionDef = app.registry.get(junction);
7085
7897
  if (junctionDef === void 0) return void 0;
7086
7898
  const junctionFields = junctionDef.fields;
7899
+ const targetDef = app.registry.get(target);
7900
+ const targetBy = byOf(junctionFields[declaration.targetKey]);
7901
+ const declaredScope = found.field.meta?.scope;
7087
7902
  return {
7088
7903
  junction,
7089
7904
  target,
7090
7905
  sourceKey: declaration.sourceKey,
7091
7906
  targetKey: declaration.targetKey,
7092
7907
  ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
7093
- targetKeyedBy: byOf(junctionFields[declaration.targetKey]) !== void 0,
7908
+ targetKeyedBy: targetBy !== void 0,
7909
+ targetValueField: targetBy ?? idFieldOf(targetDef),
7910
+ junctionIdField: idFieldOf(junctionDef),
7094
7911
  where: declaration.where,
7095
- targetDef: app.registry.get(target)
7912
+ targetDef,
7913
+ pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
7096
7914
  };
7097
7915
  }, [
7098
7916
  app.registry,
@@ -7104,10 +7922,11 @@ function LinkPanel(props) {
7104
7922
  const fixedFilters = useMemo(() => {
7105
7923
  if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
7106
7924
  const { sourceKey, targetKey } = storage;
7925
+ const placed = whereToFilter(own.where ?? [], (field) => `${targetKey}.${field}`);
7107
7926
  return [
7108
7927
  whereEntryToFilter(sourceKey, "eq", ownerKey),
7109
7928
  ...storage.where.map((entry) => whereEntryToFilter(entry.field, entry.op, entry.value)),
7110
- ...(own.where ?? []).map((entry) => whereEntryToFilter(`${targetKey}.${entry.field}`, entry.op, entry.value))
7929
+ ...placed === void 0 ? [] : [placed]
7111
7930
  ];
7112
7931
  }, [
7113
7932
  storage,
@@ -7117,7 +7936,12 @@ function LinkPanel(props) {
7117
7936
  const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
7118
7937
  const columns = useMemo(() => {
7119
7938
  if (storage === void 0) return void 0;
7120
- return [...(own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef)).map((name) => `${storage.targetKey}.${name}`), ...own.linkColumns ?? []];
7939
+ const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
7940
+ const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
7941
+ ...entry,
7942
+ field: `${storage.targetKey}.${entry.field}`
7943
+ };
7944
+ return [...shown.map(across), ...own.linkColumns ?? []];
7121
7945
  }, [
7122
7946
  storage,
7123
7947
  columnsKey,
@@ -7127,30 +7951,75 @@ function LinkPanel(props) {
7127
7951
  resource: storage.target,
7128
7952
  field: storage.targetKey
7129
7953
  }, [storage]);
7130
- const unbound = fixedFilters === void 0 || own.where?.some((entry) => entry.value === null || entry.value === void 0) === true;
7954
+ const unbound = fixedFilters === void 0 || own.where !== void 0 && hasNullRef(own.where);
7131
7955
  if (storage === void 0 || unbound) return jsx(UnboundRelatedList, {
7132
7956
  resource: storage?.target ?? "",
7133
- title: own.title
7957
+ title: own.title,
7958
+ ...empty === void 0 ? {} : { empty }
7134
7959
  });
7135
7960
  const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
7136
- const count = own.count === false ? "off" : own.count;
7137
- return jsx(ResourceListSurface, {
7961
+ let providerCounts;
7962
+ if (runtime !== null) try {
7963
+ providerCounts = linkCapabilitiesOf(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
7964
+ } catch {
7965
+ providerCounts = void 0;
7966
+ }
7967
+ const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
7968
+ const layoutId = frame === null ? void 0 : placementLayoutId({
7969
+ owner,
7970
+ key: frame.key
7971
+ });
7972
+ const surface = {
7138
7973
  resource: storage.junction,
7139
7974
  variant: "placement",
7140
7975
  state,
7141
7976
  fixedFilters,
7142
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
7977
+ ...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
7143
7978
  ...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
7144
7979
  ...columns === void 0 ? {} : { columns },
7145
7980
  open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
7146
7981
  ...rowTarget === void 0 ? {} : { rowTarget },
7147
7982
  ...frame === null ? {} : { placementKey: frame.key },
7983
+ ...layoutId === void 0 ? {} : { layoutId },
7148
7984
  ...own.title === void 0 ? {} : { title: own.title },
7985
+ ...empty === void 0 ? {} : { empty },
7149
7986
  scope: placementScope(own.scope, owner),
7150
7987
  ...count === void 0 ? {} : { count },
7151
7988
  readOnly: true,
7152
7989
  exportable: false
7990
+ };
7991
+ if (runtime === null || record === void 0) return jsx(ResourceListSurface, { ...surface });
7992
+ return jsx(WritableLinkPanel, {
7993
+ surface,
7994
+ owner,
7995
+ link,
7996
+ storage,
7997
+ record,
7998
+ ownerKey,
7999
+ placement: own.linkActions
8000
+ });
8001
+ }
8002
+ function WritableLinkPanel(props) {
8003
+ const { surface, owner, link, storage, record, ownerKey, placement } = props;
8004
+ const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
8005
+ const { linkEditing, element } = useLinkPanelWrites({
8006
+ owner,
8007
+ junction: storage.junction,
8008
+ target: storage.target,
8009
+ link,
8010
+ targetKey: storage.targetKey,
8011
+ valueField: storage.targetValueField,
8012
+ junctionIdField: storage.junctionIdField,
8013
+ displayField,
8014
+ scope: storage.pickerScope ?? "context",
8015
+ record,
8016
+ ownerKey,
8017
+ placement
7153
8018
  });
8019
+ return jsxs(Fragment$1, { children: [jsx(ResourceListSurface, {
8020
+ ...surface,
8021
+ ...linkEditing === void 0 ? {} : { linkEditing }
8022
+ }), element] });
7154
8023
  }
7155
8024
  //#endregion
7156
8025
  //#region src/blocks/route-list-context.ts
@@ -7160,7 +8029,7 @@ function useRouteList() {
7160
8029
  }
7161
8030
  //#endregion
7162
8031
  //#region src/blocks/related-list-view.tsx
7163
- function RelatedListView({ props }) {
8032
+ function RelatedListView({ props, empty }) {
7164
8033
  const own = props;
7165
8034
  const route = useRouteList();
7166
8035
  const frame = usePlacementFrame();
@@ -7168,7 +8037,7 @@ function RelatedListView({ props }) {
7168
8037
  const linked = own.link !== void 0;
7169
8038
  const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : resourceKey(own.resource);
7170
8039
  const whereKey = JSON.stringify(own.where ?? []);
7171
- const fixedFilters = useMemo(() => own.where === void 0 ? void 0 : own.where.map(({ field, op, value }) => whereEntryToFilter(field, op, value)), [whereKey]);
8040
+ const fixedFilters = useMemo(() => own.where === void 0 || own.where.length === 0 ? void 0 : [whereToFilter(own.where)], [whereKey]);
7172
8041
  const columnsKey = JSON.stringify(own.columns ?? null);
7173
8042
  const columns = useMemo(() => own.columns, [columnsKey]);
7174
8043
  const routeScreen = useIsRouteScreen();
@@ -7180,20 +8049,23 @@ function RelatedListView({ props }) {
7180
8049
  const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
7181
8050
  const granted = ownerDef === void 0 ? "unknown" : grantedPlacementKeys(ownerDef);
7182
8051
  const trusted = frame !== null && !frame.nested && granted !== "unknown" && !granted.has(frame.key);
7183
- const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
8052
+ const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && own.create !== false && (own.create === void 0 || actionWhenHolds(own.create, frame?.sources ?? {})) && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
7184
8053
  useDeepLinkOpen(frame?.key ?? null, resource, route === null && !linked && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
7185
- const unbound = own.where?.some((entry) => entry.value === null || entry.value === void 0) === true;
8054
+ const unbound = own.where !== void 0 && hasNullRef(own.where);
7186
8055
  if (linked) return frame?.owner === void 0 ? jsx(UnboundRelatedList, {
7187
8056
  resource,
7188
- title: own.title
8057
+ title: own.title,
8058
+ ...empty === void 0 ? {} : { empty }
7189
8059
  }) : jsx(LinkPanel, {
7190
8060
  link: own.link,
7191
8061
  owner: frame.owner,
7192
- own
8062
+ own,
8063
+ ...empty === void 0 ? {} : { empty }
7193
8064
  });
7194
8065
  if (route !== null) return jsx(ResourceListSurface, {
7195
8066
  resource,
7196
8067
  variant: "route",
8068
+ layoutId: "route",
7197
8069
  ...route.meta === void 0 ? {} : { meta: route.meta },
7198
8070
  ...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
7199
8071
  ...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
@@ -7206,24 +8078,28 @@ function RelatedListView({ props }) {
7206
8078
  });
7207
8079
  if (unbound) return jsx(UnboundRelatedList, {
7208
8080
  resource,
7209
- title: own.title
8081
+ title: own.title,
8082
+ ...empty === void 0 ? {} : { empty }
7210
8083
  });
7211
8084
  const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
7212
8085
  const scope = placementScope(own.scope, frame?.owner);
7213
8086
  const count = own.count === false ? "off" : own.count;
8087
+ const layoutId = placementLayoutId(frame);
7214
8088
  return jsx(ResourceListSurface, {
7215
8089
  resource,
7216
8090
  variant: "placement",
7217
8091
  state,
7218
8092
  ...fixedFilters === void 0 ? {} : { fixedFilters },
7219
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
8093
+ ...own.initial === void 0 ? {} : { initial: own.initial },
7220
8094
  ...own.search === void 0 ? {} : { search: own.search },
7221
8095
  ...columns === void 0 ? {} : { columns },
7222
8096
  open: own.open ?? "quick",
7223
8097
  ...own.view === void 0 ? {} : { view: own.view },
7224
8098
  ...isItemHandle(own.item) ? { item: own.item } : {},
7225
8099
  ...frame === null ? {} : { placementKey: frame.key },
8100
+ ...layoutId === void 0 ? {} : { layoutId },
7226
8101
  ...own.title === void 0 ? {} : { title: own.title },
8102
+ ...empty === void 0 ? {} : { empty },
7227
8103
  scope,
7228
8104
  ...count === void 0 ? {} : { count },
7229
8105
  ...onCreate === void 0 ? {} : { onCreate }
@@ -7258,7 +8134,7 @@ function Missing({ text }) {
7258
8134
  children: text ?? t("table.empty", { defaultValue: "No records" })
7259
8135
  });
7260
8136
  }
7261
- function RelatedRecordView({ props }) {
8137
+ function RelatedRecordView({ props, empty }) {
7262
8138
  const own = props;
7263
8139
  const app = useApp();
7264
8140
  const resource = resourceKey(own.resource);
@@ -7292,15 +8168,15 @@ function RelatedRecordView({ props }) {
7292
8168
  capabilityFor,
7293
8169
  access
7294
8170
  ]);
7295
- if (id === null) return jsx(Missing, { text: own.missing });
8171
+ if (id === null) return jsx(Missing, { text: own.missing ?? empty });
7296
8172
  if (result === void 0) {
7297
- if (query.error != null) return jsx(Missing, { text: own.missing });
8173
+ if (query.error != null) return jsx(Missing, { text: own.missing ?? empty });
7298
8174
  return jsx(Skeleton, {
7299
8175
  "data-buro-skeleton": "",
7300
8176
  className: "h-9 w-full"
7301
8177
  });
7302
8178
  }
7303
- if (dtoRow === void 0) return jsx(Missing, { text: own.missing });
8179
+ if (dtoRow === void 0) return jsx(Missing, { text: own.missing ?? empty });
7304
8180
  if (projected.status === "pending" || projected.value === null) return jsx(Skeleton, {
7305
8181
  "data-buro-skeleton": "",
7306
8182
  className: "h-9 w-full"
@@ -7333,13 +8209,21 @@ function RelatedRecordView({ props }) {
7333
8209
  children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
7334
8210
  }), jsx("dd", {
7335
8211
  className: "min-w-0 break-words text-sm",
7336
- children: jsx(ResolvedFieldDisplay, {
7337
- resource: own.resource,
7338
- name,
7339
- field,
8212
+ children: jsx(FieldValueFrame, {
8213
+ field: {
8214
+ ...field ?? {},
8215
+ name
8216
+ },
7340
8217
  value,
7341
- record: row,
7342
- fallback: toDisplayText(value).text
8218
+ surface: "display",
8219
+ children: jsx(ResolvedFieldDisplay, {
8220
+ resource: own.resource,
8221
+ name,
8222
+ field,
8223
+ value,
8224
+ record: row,
8225
+ fallback: toDisplayText(value).text
8226
+ })
7343
8227
  })
7344
8228
  })]
7345
8229
  }, name);
@@ -7410,28 +8294,39 @@ function Chrome({ node, size, state, children }) {
7410
8294
  });
7411
8295
  }
7412
8296
  function isAuthError(error) {
7413
- return isDataError(error) && error.kind === "auth";
8297
+ return normalizeDataError(error).kind === "auth";
7414
8298
  }
7415
- function FailedPlacement({ node, size, message, retry }) {
8299
+ const PLACE_NODE_MESSAGES = {
8300
+ failed: "This section could not be loaded.",
8301
+ sessionExpired: "Your session has expired. Sign in again to continue.",
8302
+ notImplemented: "This section is not available."
8303
+ };
8304
+ function FailedPlacement({ node, size, message, retry, error }) {
7416
8305
  const t = useOptionalT();
8306
+ const chromeT = useChromeT();
8307
+ const fallback = {
8308
+ key: `block.${message}`,
8309
+ default: PLACE_NODE_MESSAGES[message]
8310
+ };
7417
8311
  return jsx(Chrome, {
7418
8312
  node,
7419
8313
  size,
7420
8314
  state: "failed",
7421
- children: jsxs("div", {
8315
+ children: message === "notImplemented" ? jsxs("div", {
7422
8316
  role: "alert",
7423
8317
  className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
7424
- children: [jsx("span", { children: t(`block.${message}`, { defaultValue: {
7425
- failed: "This section could not be loaded.",
7426
- sessionExpired: "Your session has expired. Sign in again to continue.",
7427
- notImplemented: "This section is not available."
7428
- }[message] }) }), retry === void 0 ? null : jsx(Button, {
8318
+ children: [jsx("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : jsx(Button, {
7429
8319
  type: "button",
7430
8320
  variant: "outline",
7431
8321
  size: "sm",
7432
8322
  onClick: retry,
7433
- children: t("actions.retry", { defaultValue: "Retry" })
8323
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
7434
8324
  })]
8325
+ }) : jsx(LoadError, {
8326
+ surface: "block",
8327
+ error,
8328
+ ...retry === void 0 ? {} : { retry },
8329
+ fallback
7435
8330
  })
7436
8331
  });
7437
8332
  }
@@ -7511,7 +8406,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7511
8406
  nested,
7512
8407
  ...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
7513
8408
  closure: compiled.sourceClosure,
7514
- ...owner === void 0 ? {} : { owner }
8409
+ ...owner === void 0 ? {} : { owner },
8410
+ sources: controller.sources
7515
8411
  }), [
7516
8412
  node.key,
7517
8413
  node.id,
@@ -7519,7 +8415,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7519
8415
  nested,
7520
8416
  declaredWhere,
7521
8417
  compiled.sourceClosure,
7522
- owner
8418
+ owner,
8419
+ controller.sources
7523
8420
  ]);
7524
8421
  return jsx(PlacementBoundary, {
7525
8422
  node,
@@ -7577,6 +8474,7 @@ function PlaceNode({ node }) {
7577
8474
  node,
7578
8475
  size,
7579
8476
  message: isAuthError(placement.error) ? "sessionExpired" : "failed",
8477
+ error: placement.error,
7580
8478
  ...placement.retry === void 0 ? {} : { retry: placement.retry }
7581
8479
  });
7582
8480
  if (implementation === void 0) return jsx(FailedPlacement, {
@@ -7592,6 +8490,6 @@ function PlaceNode({ node }) {
7592
8490
  });
7593
8491
  }
7594
8492
  //#endregion
7595
- export { tanStackSortingToBuro as $, ListExport as A, i18nPrefixToRef$1 as At, DeclarativeForm as B, ListSavedViews as C, useExportRunWatcher as Ct, ListFilters as D, AppFlowButton as Dt, ListFlows as E, DeepLinkScope as Et, ColumnsToggle as F, defaultUserMapper as Ft, RouteScreenContext as G, useTabsUrlState as H, DataTableViewOptions as I, usePaletteState as It, ResourceList as J, useIsRouteScreen as K, ListBulkBar as L, useShell as Lt, ListCreate as M, lowerLegacyField as Mt, CreateButton as N, toDisplayText as Nt, FilterRegion as O, Field as Ot, ListColumns as P, ShellProvider as Pt, resolveUpdater as Q, BulkActionToolbar as R, ListControls as S, useShownItemActions as St, ListLive as T, EnumBadge as Tt, QuickScreenClaimContext as U, ScreenTree as V, RouteScreenClaimContext as W, ListErrorState as X, CursorLoadMore as Y, buroSortingToTanStack as Z, FormActions as _, listTarget as _t, peekFieldDefinition as a, TabsList as at, FrameChromeContext as b, showTarget as bt, peekRow as c, useItemDrawn as ct, shadcnFrameViews as d, PageHeader as dt, useBuroDataTable as et, ChangeTypeButton as f, ShowPageHeader as ft, CopyId as g, isShowAvailable as gt, UndeclaredTypeNotice as h, typography as ht, RouteListContext as i, TabsContent as it, ExportButton as j, ResolvedFieldDisplay as jt, ListSearchInput as k, FieldDisplay as kt, isBuiltinRecordView as l, RELATED_GRID_CLASS as lt, TypeChromeDrawnContext as m, useDocumentTitle as mt, viewOf as n, Checkbox as nt, peekLeafAllowed as o, TabsTrigger as ot, HeaderTypeNotice as p, DocumentTitleProvider as pt, ListBody as q, shadcnBlockImplementations as r, Tabs as rt, peekRecord as s, RelatedItem as st, selectImplementation as t, DataTable as tt, resolveFrameView as u, EditPageHeader as ut, RecordActions as v, recordReturnTarget as vt, SavedViewsDropdown as w, downloadArtifact as wt, useFrameChrome as x, ActionOverflow as xt, RecordTitle as y, recordRouteAvailable as yt, ExportDialog as z };
8493
+ export { CursorLoadMore as $, ListSearchInput as A, EnumBadge as At, ExportDialog as B, usePaletteState as Bt, ListControls as C, recordReturnTarget as Ct, ListFlows as D, useShownItemActions as Dt, ListLive as E, ActionOverflow as Et, ListColumns as F, i18nPrefixToRef$1 as Ft, RouteScreenClaimContext as G, ScreenTree as H, ColumnsToggle as I, FieldValueFrame as It, FormControlProvider as J, RouteScreenContext as K, DataTableViewOptions as L, toDisplayText as Lt, ExportButton as M, AppFlowButton as Mt, ListCreate as N, Field as Nt, ListFilters as O, useExportRunWatcher as Ot, CreateButton as P, FieldDisplay as Pt, ResourceList as Q, ListBulkBar as R, ShellProvider as Rt, useFrameChrome as S, listTarget as St, SavedViewsDropdown as T, showTarget as Tt, useTabsUrlState as U, DeclarativeForm as V, useShell as Vt, QuickScreenClaimContext as W, useFormControlOf as X, useFormControl as Y, ListBody as Z, CopyId as _, resolveResourceTitle as _t, peekFieldDefinition as a, DataTable as at, RecordTitle as b, typography as bt, peekRow as c, TabsContent as ct, resolveFrameView as d, RelatedItem as dt, ListErrorState as et, shadcnFrameViews as f, useItemDrawn as ft, UndeclaredTypeNotice as g, ShowPageHeader as gt, TypeChromeDrawnContext as h, PageHeader as ht, RouteListContext as i, useBuroDataTable as it, ListExport as j, DeepLinkScope as jt, FilterRegion as k, downloadArtifact as kt, useRelationPicker as l, TabsList as lt, HeaderTypeNotice as m, EditPageHeader as mt, viewOf as n, resolveUpdater as nt, peekLeafAllowed as o, Checkbox as ot, ChangeTypeButton as p, RELATED_GRID_CLASS as pt, useIsRouteScreen as q, shadcnBlockImplementations as r, tanStackSortingToBuro as rt, peekRecord as s, Tabs as st, selectImplementation as t, buroSortingToTanStack as tt, isBuiltinRecordView as u, TabsTrigger as ut, FormActions as v, DocumentTitleProvider as vt, ListSavedViews as w, recordRouteAvailable as wt, FrameChromeContext as x, isShowAvailable as xt, RecordActions as y, useDocumentTitle as yt, BulkActionToolbar as z, defaultUserMapper as zt };
7596
8494
 
7597
- //# sourceMappingURL=place-node-NraNYKPx.js.map
8495
+ //# sourceMappingURL=place-node-CfzQvh4Y.js.map