@burojs/ui-shadcn 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-2HLgz7Il.cjs +169 -0
  3. package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
  4. package/dist/bulk-summary-D6A0ua21.js +169 -0
  5. package/dist/bulk-summary-D6A0ua21.js.map +1 -0
  6. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-C47mF260.cjs} +4 -4
  7. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
  8. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CGXItMN0.js} +3 -3
  9. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
  10. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-BT04Mieo.cjs} +475 -107
  11. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
  12. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-CtiRzOAt.js} +391 -108
  13. package/dist/check-confirm-dialog-CtiRzOAt.js.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-ByGhxLf_.cjs} +1021 -154
  19. package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-z3Axzf9Y.js} +929 -140
  21. package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BVFjMU5-.cjs} +64 -29
  23. package/dist/flow-runtime-BVFjMU5-.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DrUWyTS0.js} +65 -30
  25. package/dist/flow-runtime-DrUWyTS0.js.map +1 -0
  26. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-3fk6J33y.js} +130 -87
  27. package/dist/flow-screen-3fk6J33y.js.map +1 -0
  28. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-6y5E_HT6.cjs} +130 -87
  29. package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
  30. package/dist/index.cjs +1186 -950
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +631 -322
  33. package/dist/index.d.ts +631 -322
  34. package/dist/index.js +1100 -870
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-C4KmZIYE.cjs} +5 -5
  37. package/dist/inline-flow-host-C4KmZIYE.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-YnnvWjdU.js} +5 -5
  39. package/dist/inline-flow-host-YnnvWjdU.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-DN4bvQfg.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-CtEfqAvU.js} +35 -23
  45. package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DEjAxHe3.cjs} +42 -30
  47. package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
  48. package/dist/{place-node-NraNYKPx.js → place-node-BdxT-30h.js} +1980 -1041
  49. package/dist/place-node-BdxT-30h.js.map +1 -0
  50. package/dist/{place-node-C5SmIAYU.cjs → place-node-Bhl8B3fF.cjs} +2038 -1087
  51. package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
  52. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-Bc_vfCE_.cjs} +9 -4
  53. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
  54. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BvkD5OcC.js} +9 -4
  55. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
  56. package/dist/related-list-timeline-DCrwN9oO.js +167 -0
  57. package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
  58. package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
  59. package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
  60. package/dist/{related-rows-ea43qhhy.cjs → related-rows-BW9Jr07T.cjs} +27 -16
  61. package/dist/related-rows-BW9Jr07T.cjs.map +1 -0
  62. package/dist/{related-rows-o0_t-Yce.js → related-rows-CPE-9FFr.js} +25 -14
  63. package/dist/related-rows-CPE-9FFr.js.map +1 -0
  64. package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
  65. package/dist/relation-create-C43cdPQt.js.map +1 -0
  66. package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
  67. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  68. package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BlrGRwMV.js} +210 -104
  69. package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-Br-zBHxT.cjs} +218 -112
  71. package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
  72. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-ADjxI_TI.cjs} +36 -73
  73. package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
  74. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-CyKliBkN.js} +134 -36
  75. package/dist/relation-picker-dialog-CyKliBkN.js.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-DimVQF9Y.cjs} +133 -35
  77. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +1 -0
  78. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-x7P5HKie.js} +30 -61
  79. package/dist/relation-picker-x7P5HKie.js.map +1 -0
  80. package/dist/relation-select-B8Fq4o-t.js +48 -0
  81. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  82. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  83. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  84. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-BRpUnhay.js} +634 -264
  85. package/dist/resource-page-router-BRpUnhay.js.map +1 -0
  86. package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-qEd2xb55.cjs} +694 -312
  87. package/dist/resource-page-router-qEd2xb55.cjs.map +1 -0
  88. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  89. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  90. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  91. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  92. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  93. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  94. package/dist/success-notices-LDEgEkc4.js +41 -0
  95. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  96. package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
  97. package/dist/toaster-BbByOytP.cjs.map +1 -0
  98. package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
  99. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  100. package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  101. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  102. package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  103. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  104. package/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
  105. package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
  106. package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
  107. package/dist/use-record-subtitle-gIaCGgbI.js.map +1 -0
  108. package/package.json +11 -11
  109. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  110. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  111. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  112. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  113. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  114. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  115. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  116. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  117. package/dist/dialog-B_rt9S1c.js.map +0 -1
  118. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  119. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  120. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  121. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  122. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  123. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  124. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  125. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  126. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  127. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  128. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  129. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  130. package/dist/place-node-NraNYKPx.js.map +0 -1
  131. package/dist/related-list-timeline-CCzl71aA.cjs +0 -142
  132. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  133. package/dist/related-list-timeline-yrPsjA3F.js +0 -142
  134. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  135. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  136. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  137. package/dist/relation-create-IA40t1hi.cjs +0 -364
  138. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  139. package/dist/relation-create-h6F9B4C4.js +0 -339
  140. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  141. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  142. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  143. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  144. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  145. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  146. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  147. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  148. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  149. package/dist/search-access-CPPmfuvy.js.map +0 -1
  150. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  151. package/dist/shell-BPlk8hEa.js +0 -205
  152. package/dist/shell-BPlk8hEa.js.map +0 -1
  153. package/dist/shell-DxhlsPwm.cjs +0 -271
  154. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  155. package/dist/toaster-C02XbB9r.js.map +0 -1
  156. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  157. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  158. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
@@ -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-CtiRzOAt.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 TableHeader, C as SelectTrigger, E as TableBody, F as ResolvedFieldDisplay, I as ResolvedFieldInput, L as useFieldMetaDiagnostic, M as Badge, O as TableCell, P as MetaPlaceholder, R as fieldDescription, T as Table$1, g as SelectContent, h as Select, j as TableRow, k as TableHead, t as FilterBuilder, v as SelectItem, w as SelectValue, z as lowerLegacyField } from "./filter-builder-z3Axzf9Y.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-DN4bvQfg.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, 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, deriveRecord, implementFrame, isObjectColumn, kindErrorText, linkCapabilitiesOf, policyRecordOf, policyRowsOf, readColumnValue, recordKeyOf, resolveColumnField, resolveErrorText, resolveObjectColumn, 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, useReturnTarget, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow, withRecordKeys } 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, deepestPlacedPath, enumBindingKey, hasNullRef, isFlowAction, pathHead, 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(null);
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 === null) return;
149
+ const timer = setTimeout(() => setCopied(null), 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("copied"), () => setCopied("failed"));
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 === "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 === "copied" ? t("field.copied", { defaultValue: "Copied" }) : copied === "failed" ? t("field.copyFailed", { defaultValue: "Could not copy" }) : ""
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({
@@ -606,6 +281,10 @@ function leafAttributes(size, weight) {
606
281
  ...weight === void 0 ? {} : { "data-buro-weight": String(weight) }
607
282
  };
608
283
  }
284
+ function labelHiddenOf(compiled, node) {
285
+ return compiled.nodes[node.index]?.options.label === "hidden";
286
+ }
287
+ const labelRegion = (hidden, visible) => hidden ? "sr-only" : visible;
609
288
  function FieldLeaf({ node }) {
610
289
  const { leafIds } = useScreenTree();
611
290
  if (node.state === "failed" && node.failedOn.length > 0 && node.failedOn.every((name) => name.startsWith("meta:"))) return jsx(MisconfiguredLeaf, { node });
@@ -617,7 +296,8 @@ function FieldLeaf({ node }) {
617
296
  return jsx(RecordLeaf, { node });
618
297
  }
619
298
  function MisconfiguredLeaf({ node }) {
620
- const { leafIds, resource, mode, controller } = useScreenTree();
299
+ const { leafIds, resource, mode, compiled, controller } = useScreenTree();
300
+ const hidden = labelHiddenOf(compiled, node);
621
301
  const name = node.field;
622
302
  const binding = controller.field(name);
623
303
  const caps = useI18nCaps(resource);
@@ -633,7 +313,7 @@ function MisconfiguredLeaf({ node }) {
633
313
  values: controller.values,
634
314
  extra: controller.sources
635
315
  });
636
- const state = useFieldMeta(field, resolved.implementation, sources);
316
+ const state = useFieldMeta(field, resolved.implementation, sources, resolved.params);
637
317
  useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
638
318
  const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
639
319
  const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
@@ -644,7 +324,7 @@ function MisconfiguredLeaf({ node }) {
644
324
  "data-buro-field-meta": "failed",
645
325
  children: [jsx("div", {
646
326
  "data-buro-region": "field-label",
647
- className: "text-sm font-medium leading-none",
327
+ className: labelRegion(hidden, "text-sm font-medium leading-none"),
648
328
  children: label
649
329
  }), jsx("div", {
650
330
  "data-buro-region": "field-control",
@@ -652,12 +332,22 @@ function MisconfiguredLeaf({ node }) {
652
332
  })]
653
333
  });
654
334
  }
335
+ function valueAt(record, path) {
336
+ let at = record;
337
+ for (const segment of path.split(".")) {
338
+ if (at === null || typeof at !== "object") return void 0;
339
+ at = at[segment];
340
+ }
341
+ return at;
342
+ }
655
343
  function RecordLeaf({ node }) {
656
344
  const { resource, mode, compiled, controller } = useScreenTree();
657
- const name = node.field;
345
+ const head = node.field;
346
+ const name = node.fieldPath ?? head;
658
347
  const binding = controller.field(name);
659
348
  const size = useLeafSize(binding.field);
660
349
  const weight = compiled.nodes[node.index]?.options.weight;
350
+ const hidden = labelHiddenOf(compiled, node);
661
351
  const caps = useI18nCaps(resource);
662
352
  const tid = useTestIds();
663
353
  const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
@@ -665,10 +355,7 @@ function RecordLeaf({ node }) {
665
355
  const t = useOptionalT();
666
356
  if (node.restricted === true) {
667
357
  const rawReason = node.restrictedReason;
668
- const reason = rawReason === void 0 || rawReason === "" ? void 0 : resolveContentSlot(caps, [rawReason], {
669
- resource,
670
- default: rawReason
671
- });
358
+ const reason = rawReason === void 0 || rawReason === "" ? void 0 : reasonText(caps, rawReason, resource);
672
359
  const restrictedId = tid.formFieldRestricted(resource, mode, name);
673
360
  return jsxs("div", {
674
361
  ...attributes,
@@ -677,7 +364,7 @@ function RecordLeaf({ node }) {
677
364
  "data-buro-restricted": "true",
678
365
  children: [jsx("div", {
679
366
  "data-buro-region": "field-label",
680
- className: "text-sm font-medium leading-none",
367
+ className: labelRegion(hidden, "text-sm font-medium leading-none"),
681
368
  children: label
682
369
  }), jsx("div", {
683
370
  "data-buro-region": "field-control",
@@ -697,7 +384,7 @@ function RecordLeaf({ node }) {
697
384
  });
698
385
  }
699
386
  const record = controller.record;
700
- const value = mode === "show" ? record?.[name] ?? controller.values[name] : mode === "edit" && controller.plan?.fields[name] !== "active" ? record?.[name] ?? controller.values[name] : controller.values[name];
387
+ const value = mode === "show" ? valueAt(record, name) ?? binding.value : mode === "edit" && controller.plan?.fields[head] !== "active" ? valueAt(record, name) ?? binding.value : binding.value;
701
388
  const display = jsx(FieldDisplay, {
702
389
  resource: binding.resource,
703
390
  name,
@@ -705,6 +392,8 @@ function RecordLeaf({ node }) {
705
392
  value,
706
393
  metaSources: controller.sources,
707
394
  ...record === void 0 ? {} : { record },
395
+ ...binding.absentPaths === void 0 ? {} : { absentPaths: binding.absentPaths },
396
+ ...binding.pathStatus === void 0 ? {} : { pathStatus: binding.pathStatus },
708
397
  fallback: value == null || value === "" ? jsx("span", {
709
398
  className: "text-muted-foreground",
710
399
  children: "—"
@@ -715,11 +404,11 @@ function RecordLeaf({ node }) {
715
404
  "data-testid": `show-field:${resource}:${name}`,
716
405
  children: [jsxs("dt", {
717
406
  "data-buro-region": "field-label",
718
- className: cn("flex items-center gap-0.5", FIELD_LABEL_SHOW),
407
+ className: labelRegion(hidden, cn("flex items-center gap-0.5", FIELD_LABEL_SHOW)),
719
408
  children: [jsx("span", {
720
409
  className: "min-w-0",
721
410
  children: label
722
- }), jsx(DocInfo, {
411
+ }), hidden ? null : jsx(DocInfo, {
723
412
  address: `${resource}/fields/${name}`,
724
413
  name: label
725
414
  })]
@@ -731,10 +420,7 @@ function RecordLeaf({ node }) {
731
420
  });
732
421
  if (node.editable !== true) {
733
422
  const rawReason = node.lockReason;
734
- const reason = rawReason === void 0 ? void 0 : resolveContentSlot(caps, [rawReason], {
735
- resource,
736
- default: rawReason
737
- });
423
+ const reason = rawReason === void 0 ? void 0 : reasonText(caps, rawReason, resource);
738
424
  const locked = tid.formFieldLocked(resource, mode, name);
739
425
  return jsxs("div", {
740
426
  ...attributes,
@@ -742,8 +428,8 @@ function RecordLeaf({ node }) {
742
428
  "data-testid": tid.formField(resource, mode, name),
743
429
  children: [jsxs("div", {
744
430
  "data-buro-region": "field-label",
745
- className: "flex items-center gap-2",
746
- children: [jsx(Label, { children: label }), jsx(DocInfo, {
431
+ className: labelRegion(hidden, "flex items-center gap-2"),
432
+ children: [jsx(Label, { children: label }), hidden ? null : jsx(DocInfo, {
747
433
  address: `${resource}/fields/${name}`,
748
434
  name: label,
749
435
  testId: `${tid.formField(resource, mode, name)}:info`
@@ -779,14 +465,14 @@ function RecordLeaf({ node }) {
779
465
  node,
780
466
  binding,
781
467
  label,
468
+ labelHidden: hidden,
782
469
  attributes,
783
470
  inputId
784
471
  });
785
472
  }
786
- function ActiveLeaf({ node, binding, label, attributes, inputId }) {
473
+ function ActiveLeaf({ node, binding, label, labelHidden, attributes, inputId }) {
787
474
  const { resource, mode, controller, disabled: locked } = useScreenTree();
788
475
  const caps = useI18nCaps(resource);
789
- const t = useOptionalT();
790
476
  const tid = useTestIds();
791
477
  const name = binding.name;
792
478
  const record = controller.record;
@@ -802,24 +488,21 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
802
488
  const messageId = `${mode}-${name}-msg`;
803
489
  const placeholder = resolveFieldSlot(caps, name, "placeholder");
804
490
  const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
491
+ const chromeT = useChromeT();
805
492
  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 }
493
+ const text = (issue) => resolveValidationMessage({
494
+ code: issue.code ?? "",
495
+ message: issue.message,
496
+ ...issue.check === void 0 ? {} : { check: issue.check }
819
497
  }, {
820
- t,
498
+ has: caps.has,
499
+ t: caps.t,
821
500
  fieldKey
822
501
  });
502
+ const shown = binding.issues.slice(0, SHOWN_ISSUES);
503
+ const more = binding.issues.length - shown.length;
504
+ const firstError = shown.find((issue) => issue.severity === "error");
505
+ const errorText = firstError === void 0 ? null : text(firstError);
823
506
  const disabled = binding.disabled || controller.isSubmitting || locked === true;
824
507
  const describedBy = errorText === null && hint ? messageId : void 0;
825
508
  const errorMessageId = errorText === null ? void 0 : messageId;
@@ -831,12 +514,13 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
831
514
  onBlur: binding.onBlur,
832
515
  children: [jsxs("div", {
833
516
  "data-buro-region": "field-label",
834
- className: "flex items-center gap-2",
517
+ className: labelRegion(labelHidden, "flex items-center gap-2"),
835
518
  children: [jsx(Label, {
519
+ id: `${inputId}-label`,
836
520
  htmlFor: inputId,
837
521
  required: binding.required,
838
522
  children: label
839
- }), jsx(DocInfo, {
523
+ }), labelHidden ? null : jsx(DocInfo, {
840
524
  address: `${resource}/fields/${name}`,
841
525
  name: label,
842
526
  testId: `${tid.formField(resource, mode, name)}:info`
@@ -848,6 +532,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
848
532
  resource: binding.resource,
849
533
  name,
850
534
  inputId,
535
+ labelId: `${inputId}-label`,
851
536
  field: binding.field,
852
537
  value: binding.value,
853
538
  onChange: binding.onChange,
@@ -858,11 +543,17 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
858
543
  ...mode === "create" || record === void 0 ? {} : { record },
859
544
  form: controller.values,
860
545
  metaSources: controller.sources,
546
+ ...controller.baseline === void 0 ? {} : { baseline: controller.baseline },
861
547
  ...describedBy === void 0 ? {} : { descriptionId: describedBy },
862
548
  ...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
863
549
  errorMessage: errorText,
550
+ issues: binding.issues,
864
551
  ...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
552
+ ...binding.optionSubtitle === void 0 ? {} : { optionSubtitle: binding.optionSubtitle },
865
553
  ...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
554
+ ...binding.link === void 0 ? {} : { link: binding.link },
555
+ ...binding.absentPaths === void 0 ? {} : { absentPaths: binding.absentPaths },
556
+ ...binding.pathStatus === void 0 ? {} : { pathStatus: binding.pathStatus },
866
557
  fallback: jsx(Input, {
867
558
  id: inputId,
868
559
  name: inputId,
@@ -877,18 +568,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
877
568
  ...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
878
569
  onChange: (event) => binding.onChange(event.target.value)
879
570
  })
880
- }), errorText !== null ? jsx("p", {
571
+ }), shown.length > 0 ? jsxs("div", {
881
572
  id: messageId,
882
573
  "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", {
887
- id: messageId,
888
- "aria-live": "polite",
889
- className: "text-sm text-warning-emphasis",
890
- "data-testid": `form-field-warning:${resource}:${mode}:${name}`,
891
- children: warningText
574
+ className: cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
575
+ "data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
576
+ children: [shown.map((issue, index) => jsx("p", {
577
+ "data-buro-severity": issue.severity,
578
+ className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
579
+ children: text(issue)
580
+ }, index)), more > 0 ? jsx("p", {
581
+ className: "text-muted-foreground",
582
+ "data-testid": `form-field-more:${resource}:${mode}:${name}`,
583
+ children: chromeT("validation.moreIssues", {
584
+ count: more,
585
+ defaultValue: "+{{count}} more"
586
+ })
587
+ }) : null]
892
588
  }) : hint ? jsx("p", {
893
589
  id: messageId,
894
590
  "data-testid": tid.formFieldHelp(resource, mode, name),
@@ -904,11 +600,12 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
904
600
  const binding = controller.field(name);
905
601
  const size = useLeafSize(binding.field);
906
602
  const weight = compiled.nodes[node.index]?.options.weight;
603
+ const hidden = labelHiddenOf(compiled, node);
907
604
  const attributes = leafAttributes(size, weight);
908
605
  const docRef = i18nPrefixToRef(prefix);
909
606
  const docScope = docRef.namespace === "default" ? docRef.id : formatEntityRef(docRef);
910
607
  const caps = useI18nCaps(docScope);
911
- const t = useOptionalT();
608
+ const issueCaps = useI18nCaps();
912
609
  const uid = useId();
913
610
  const root = useRef(null);
914
611
  const inputId = `${inputIds ?? uid}-${name}`;
@@ -922,7 +619,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
922
619
  });
923
620
  const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
924
621
  const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
925
- const info = jsx(DocInfo, {
622
+ const info = hidden ? null : jsx(DocInfo, {
926
623
  address: `${docScope}/fields/${name}`,
927
624
  name: label,
928
625
  testId: `${prefix}:field:${name}:info`
@@ -939,7 +636,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
939
636
  children: [
940
637
  jsxs("div", {
941
638
  "data-buro-region": "field-label",
942
- className: "flex items-center gap-2",
639
+ className: labelRegion(hidden, "flex items-center gap-2"),
943
640
  children: [jsx("span", {
944
641
  className: FIELD_LABEL_SHOW,
945
642
  children: label
@@ -962,16 +659,19 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
962
659
  const text = (issue) => resolveValidationMessage({
963
660
  code: issue.code ?? "",
964
661
  message: issue.message,
965
- ...issue.params === void 0 ? {} : { params: issue.params }
662
+ ...issue.check === void 0 ? {} : { check: issue.check }
966
663
  }, {
967
- t,
664
+ has: issueCaps.has,
665
+ t: issueCaps.t,
968
666
  fieldKey
969
667
  });
970
668
  const defaultNode = jsx(Field, {
971
669
  ...binding,
972
670
  field: binding.field,
973
671
  metaSources: controller.sources,
672
+ ...controller.baseline === void 0 ? {} : { baseline: controller.baseline },
974
673
  inputId,
674
+ labelId: `${inputId}-label`,
975
675
  i18nPrefix: prefix,
976
676
  disabled: disabled === true || binding.disabled
977
677
  });
@@ -985,8 +685,9 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
985
685
  children: [
986
686
  jsxs("div", {
987
687
  "data-buro-region": "field-label",
988
- className: "flex items-center gap-2",
688
+ className: labelRegion(hidden, "flex items-center gap-2"),
989
689
  children: [jsx(Label, {
690
+ id: `${inputId}-label`,
990
691
  htmlFor: inputId,
991
692
  required: binding.required,
992
693
  children: label
@@ -1027,9 +728,10 @@ function Notice({ children }) {
1027
728
  children
1028
729
  });
1029
730
  }
1030
- function FactsView({ props }) {
731
+ function FactsView({ props, empty }) {
1031
732
  const own = props;
1032
733
  const t = useOptionalT();
734
+ const chromeT = useChromeT();
1033
735
  const snapshot = useApplicationSnapshot();
1034
736
  const queryId = formatEntityRef(own.query);
1035
737
  const caps = useI18nCaps();
@@ -1046,7 +748,10 @@ function FactsView({ props }) {
1046
748
  kind: "custom-query",
1047
749
  ref: own.query
1048
750
  }), [own.query]);
1049
- const result = useCustomQuery(handle, blocked ? {} : input, { enabled: !blocked });
751
+ const result = useCustomQuery(handle, blocked ? {} : input, {
752
+ enabled: !blocked,
753
+ ...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
754
+ });
1050
755
  const shown = useMemo(() => {
1051
756
  const byName = new Map(fields.map((field) => [field.name, field]));
1052
757
  return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
@@ -1054,7 +759,7 @@ function FactsView({ props }) {
1054
759
  return field === void 0 ? [] : [field];
1055
760
  });
1056
761
  }, [fields, own.rows]);
1057
- if (blocked) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
762
+ if (blocked) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1058
763
  if (result.isError) return jsxs("div", {
1059
764
  role: "alert",
1060
765
  className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
@@ -1063,7 +768,7 @@ function FactsView({ props }) {
1063
768
  variant: "outline",
1064
769
  size: "sm",
1065
770
  onClick: () => void result.refetch(),
1066
- children: t("actions.retry", { defaultValue: "Retry" })
771
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
1067
772
  })]
1068
773
  });
1069
774
  if (result.data === void 0) return own.view === "list" ? jsx(Skeleton, {
@@ -1077,7 +782,7 @@ function FactsView({ props }) {
1077
782
  }, field.name))
1078
783
  });
1079
784
  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" }) });
785
+ if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1081
786
  const pairs = shown.map((field) => {
1082
787
  const value = data[field.name];
1083
788
  const label = resolveContentSlot(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
@@ -1087,13 +792,18 @@ function FactsView({ props }) {
1087
792
  const body = value === null || value === void 0 ? jsx("span", {
1088
793
  className: "text-muted-foreground",
1089
794
  children: "—"
1090
- }) : jsx(ResolvedFieldDisplay, {
1091
- resource: own.query,
1092
- name: field.name,
795
+ }) : jsx(FieldValueFrame, {
1093
796
  field,
1094
797
  value,
1095
- record: data,
1096
- fallback: toDisplayText(value).text
798
+ surface: "display",
799
+ children: jsx(ResolvedFieldDisplay, {
800
+ resource: own.query,
801
+ name: field.name,
802
+ field,
803
+ value,
804
+ record: data,
805
+ fallback: toDisplayText(value).text
806
+ })
1097
807
  });
1098
808
  return {
1099
809
  name: field.name,
@@ -1147,6 +857,9 @@ const PlacementFrameContext = createContext(null);
1147
857
  function usePlacementFrame() {
1148
858
  return useContext(PlacementFrameContext);
1149
859
  }
860
+ function placementLayoutId(frame) {
861
+ return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
862
+ }
1150
863
  //#endregion
1151
864
  //#region src/flows/app-flow-button.tsx
1152
865
  function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
@@ -1234,10 +947,7 @@ function RecordInlineFlow({ name }) {
1234
947
  const id = frame?.recordId;
1235
948
  const asRead = frame === null ? void 0 : policyRecordOf(frame.controller, frame.record);
1236
949
  const key = placement?.key ?? flowName;
1237
- const state = frame === null || id === void 0 || asRead === void 0 ? null : resolve(flowName, {
1238
- ...asRead,
1239
- id
1240
- });
950
+ const state = frame === null || id === void 0 || asRead === void 0 ? null : resolve(flowName, withRecordKeys(asRead, { id }));
1241
951
  const kept = holder?.get(key);
1242
952
  const stale = state?.state === "absent" && state.cause === "when" && kept !== void 0 && kept.dirty;
1243
953
  const gone = state?.state === "absent" && !stale;
@@ -1249,10 +959,7 @@ function RecordInlineFlow({ name }) {
1249
959
  key
1250
960
  ]);
1251
961
  if (frame === null || asRead === void 0 || id === void 0 || compiled === null) return null;
1252
- const row = {
1253
- ...asRead,
1254
- id
1255
- };
962
+ const row = withRecordKeys(asRead, { id });
1256
963
  const control = resolve(flowName, row);
1257
964
  switch (control.state) {
1258
965
  case "undecided":
@@ -1266,7 +973,7 @@ function RecordInlineFlow({ name }) {
1266
973
  return jsx(InlineHostLoader, {
1267
974
  resource,
1268
975
  name: flowName,
1269
- record: row,
976
+ ...hostRecord(flowRecordOf(compiled, row)),
1270
977
  placementKey: key,
1271
978
  lease: kept,
1272
979
  stale: true,
@@ -1282,7 +989,7 @@ function RecordInlineFlow({ name }) {
1282
989
  if (control.state === "disabled" && kept !== void 0 && kept.dirty) return jsx(InlineHostLoader, {
1283
990
  resource,
1284
991
  name: flowName,
1285
- record: row,
992
+ ...hostRecord(flowRecordOf(compiled, row)),
1286
993
  placementKey: key,
1287
994
  lease: kept,
1288
995
  stale: true,
@@ -1294,11 +1001,12 @@ function RecordInlineFlow({ name }) {
1294
1001
  return jsx(InlineHostLoader, {
1295
1002
  resource,
1296
1003
  name: flowName,
1297
- record: control.record ?? row,
1004
+ ...hostRecord(flowRecordOf(compiled, control.record ?? row)),
1298
1005
  placementKey: key,
1299
1006
  ...lease === void 0 ? {} : { lease }
1300
1007
  });
1301
1008
  }
1009
+ const hostRecord = (record) => record === void 0 ? {} : { record };
1302
1010
  function InlineHostLoader(props) {
1303
1011
  const loaded = useLoaded(loadInlineFlowHost);
1304
1012
  if (loaded.error !== void 0) return jsx("p", {
@@ -1514,6 +1222,7 @@ function placementSeeds(where, resolved, closure) {
1514
1222
  where.forEach((entry, index) => {
1515
1223
  const value = resolved[index]?.value;
1516
1224
  if (entry.op !== "eq" || value === null || value === void 0) return;
1225
+ if (typeof entry.field !== "string" || entry.field.includes(".")) return;
1517
1226
  if (resolved[index]?.field !== entry.field) return;
1518
1227
  const source = refSource(entry.value);
1519
1228
  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;
@@ -1521,6 +1230,28 @@ function placementSeeds(where, resolved, closure) {
1521
1230
  return Object.freeze(seeds);
1522
1231
  }
1523
1232
  //#endregion
1233
+ //#region src/enum/enum-settling.tsx
1234
+ function unknownEnumValueLabel(caps, def, ns, value) {
1235
+ const declared = enumFallbackText(def);
1236
+ if (def.fallback === void 0 && declared === void 0) return {
1237
+ label: value,
1238
+ fallback: false
1239
+ };
1240
+ return {
1241
+ label: resolveContentSlot(caps, [enumFallbackKey(ns)], { default: declared }) ?? value,
1242
+ fallback: true
1243
+ };
1244
+ }
1245
+ function EnumSettlingRow({ status, testid }) {
1246
+ const t = useOptionalT();
1247
+ return jsx("div", {
1248
+ role: "status",
1249
+ "data-testid": testid === void 0 ? void 0 : `${testid}:${status}`,
1250
+ className: "px-2 py-1.5 text-sm text-muted-foreground",
1251
+ children: status === "pending" ? t("field.loading", { defaultValue: "Loading…" }) : t("relation.failed", { defaultValue: "Failed to load options." })
1252
+ });
1253
+ }
1254
+ //#endregion
1524
1255
  //#region src/enum/enum-badge.tsx
1525
1256
  function BadgeBody({ icon, label }) {
1526
1257
  return jsxs("span", {
@@ -1531,7 +1262,8 @@ function BadgeBody({ icon, label }) {
1531
1262
  }) : null, label]
1532
1263
  });
1533
1264
  }
1534
- function EnumBadge({ enumName, value, testid, className, presentation }) {
1265
+ function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
1266
+ const enumName = enumBindingKey(enumInput) ?? "";
1535
1267
  const app = useApp();
1536
1268
  const caps = useI18nCaps();
1537
1269
  const def = app.registry.enums.get(enumName);
@@ -1590,9 +1322,9 @@ function EnumBadge({ enumName, value, testid, className, presentation }) {
1590
1322
  children: body
1591
1323
  });
1592
1324
  }
1593
- const declaredFallback = enumFallbackText(def);
1594
- if (def?.fallback !== void 0 || declaredFallback !== void 0) {
1595
- const fallbackLabel = resolveContentSlot(caps, [enumFallbackKey(ns)], { default: declaredFallback }) ?? value;
1325
+ const unknown = unknownEnumValueLabel(caps, def, ns, value);
1326
+ if (unknown.fallback) {
1327
+ const fallbackLabel = unknown.label;
1596
1328
  if (presentation === "plain") return jsx("span", {
1597
1329
  "data-testid": `${id}:fallback`,
1598
1330
  className,
@@ -1677,6 +1409,7 @@ function useExportRunWatcher() {
1677
1409
  const runtime = useDataRuntime();
1678
1410
  const runs = useExportRuns();
1679
1411
  const t = useOptionalT();
1412
+ const caps = useI18nCaps();
1680
1413
  React.useEffect(() => {
1681
1414
  const announced = announcedRunsFor(runtime);
1682
1415
  const unsubscribes = runs.map((run) => {
@@ -1695,7 +1428,7 @@ function useExportRunWatcher() {
1695
1428
  label: t("export.toast.download"),
1696
1429
  onClick: () => downloadArtifact(artifact)
1697
1430
  } });
1698
- } else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: state.error.message } }));
1431
+ } else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: resolveErrorText(state.error, caps) } }));
1699
1432
  };
1700
1433
  check();
1701
1434
  return run.handle.subscribe(check);
@@ -1706,24 +1439,27 @@ function useExportRunWatcher() {
1706
1439
  }, [
1707
1440
  runs,
1708
1441
  runtime,
1709
- t
1442
+ t,
1443
+ caps
1710
1444
  ]);
1711
1445
  }
1712
1446
  //#endregion
1713
1447
  //#region src/actions/action-overflow.tsx
1714
1448
  const RowActionsCollapseContext = createContext(false);
1715
- function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
1716
- const t = useOptionalT();
1449
+ function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
1450
+ const resource = routeResourceKey(resourceInput);
1451
+ const chromeT = useChromeT();
1717
1452
  const tid = useTestIds();
1453
+ const recordKey = String(recordKeyOf(useApp().registry.get(resource), record) ?? "");
1718
1454
  const collapsedRows = useContext(RowActionsCollapseContext);
1719
1455
  const [flowFor, setFlowFor] = useState(null);
1720
- const actions = useShownItemActions(resource, record, surface, hideFlows);
1456
+ const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
1721
1457
  if (actions.length === 0) return null;
1722
1458
  const isRow = surface === "row";
1723
1459
  const folded = isRow && (collapse ?? collapsedRows);
1724
1460
  const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
1725
1461
  const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
1726
- const menuTestId = isRow ? tid.listRowActionsMenu(resource, record.id) : tid.formActionsMenu(resource, surface);
1462
+ const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
1727
1463
  const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
1728
1464
  const overlays = jsx(Fragment$1, { children: flowFor != null ? jsx(RuntimeFlowRunner, {
1729
1465
  resource,
@@ -1737,6 +1473,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1737
1473
  actionName: a.name,
1738
1474
  record,
1739
1475
  scope: surface,
1476
+ ...sources === void 0 ? {} : { sources },
1740
1477
  density: a.density
1741
1478
  }, a.name));
1742
1479
  return jsxs("div", {
@@ -1760,7 +1497,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1760
1497
  size: "icon",
1761
1498
  className: "size-7",
1762
1499
  "data-testid": menuTestId,
1763
- "aria-label": t("actions.more", { defaultValue: "More" }),
1500
+ "aria-label": chromeT("actions.more", { defaultValue: "More" }),
1764
1501
  children: jsx(IconDots, {
1765
1502
  className: "h-4 w-4",
1766
1503
  "aria-hidden": "true"
@@ -1774,15 +1511,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1774
1511
  children: [jsx(IconDots, {
1775
1512
  className: "h-4 w-4",
1776
1513
  "aria-hidden": "true"
1777
- }), t("actions.more", { defaultValue: "More" })]
1514
+ }), chromeT("actions.more", { defaultValue: "More" })]
1778
1515
  })
1779
1516
  }), jsx(DropdownMenuContent, {
1780
1517
  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, {
1518
+ 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
1519
  resource,
1783
1520
  actionName: a.name,
1784
1521
  record,
1785
1522
  scope: surface,
1523
+ ...sources === void 0 ? {} : { sources },
1786
1524
  asMenuItem: true
1787
1525
  }) : jsx(FlowMenuItem, {
1788
1526
  resource,
@@ -1802,46 +1540,18 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1802
1540
  ]
1803
1541
  });
1804
1542
  }
1805
- function useShownItemActions(resource, record, surface, hideFlows) {
1543
+ function useShownItemActions(resource, record, surface, hideFlows, sources) {
1806
1544
  const resolved = useItemActions({
1807
1545
  resource,
1808
1546
  record,
1809
- surface
1547
+ surface,
1548
+ ...sources === void 0 ? {} : { sources }
1810
1549
  });
1811
1550
  const def = useApp().registry.get(resource);
1812
1551
  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;
1813
1552
  return hideFlows === void 0 ? shown : shown.filter((a) => a.flow === void 0 || !hideFlows(a.name));
1814
1553
  }
1815
1554
  //#endregion
1816
- //#region src/layout/record-targets.ts
1817
- function recordRouteAvailable(registry, resource, action) {
1818
- if (resolveStaticAvailability(registry, resource, action).state !== "available") return false;
1819
- const def = registry.get(resource);
1820
- return def !== void 0 && hasRecordPage(def, action);
1821
- }
1822
- function isShowAvailable(registry, resource) {
1823
- return recordRouteAvailable(registry, resource, "show");
1824
- }
1825
- function showTarget(resource, id) {
1826
- return {
1827
- resource,
1828
- action: "show",
1829
- params: { id }
1830
- };
1831
- }
1832
- function listTarget(resource) {
1833
- return {
1834
- resource,
1835
- action: "list"
1836
- };
1837
- }
1838
- function recordReturnTarget(registry, resource, id) {
1839
- return isShowAvailable(registry, resource) ? showTarget(resource, id) : listTarget(resource);
1840
- }
1841
- function editBackHref(registry, resource, id, backHref) {
1842
- return backHref ?? resolveRoute(recordReturnTarget(registry, resource, id), registry);
1843
- }
1844
- //#endregion
1845
1555
  //#region src/lib/typography.ts
1846
1556
  const typography = {
1847
1557
  h1: "text-2xl font-semibold tracking-tight",
@@ -1899,8 +1609,14 @@ function DocumentTitleProvider({ routeKey, children }) {
1899
1609
  });
1900
1610
  }
1901
1611
  //#endregion
1612
+ //#region src/resource/resource-title.ts
1613
+ function resolveResourceTitle(caps, t, resource, slot, params) {
1614
+ return resolveContentSlot(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
1615
+ }
1616
+ //#endregion
1902
1617
  //#region src/resource/page-header.tsx
1903
- function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1618
+ function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1619
+ const resource = routeResourceKey(resourceInput);
1904
1620
  const app = useApp();
1905
1621
  const t = useT();
1906
1622
  const def = app.registry.get(resource);
@@ -1909,10 +1625,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1909
1625
  const resourceSingle = resolveResourceLabel(caps, resource, "single", def);
1910
1626
  const labelSingle = labelOverride ?? resourceSingle;
1911
1627
  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: {
1628
+ const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
1913
1629
  label: labelSingle,
1914
1630
  name: recordName
1915
- } }) : t("pageHeader.editTitle", { params: { label: labelSingle } }) : t("pageHeader.createTitle", { params: { label: labelSingle } }));
1631
+ }) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
1916
1632
  useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
1917
1633
  const showBack = backLink && mode !== "list" && !breadcrumbs;
1918
1634
  const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
@@ -1943,9 +1659,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1943
1659
  ]
1944
1660
  });
1945
1661
  }
1946
- function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1662
+ function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
1663
+ const resource = routeResourceKey(resourceInput);
1947
1664
  const def = useApp().registry.get(resource);
1948
- const recordName = useProjectedTitle(resource, record?.id, record);
1665
+ const recordName = useProjectedTitle(resource, recordKeyOf(def, record), record);
1949
1666
  usePublishDocVariant(resource, record);
1950
1667
  if (!def) return null;
1951
1668
  return jsx(PageHeader, {
@@ -1960,16 +1677,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1960
1677
  }), extraActions] })
1961
1678
  });
1962
1679
  }
1963
- function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
1964
- const app = useApp();
1965
- const def = app.registry.get(resource);
1680
+ function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
1681
+ const resource = routeResourceKey(resourceInput);
1682
+ const def = useApp().registry.get(resource);
1966
1683
  const caps = useI18nCaps(resource);
1967
- const recordName = useProjectedTitle(resource, record?.id, record);
1684
+ const recordKey = recordKeyOf(def, record);
1685
+ const recordName = useProjectedTitle(resource, recordKey, record);
1686
+ const back = useReturnTarget(resource, "edit", String(recordKey ?? ""));
1968
1687
  return jsx(PageHeader, {
1969
1688
  resource,
1970
1689
  mode: "edit",
1971
1690
  recordName,
1972
- backHref: editBackHref(app.registry, resource, String(record.id), backHref),
1691
+ backHref: backHref ?? back,
1973
1692
  breadcrumbs,
1974
1693
  backLabel: recordName ? `${resolveResourceLabel(caps, resource, "single", def)}: ${recordName}` : void 0,
1975
1694
  actions: jsx(ActionOverflow, {
@@ -2169,7 +1888,8 @@ function ItemNode({ node, row, fallback }) {
2169
1888
  const viewProps = {
2170
1889
  props,
2171
1890
  data: {},
2172
- t: placement.viewProps?.t ?? ((name) => name)
1891
+ t: placement.viewProps?.t ?? ((name) => name),
1892
+ ...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
2173
1893
  };
2174
1894
  return jsx(View, { ...viewProps });
2175
1895
  }
@@ -2251,24 +1971,32 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
2251
1971
  const t = useOptionalT();
2252
1972
  return jsxs("section", {
2253
1973
  ...nodeAttributes,
1974
+ ...title === void 0 ? {} : { "aria-labelledby": titleId },
2254
1975
  children: [
2255
1976
  title === void 0 && info === null && !collapsible ? null : jsxs("div", {
2256
1977
  "data-buro-region": "panel-title",
2257
1978
  className: "flex items-center gap-1",
2258
1979
  children: [
2259
- collapsible ? jsx("button", {
1980
+ collapsible && title !== void 0 ? jsx("h3", {
1981
+ id: titleId,
1982
+ className: "min-w-0 flex-1 text-sm font-semibold",
1983
+ children: jsxs("button", {
1984
+ type: "button",
1985
+ "aria-expanded": open,
1986
+ "aria-controls": bodyId,
1987
+ onClick: () => onToggle(!open),
1988
+ className: "-mx-1 flex w-full items-center gap-1 rounded px-1 text-left hover:bg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
1989
+ children: [jsx(Chevron, { open }), title]
1990
+ })
1991
+ }) : collapsible ? jsx("button", {
2260
1992
  type: "button",
2261
1993
  "aria-expanded": open,
2262
1994
  "aria-controls": bodyId,
2263
1995
  "aria-label": open ? t("screen.collapse", { defaultValue: "Collapse" }) : t("screen.expand", { defaultValue: "Expand" }),
2264
1996
  onClick: () => onToggle(!open),
2265
1997
  className: "inline-flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-muted",
2266
- children: jsx(IconChevronRight, {
2267
- className: open ? "h-4 w-4 rotate-90 transition-transform" : "h-4 w-4 transition-transform",
2268
- "aria-hidden": "true"
2269
- })
2270
- }) : null,
2271
- title === void 0 ? null : jsx("h3", {
1998
+ children: jsx(Chevron, { open })
1999
+ }) : title === void 0 ? null : jsx("h3", {
2272
2000
  id: titleId,
2273
2001
  className: "text-sm font-semibold",
2274
2002
  children: title
@@ -2286,6 +2014,12 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
2286
2014
  ]
2287
2015
  });
2288
2016
  }
2017
+ function Chevron({ open }) {
2018
+ return jsx(IconChevronRight, {
2019
+ className: open ? "h-4 w-4 shrink-0 rotate-90 text-muted-foreground transition-transform" : "h-4 w-4 shrink-0 text-muted-foreground transition-transform",
2020
+ "aria-hidden": "true"
2021
+ });
2022
+ }
2289
2023
  function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
2290
2024
  if (tabs.length === 1) return jsx("div", {
2291
2025
  ...nodeAttributes,
@@ -2310,7 +2044,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
2310
2044
  }), info]
2311
2045
  }), tabs.map((tab) => jsx(TabsContent, {
2312
2046
  value: tab.key,
2313
- forceMount: true,
2314
2047
  "data-buro-region": "tab-panel",
2315
2048
  "data-buro-key": tab.key,
2316
2049
  children: tab.panel
@@ -2398,17 +2131,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
2398
2131
  //#region src/primitives/checkbox.tsx
2399
2132
  const Checkbox = React.forwardRef(({ className, checked, ...props }, ref) => jsx(CheckboxPrimitive.Root, {
2400
2133
  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),
2134
+ 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
2135
  checked,
2403
2136
  ...props,
2404
2137
  children: jsx(CheckboxPrimitive.Indicator, {
2405
2138
  className: cn("flex items-center justify-center text-current"),
2406
2139
  children: checked === "indeterminate" ? jsx(IconMinus, {
2407
2140
  "data-icon": "minus",
2408
- className: "h-4 w-4"
2141
+ className: "size-3.5"
2409
2142
  }) : jsx(IconCheck, {
2410
2143
  "data-icon": "check",
2411
- className: "h-4 w-4"
2144
+ className: "size-3.5"
2412
2145
  })
2413
2146
  })
2414
2147
  }));
@@ -2433,6 +2166,17 @@ function useContainerNarrow(maxRem = 40) {
2433
2166
  return [setElement, narrow];
2434
2167
  }
2435
2168
  //#endregion
2169
+ //#region src/data-table/fill-column.ts
2170
+ function fillColumnId(columns) {
2171
+ const eligible = columns.filter((c) => !c.pinned && !c.service);
2172
+ const declared = eligible.find((c) => c.fill);
2173
+ if (declared !== void 0) return declared.id;
2174
+ for (let i = eligible.length - 1; i >= 0; i--) {
2175
+ const c = eligible[i];
2176
+ if (c.resize && c.max === void 0) return c.id;
2177
+ }
2178
+ }
2179
+ //#endregion
2436
2180
  //#region src/data-table/data-table.tsx
2437
2181
  function pinEdgeClass(isLastLeft, isFirstRight) {
2438
2182
  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 +2188,8 @@ function pinEdgeClassFor(column, overflowing) {
2444
2188
  if (!pinned || !overflowing) return "";
2445
2189
  return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
2446
2190
  }
2447
- function getPinStyles(column, overflowing) {
2191
+ function getPinStyles(column, overflowing, fill = false) {
2192
+ if (fill) return {};
2448
2193
  const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
2449
2194
  const pinned = column.getIsPinned();
2450
2195
  if (!pinned || !overflowing) return explicitWidth;
@@ -2469,21 +2214,35 @@ function columnMetaNoRowNav(meta) {
2469
2214
  function columnMetaNoClip(meta) {
2470
2215
  return meta?.noClip === true;
2471
2216
  }
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");
2217
+ function columnMetaWrap(meta) {
2218
+ return meta?.wrap === true;
2219
+ }
2220
+ function columnMetaFill(meta) {
2221
+ return meta?.fill === true;
2222
+ }
2223
+ function tableFillColumnId(table) {
2224
+ if (table.options.enableColumnResizing !== true) return void 0;
2225
+ return fillColumnId(table.getVisibleLeafColumns().map((c) => {
2226
+ const max = c.columnDef.maxSize;
2227
+ return {
2228
+ id: c.id,
2229
+ fill: columnMetaFill(c.columnDef.meta),
2230
+ resize: c.getCanResize(),
2231
+ ...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
2232
+ pinned: c.getIsPinned() !== false,
2233
+ service: c.id.startsWith("__")
2234
+ };
2235
+ }));
2477
2236
  }
2478
- function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, noRowNav }) {
2237
+ function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
2479
2238
  const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
2480
2239
  const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
2481
- const noClip = columnMetaNoClip(cell.column.columnDef.meta);
2240
+ const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
2482
2241
  return jsx(TableCell, {
2483
2242
  "data-testid": testId,
2484
2243
  "data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
2485
2244
  onClick: onCellClick,
2486
- style: getPinStyles(liveColumn, overflowing),
2245
+ style: getPinStyles(liveColumn, overflowing, fill),
2487
2246
  className: cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
2488
2247
  children: resizable && !noClip ? jsx("div", {
2489
2248
  className: "truncate",
@@ -2495,7 +2254,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
2495
2254
  function MemoCellInner(props) {
2496
2255
  return renderCellMarkup(props);
2497
2256
  }
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);
2257
+ 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
2258
  function SelectionCellInner(props) {
2500
2259
  const row = props.cell.row;
2501
2260
  const toggle = (e) => {
@@ -2509,7 +2268,7 @@ function SelectionCellInner(props) {
2509
2268
  });
2510
2269
  }
2511
2270
  const SelectionCell = memo(SelectionCellInner);
2512
- function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2271
+ function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2513
2272
  const clickable = rowHref !== void 0 || onRowOpen !== void 0;
2514
2273
  return jsx(TableRow, {
2515
2274
  "data-state": isSelected ? "selected" : void 0,
@@ -2522,6 +2281,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2522
2281
  isSelected,
2523
2282
  resizable,
2524
2283
  overflowing,
2284
+ fill: cell.column.id === fillColumnId,
2525
2285
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2526
2286
  testId: getCellTestId?.(row.original, cell.column.id)
2527
2287
  }, cell.id) : jsx(MemoCell, {
@@ -2529,13 +2289,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2529
2289
  rowOriginal: row.original,
2530
2290
  resizable,
2531
2291
  overflowing,
2292
+ fill: cell.column.id === fillColumnId,
2532
2293
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2533
2294
  testId: getCellTestId?.(row.original, cell.column.id)
2534
2295
  }, cell.id))
2535
2296
  });
2536
2297
  }
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 }) {
2298
+ 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);
2299
+ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2539
2300
  return jsx(TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => jsx(TableRow, {
2540
2301
  "aria-busy": "true",
2541
2302
  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 +2313,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2552
2313
  resizable,
2553
2314
  overflowing,
2554
2315
  columnLayoutKey,
2316
+ fillColumnId,
2555
2317
  selectionColumnId,
2556
2318
  getCellTestId,
2557
2319
  getRowTestId,
@@ -2563,7 +2325,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2563
2325
  }
2564
2326
  const DataTableBody = memo(DataTableBodyInner, (prev, next) => {
2565
2327
  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;
2328
+ 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
2329
  });
2568
2330
  function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
2569
2331
  const t = useOptionalT();
@@ -2593,8 +2355,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2593
2355
  const rows = table.getRowModel().rows;
2594
2356
  const headerGroups = table.getHeaderGroups();
2595
2357
  const visibleColumnCount = table.getVisibleLeafColumns().length;
2358
+ const fillId = tableFillColumnId(table);
2596
2359
  const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
2597
2360
  const resizable = table.options.enableColumnResizing === true;
2361
+ const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
2598
2362
  const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
2599
2363
  const sizingState = table.getState().columnSizing;
2600
2364
  const sizingInfo = table.getState().columnSizingInfo;
@@ -2686,7 +2450,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2686
2450
  className,
2687
2451
  style: resizable ? {
2688
2452
  ...columnSizeVars,
2689
- minWidth: "100%",
2453
+ width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
2690
2454
  tableLayout: "fixed"
2691
2455
  } : void 0,
2692
2456
  children: [jsx(TableHeader, { children: headerGroups.map((hg) => jsx(TableRow, { children: hg.headers.map((header) => {
@@ -2697,7 +2461,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2697
2461
  return jsxs(TableHead, {
2698
2462
  colSpan: header.colSpan,
2699
2463
  "aria-sort": ariaSort,
2700
- style: getPinStyles(header.column, overflowing),
2464
+ style: getPinStyles(header.column, overflowing, header.column.id === fillId),
2701
2465
  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
2466
  draggable: canReorder || void 0,
2703
2467
  onDragStart: canReorder ? (e) => {
@@ -2756,6 +2520,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2756
2520
  resizable,
2757
2521
  overflowing,
2758
2522
  columnLayoutKey,
2523
+ fillColumnId: fillId,
2759
2524
  selectionColumnId,
2760
2525
  isResizing: Boolean(sizingInfo.isResizingColumn),
2761
2526
  getCellTestId,
@@ -2872,7 +2637,8 @@ function ColumnHeaderMenu({ label, sortDirection, sortable, onSort, pinned, onPi
2872
2637
  }
2873
2638
  //#endregion
2874
2639
  //#region src/resource/column-accessor.ts
2875
- function columnAccessor(column) {
2640
+ function columnAccessor(column, objectPath = false) {
2641
+ if (objectPath) return { accessorFn: (row) => readColumnValue(row, column, true) };
2876
2642
  return column.includes(".") ? { accessorFn: (row) => row[column] } : { accessorKey: column };
2877
2643
  }
2878
2644
  //#endregion
@@ -2979,96 +2745,280 @@ function resolveUpdater(updater, prev) {
2979
2745
  }
2980
2746
  //#endregion
2981
2747
  //#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
- ]
2748
+ function ListErrorState(props) {
2749
+ const { error, onRetry, resource, title, className } = props;
2750
+ return jsx(LoadError, {
2751
+ surface: "list",
2752
+ error,
2753
+ ...onRetry === void 0 ? {} : { retry: onRetry },
2754
+ ...resource === void 0 ? {} : { resource },
2755
+ ...title === void 0 ? {} : { title },
2756
+ ...className === void 0 ? {} : { className }
3036
2757
  });
3037
2758
  }
3038
2759
  //#endregion
3039
- //#region src/list-kit/cursor-load-more.tsx
3040
- function CursorLoadMore({ hasMore, loading, onMore }) {
3041
- const t = useT();
3042
- if (!hasMore) return null;
3043
- return jsx("div", {
3044
- className: "flex justify-center py-4",
3045
- children: jsx(Button, {
3046
- type: "button",
3047
- variant: "outline",
3048
- size: "sm",
3049
- disabled: loading,
3050
- onClick: onMore,
3051
- "data-testid": "cursor-load-more",
3052
- children: loading ? t("pagination.loadingMore") : t("pagination.loadMore")
3053
- })
3054
- });
2760
+ //#region src/resource/use-table-layout.ts
2761
+ const WRITE_DEBOUNCE_MS = 200;
2762
+ function mutate(run, ok, then) {
2763
+ try {
2764
+ run().then((result) => {
2765
+ if (ok(result.status)) then();
2766
+ }, () => void 0);
2767
+ } catch {}
3055
2768
  }
3056
- //#endregion
3057
- //#region src/resource/resource-list.tsx
3058
- function CountRowsTrigger({ label, loading, onClick, testid, variant, className }) {
3059
- return jsxs(Button, {
3060
- type: "button",
3061
- variant,
3062
- size: "sm",
3063
- "data-testid": testid,
3064
- onClick,
3065
- disabled: loading,
3066
- "aria-label": label,
3067
- "aria-busy": loading || void 0,
3068
- className: cn("relative", className),
3069
- children: [jsx("span", {
3070
- className: loading ? "invisible" : void 0,
3071
- children: label
2769
+ function resolve(updater, current) {
2770
+ return typeof updater === "function" ? updater(current) : updater;
2771
+ }
2772
+ function sameNames(a, b) {
2773
+ return a.length === b.length && a.every((name, index) => name === b[index]);
2774
+ }
2775
+ function readSizes(sizes, rule) {
2776
+ const out = {};
2777
+ for (const [column, size] of Object.entries(sizes)) {
2778
+ const of = rule(column);
2779
+ if (of === void 0 || !of.resize) continue;
2780
+ out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
2781
+ }
2782
+ return out;
2783
+ }
2784
+ function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
2785
+ const params = useMemo(() => layoutId === void 0 ? null : {
2786
+ resource,
2787
+ list: layoutId
2788
+ }, [resource, layoutId]);
2789
+ const settings = useOptionalUiSettings(tableLayoutSetting, params);
2790
+ const settingsRef = useRef(settings);
2791
+ settingsRef.current = settings;
2792
+ const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
2793
+ const fallback = useMemo(() => ({
2794
+ sizes: {},
2795
+ order: [],
2796
+ pinning: defaultPinning
2797
+ }), [defaultPinning]);
2798
+ const [local, setLocal] = useState();
2799
+ const stored = settings?.snapshot.entries[0]?.value;
2800
+ const settled = settings === void 0 || !settings.snapshot.pending;
2801
+ const held = useRef(null);
2802
+ const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
2803
+ const latest = useRef(layout);
2804
+ latest.current = layout;
2805
+ const sizing = useMemo(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
2806
+ const sizingRef = useRef(sizing);
2807
+ sizingRef.current = sizing;
2808
+ const generation = useRef(0);
2809
+ const timer = useRef(null);
2810
+ const pending = useRef(null);
2811
+ const confirm = useCallback((sent) => {
2812
+ if (generation.current === sent && pending.current === null) setLocal(void 0);
2813
+ }, []);
2814
+ const write = useCallback((target, value) => {
2815
+ const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
2816
+ if (scopeId === void 0) return;
2817
+ const sent = generation.current;
2818
+ mutate(() => target.set({
2819
+ scopeId,
2820
+ value
2821
+ }), (status) => status === "created" || status === "updated", () => confirm(sent));
2822
+ }, [confirm]);
2823
+ const store = useCallback((target, next) => {
2824
+ if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
2825
+ write(target, next);
2826
+ return;
2827
+ }
2828
+ const entry = target.snapshot.entries[0];
2829
+ if (entry === void 0) {
2830
+ setLocal(void 0);
2831
+ return;
2832
+ }
2833
+ const sent = generation.current;
2834
+ mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
2835
+ }, [
2836
+ defaultPinning,
2837
+ write,
2838
+ confirm
2839
+ ]);
2840
+ const flush = useCallback(() => {
2841
+ if (timer.current !== null) clearTimeout(timer.current);
2842
+ timer.current = null;
2843
+ const due = pending.current;
2844
+ pending.current = null;
2845
+ if (due !== null) write(due.target, due.value);
2846
+ }, [write]);
2847
+ const change = useCallback((next, debounced, touch) => {
2848
+ latest.current = next;
2849
+ generation.current += 1;
2850
+ setLocal({
2851
+ key,
2852
+ value: next
2853
+ });
2854
+ const target = settingsRef.current;
2855
+ if (target === void 0) return;
2856
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2857
+ const edits = held.current !== null && held.current.key === key ? held.current : {
2858
+ key,
2859
+ resetAll: false,
2860
+ resetSizes: false,
2861
+ sizes: new Map()
2862
+ };
2863
+ held.current = edits;
2864
+ touch(edits);
2865
+ return;
2866
+ }
2867
+ pending.current = {
2868
+ target,
2869
+ value: next
2870
+ };
2871
+ if (!debounced) {
2872
+ flush();
2873
+ return;
2874
+ }
2875
+ if (timer.current !== null) clearTimeout(timer.current);
2876
+ timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
2877
+ }, [key, flush]);
2878
+ useEffect(() => flush, [key]);
2879
+ useEffect(() => {
2880
+ const edits = held.current;
2881
+ if (edits === null) return;
2882
+ if (edits.key !== key) {
2883
+ held.current = null;
2884
+ return;
2885
+ }
2886
+ const target = settingsRef.current;
2887
+ if (!settled || target === void 0) return;
2888
+ held.current = null;
2889
+ const base = edits.resetAll ? fallback : stored ?? fallback;
2890
+ const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
2891
+ for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
2892
+ else sizes[column] = size;
2893
+ const next = {
2894
+ sizes,
2895
+ order: edits.order ?? base.order,
2896
+ pinning: edits.pinning ?? base.pinning
2897
+ };
2898
+ latest.current = next;
2899
+ generation.current += 1;
2900
+ setLocal({
2901
+ key,
2902
+ value: next
2903
+ });
2904
+ store(target, next);
2905
+ }, [settled, key]);
2906
+ const resetToken = resetLayout?.token ?? 0;
2907
+ const seenToken = useRef(resetToken);
2908
+ useEffect(() => {
2909
+ if (resetToken === seenToken.current) return;
2910
+ seenToken.current = resetToken;
2911
+ const all = resetLayout?.mode === "all";
2912
+ const next = all ? fallback : {
2913
+ ...latest.current,
2914
+ sizes: {}
2915
+ };
2916
+ if (timer.current !== null) clearTimeout(timer.current);
2917
+ timer.current = null;
2918
+ pending.current = null;
2919
+ latest.current = next;
2920
+ generation.current += 1;
2921
+ setLocal({
2922
+ key,
2923
+ value: next
2924
+ });
2925
+ const target = settingsRef.current;
2926
+ if (target === void 0) return;
2927
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2928
+ const prior = held.current !== null && held.current.key === key ? held.current : void 0;
2929
+ held.current = all ? {
2930
+ key,
2931
+ resetAll: true,
2932
+ resetSizes: false,
2933
+ sizes: new Map()
2934
+ } : {
2935
+ key,
2936
+ resetAll: prior?.resetAll ?? false,
2937
+ resetSizes: true,
2938
+ sizes: new Map(),
2939
+ ...prior?.order === void 0 ? {} : { order: prior.order },
2940
+ ...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
2941
+ };
2942
+ return;
2943
+ }
2944
+ store(target, next);
2945
+ }, [resetToken]);
2946
+ const setColumnSizing = useCallback((updater) => {
2947
+ const before = sizingRef.current;
2948
+ const sizes = resolve(updater, before);
2949
+ sizingRef.current = sizes;
2950
+ change({
2951
+ ...latest.current,
2952
+ sizes
2953
+ }, true, (edits) => {
2954
+ for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
2955
+ });
2956
+ }, [change]);
2957
+ const setColumnOrder = useCallback((updater) => {
2958
+ const order = resolve(updater, latest.current.order);
2959
+ change({
2960
+ ...latest.current,
2961
+ order
2962
+ }, false, (edits) => {
2963
+ edits.order = order;
2964
+ });
2965
+ }, [change]);
2966
+ const setColumnPinning = useCallback((updater) => {
2967
+ const resolved = resolve(updater, latest.current.pinning);
2968
+ const pinning = {
2969
+ left: resolved.left ?? [],
2970
+ right: resolved.right ?? []
2971
+ };
2972
+ change({
2973
+ ...latest.current,
2974
+ pinning
2975
+ }, false, (edits) => {
2976
+ edits.pinning = pinning;
2977
+ });
2978
+ }, [change]);
2979
+ return {
2980
+ columnSizing: sizing,
2981
+ setColumnSizing,
2982
+ columnOrder: layout.order,
2983
+ setColumnOrder,
2984
+ columnPinning: layout.pinning,
2985
+ setColumnPinning
2986
+ };
2987
+ }
2988
+ //#endregion
2989
+ //#region src/list-kit/cursor-load-more.tsx
2990
+ function CursorLoadMore({ hasMore, loading, onMore }) {
2991
+ const t = useT();
2992
+ if (!hasMore) return null;
2993
+ return jsx("div", {
2994
+ className: "flex justify-center py-4",
2995
+ children: jsx(Button, {
2996
+ type: "button",
2997
+ variant: "outline",
2998
+ size: "sm",
2999
+ disabled: loading,
3000
+ onClick: onMore,
3001
+ "data-testid": "cursor-load-more",
3002
+ children: loading ? t("pagination.loadingMore") : t("pagination.loadMore")
3003
+ })
3004
+ });
3005
+ }
3006
+ //#endregion
3007
+ //#region src/resource/resource-list.tsx
3008
+ function CountRowsTrigger({ label, loading, onClick, testid, variant, className }) {
3009
+ return jsxs(Button, {
3010
+ type: "button",
3011
+ variant,
3012
+ size: "sm",
3013
+ "data-testid": testid,
3014
+ onClick,
3015
+ disabled: loading,
3016
+ "aria-label": label,
3017
+ "aria-busy": loading || void 0,
3018
+ className: cn("relative", className),
3019
+ children: [jsx("span", {
3020
+ className: loading ? "invisible" : void 0,
3021
+ children: label
3072
3022
  }), loading && jsx(IconLoader2, {
3073
3023
  className: "absolute inset-0 m-auto size-3.5 animate-spin",
3074
3024
  "aria-hidden": "true"
@@ -3077,6 +3027,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
3077
3027
  }
3078
3028
  const SELECT_COL_ID = "__select__";
3079
3029
  const ACTIONS_COL_ID = "__actions__";
3030
+ const DEFAULT_PINNING = Object.freeze({
3031
+ left: Object.freeze([SELECT_COL_ID, "id"]),
3032
+ right: Object.freeze([ACTIONS_COL_ID])
3033
+ });
3080
3034
  const ROW_ACTION_CONTROL_PX = 28;
3081
3035
  const ROW_ACTION_GAP_PX = 4;
3082
3036
  const ACTIONS_CELL_PADDING_PX = 16;
@@ -3088,29 +3042,30 @@ function actionsColumnSize(items) {
3088
3042
  const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
3089
3043
  return Math.max(ACTIONS_COL_MIN_PX, strip);
3090
3044
  }
3091
- const COLUMN_SIZE_BY_TYPE = {
3092
- boolean: 96,
3093
- number: 112
3094
- };
3095
- const COLUMN_SIZE_BY_FORMAT = {
3096
- date: 132,
3097
- "date-time": 132
3045
+ const ALIGN_CLASS = {
3046
+ start: "text-start",
3047
+ center: "text-center",
3048
+ end: "text-end"
3098
3049
  };
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 = [
3050
+ const STANDALONE_COLUMN_UI = Object.freeze({
3051
+ default: 160,
3052
+ min: 90,
3053
+ align: "start",
3054
+ resize: true,
3055
+ fill: false,
3056
+ display: Object.freeze({ params: Object.freeze({}) })
3057
+ });
3058
+ function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
3106
3059
  10,
3107
3060
  25,
3108
3061
  50,
3109
3062
  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 }) {
3063
+ ], 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 }) {
3064
+ const resource = routeResourceKey(resourceInput);
3111
3065
  const app = useApp();
3112
3066
  const tid = useTestIds();
3113
3067
  const t = useOptionalT();
3068
+ const chromeT = useChromeT();
3114
3069
  const locale = useLocale();
3115
3070
  const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
3116
3071
  const i18nCaps = useI18nCaps(resource);
@@ -3132,23 +3087,6 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3132
3087
  useEffect(() => {
3133
3088
  emittedRef.current = null;
3134
3089
  }, [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
3090
  useEffect(() => {
3153
3091
  if (controlled) return;
3154
3092
  setOwnSelection({});
@@ -3158,7 +3096,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3158
3096
  const accessPending = listAccess.status === "pending" || pathAccess.pending;
3159
3097
  const declaredColumnKeys = useMemo(() => {
3160
3098
  if (!def) return [];
3161
- return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
3099
+ return [...columnsOverride ?? def.views?.list?.columns?.map(columnName) ?? Object.keys(def.fields)];
3162
3100
  }, [def, columnsOverride]);
3163
3101
  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
3102
  accessPending,
@@ -3208,10 +3146,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3208
3146
  const resourceLabel = resolveResourceLabel(i18nCaps, resource, "plural", def);
3209
3147
  const recordSingular = resolveResourceLabel(i18nCaps, resource, "single", def);
3210
3148
  const ariaLabel = resourceLabel;
3211
- const actionsHeader = t("actions.column", { defaultValue: "Actions" });
3149
+ const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
3212
3150
  const viewRow = useViewRow(resource);
3213
3151
  const volatileRef = useRef({
3214
3152
  onSort,
3153
+ sortable,
3215
3154
  onHideColumn,
3216
3155
  requiredColumns,
3217
3156
  renderCell,
@@ -3223,6 +3162,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3223
3162
  });
3224
3163
  volatileRef.current = {
3225
3164
  onSort,
3165
+ sortable,
3226
3166
  onHideColumn,
3227
3167
  requiredColumns,
3228
3168
  renderCell,
@@ -3246,6 +3186,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3246
3186
  }, [rowLinkKey]);
3247
3187
  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
3188
  const [listRoot, narrow] = useContainerNarrow();
3189
+ const columnUiOf = useMemo(() => {
3190
+ const entries = new Map();
3191
+ for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
3192
+ const name = columnName(entry);
3193
+ if (!entries.has(name)) entries.set(name, entry);
3194
+ }
3195
+ return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : resolveColumnUi(def, entries.get(col) ?? col, app.registry, columnTypeWidth));
3196
+ }, [
3197
+ app,
3198
+ def,
3199
+ columnUi
3200
+ ]);
3201
+ const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
3202
+ resource,
3203
+ layoutId,
3204
+ rule: useMemo(() => {
3205
+ const known = new Set([
3206
+ ...declaredColumnKeys,
3207
+ ...(def?.views?.list?.columns ?? []).map(columnName),
3208
+ ...(def?.views?.list?.optionalColumns ?? []).map(columnName),
3209
+ ...Object.keys(def?.fields ?? {})
3210
+ ]);
3211
+ return (col) => {
3212
+ if (!known.has(col)) return void 0;
3213
+ const ui = columnUiOf(col);
3214
+ return {
3215
+ min: ui.min,
3216
+ max: ui.max,
3217
+ resize: ui.resize
3218
+ };
3219
+ };
3220
+ }, [
3221
+ declaredColumnKeys,
3222
+ def,
3223
+ columnUiOf
3224
+ ]),
3225
+ defaultPinning: DEFAULT_PINNING,
3226
+ resetLayout
3227
+ });
3249
3228
  const tableColumns = useMemo(() => {
3250
3229
  const cols = [];
3251
3230
  if (selectable) cols.push({
@@ -3268,19 +3247,36 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3268
3247
  });
3269
3248
  for (const col of visibleColumnKeys) {
3270
3249
  const target = columnTarget(app.registry, resource, col);
3271
- const field = target === void 0 ? def?.fields?.[col] : target.field;
3250
+ const objectPath = isObjectColumn(app.registry, resource, col);
3251
+ const field = target !== void 0 ? target.field : objectPath ? resolveObjectColumn(app.registry, resource, col)?.field : def === void 0 ? void 0 : declaredField(def, col);
3272
3252
  const declaredLabel = declaredText("field", field, "label");
3273
3253
  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);
3254
+ const ui = columnUiOf(col);
3255
+ const frameField = {
3256
+ ...field ?? { type: "unknown" },
3257
+ name: target === void 0 ? col : target.name
3258
+ };
3259
+ const cellEntry = { display: {
3260
+ ...ui.display,
3261
+ ...ui.display.dateDisplay
3262
+ } };
3263
+ const wraps = ui.display.overflow === "wrap";
3264
+ const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && isColumnSortable(def, col, app, app.registry));
3277
3265
  cols.push({
3278
3266
  id: col,
3279
- ...columnAccessor(col),
3280
- ...kindSize !== void 0 ? { size: kindSize } : {},
3281
- enableSorting: !linkColumn && Boolean(volatileRef.current.onSort),
3267
+ ...columnAccessor(col, objectPath),
3268
+ size: ui.default,
3269
+ minSize: ui.min,
3270
+ maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
3271
+ enableResizing: ui.resize,
3272
+ enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
3282
3273
  enableHiding: true,
3283
- meta: { testid: col },
3274
+ meta: {
3275
+ testid: col,
3276
+ className: ALIGN_CLASS[ui.align],
3277
+ ...wraps ? { wrap: true } : {},
3278
+ ...ui.fill ? { fill: true } : {}
3279
+ },
3284
3280
  header: ({ column, table }) => {
3285
3281
  const v = volatileRef.current;
3286
3282
  const pinSide = column.getIsPinned();
@@ -3311,9 +3307,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3311
3307
  };
3312
3308
  return jsx(ColumnHeaderMenu, {
3313
3309
  label: headerLabel,
3314
- sortable: !linkColumn && Boolean(v.onSort),
3310
+ sortable: columnSortable && Boolean(v.onSort),
3315
3311
  sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
3316
- onSort: !linkColumn && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3312
+ onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3317
3313
  pinned: column.getIsPinned(),
3318
3314
  onPin: (side) => column.pin(side),
3319
3315
  canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
@@ -3327,7 +3323,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3327
3323
  cell: ({ row }) => {
3328
3324
  const raw = row.original;
3329
3325
  const record = volatileRef.current.viewRow(raw);
3330
- const value = record[col];
3326
+ const value = readColumnValue(record, col, objectPath);
3331
3327
  const customCell = volatileRef.current.renderCell?.(col, value, record);
3332
3328
  const cellInner = customCell !== void 0 ? customCell : jsx(ResolvedFieldDisplay, {
3333
3329
  resource: target === void 0 ? resourceRef : target.ref,
@@ -3335,12 +3331,21 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3335
3331
  field: field ?? { type: "unknown" },
3336
3332
  value,
3337
3333
  record,
3334
+ uiSurface: "column",
3335
+ columnEntry: cellEntry,
3338
3336
  fallback: jsx("span", { children: String(value ?? "") })
3339
3337
  });
3338
+ const framed = (node) => customCell !== void 0 ? node : jsx(FieldValueFrame, {
3339
+ field: frameField,
3340
+ value,
3341
+ surface: "column",
3342
+ entry: cellEntry,
3343
+ children: node
3344
+ });
3340
3345
  const href = volatileRef.current.rowHref?.(record);
3341
3346
  const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
3342
3347
  const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
3343
- if (quick !== null) return href ? jsx(ClaimedQuickLink, {
3348
+ if (quick !== null) return framed(href ? jsx(ClaimedQuickLink, {
3344
3349
  href,
3345
3350
  className: relationLinkClassName,
3346
3351
  onOpen: quick,
@@ -3354,13 +3359,13 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3354
3359
  quick();
3355
3360
  },
3356
3361
  children: cellInner
3357
- });
3358
- return isFirstContentCell && href ? jsx(RouterLink, {
3362
+ }));
3363
+ return framed(isFirstContentCell && href ? jsx(RouterLink, {
3359
3364
  href,
3360
3365
  className: relationLinkClassName,
3361
3366
  onClick: (e) => e.stopPropagation(),
3362
3367
  children: cellInner
3363
- }) : cellInner;
3368
+ }) : cellInner);
3364
3369
  }
3365
3370
  });
3366
3371
  }
@@ -3369,7 +3374,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3369
3374
  id: ACTIONS_COL_ID,
3370
3375
  enableSorting: false,
3371
3376
  enableResizing: false,
3372
- size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
3377
+ size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
3373
3378
  enableHiding: false,
3374
3379
  header: () => jsx("span", {
3375
3380
  className: "sr-only",
@@ -3380,7 +3385,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3380
3385
  noRowNav: true,
3381
3386
  noClip: true
3382
3387
  },
3383
- cell: ({ row }) => jsx(ActionOverflow, {
3388
+ cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : jsx(ActionOverflow, {
3384
3389
  resource,
3385
3390
  record: row.original,
3386
3391
  surface: "row"
@@ -3400,7 +3405,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3400
3405
  actionsHeader,
3401
3406
  i18nCaps,
3402
3407
  rowLinkKey,
3403
- narrow
3408
+ narrow,
3409
+ columnUiOf
3404
3410
  ]);
3405
3411
  const handleSelectionChange = (updater) => {
3406
3412
  if (controlled) {
@@ -3503,8 +3509,23 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3503
3509
  const isWrongPage = page > 1;
3504
3510
  const EmptyIcon = isFiltered ? IconFilterX : IconInbox;
3505
3511
  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 } });
3512
+ const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
3513
+ resource,
3514
+ params: { items }
3515
+ }) : t("list.emptyTitle", {
3516
+ resource,
3517
+ params: { items }
3518
+ });
3519
+ const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
3520
+ resource,
3521
+ params: { items }
3522
+ }) : createHref ? t("list.emptyBody", {
3523
+ resource,
3524
+ params: { item: recordSingular.toLowerCase() }
3525
+ }) : t("list.emptyBodyReadOnly", {
3526
+ resource,
3527
+ params: { items }
3528
+ });
3508
3529
  return jsxs(Fragment$1, { children: [jsxs("div", {
3509
3530
  className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
3510
3531
  "data-testid": tid.listEmpty(resource),
@@ -3529,7 +3550,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3529
3550
  jsxs("div", {
3530
3551
  className: "flex justify-center gap-2 pt-2",
3531
3552
  children: [
3532
- isFiltered && onClearFilter && jsx(Button, {
3553
+ onClearFilter && jsx(Button, {
3533
3554
  type: "button",
3534
3555
  variant: "outline",
3535
3556
  size: "sm",
@@ -3717,43 +3738,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3717
3738
  })
3718
3739
  });
3719
3740
  }
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
- }
3741
+ //#endregion
3742
+ //#region src/blocks/related-list-unbound.tsx
3743
+ function UnboundRelatedList(props) {
3744
+ const t = useOptionalT();
3745
+ return jsxs("div", {
3746
+ className: "flex flex-col gap-2",
3747
+ "data-testid": `list-unbound:${props.resource}`,
3748
+ children: [props.title === void 0 ? null : jsx("h3", {
3749
+ className: "text-sm font-semibold",
3750
+ children: props.title
3751
+ }), jsx("p", {
3752
+ className: "text-sm text-muted-foreground",
3753
+ children: props.empty ?? t("table.empty", { defaultValue: "No records" })
3754
+ })]
3729
3755
  });
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];
3756
+ }
3757
+ function placementScope(scope, owner) {
3758
+ return scope ?? (owner === void 0 ? "session" : "none");
3759
+ }
3760
+ //#endregion
3761
+ //#region src/list-kit/pieces/bulk-reach.tsx
3762
+ function useBulkFlowReachable(resource) {
3763
+ const app = useApp();
3764
+ const control = useFlowRowResolver(resource);
3765
+ return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => isFlowAction(action) && control(action.name, void 0).state !== "absent");
3766
+ }
3767
+ function ExportReach(props) {
3768
+ const { available } = useResourceExport(props.resource);
3769
+ const capability = useCapability("run", {
3770
+ resource: props.resource,
3771
+ target: {
3772
+ kind: "action",
3773
+ name: "export"
3774
+ },
3775
+ mode: "list"
3776
+ });
3777
+ return props.children(available && capability.allowed);
3778
+ }
3779
+ function BulkExportReach(props) {
3780
+ return useOptionalDataRuntime() !== null ? jsx(ExportReach, { ...props }) : props.children(false);
3757
3781
  }
3758
3782
  //#endregion
3759
3783
  //#region src/list-kit/pieces/piece.tsx
@@ -3821,15 +3845,47 @@ function ForceTableView(props) {
3821
3845
  }
3822
3846
  //#endregion
3823
3847
  //#region src/list-kit/pieces/list-body.tsx
3848
+ function LinkDetachRowButton(props) {
3849
+ const { resource, linkEditing } = useListSurface();
3850
+ const t = useOptionalT();
3851
+ const [busy, setBusy] = useState(false);
3852
+ const detach = linkEditing?.detach;
3853
+ if (detach === void 0) return null;
3854
+ const label = t("field.link.detach", { defaultValue: "Detach" });
3855
+ return jsx(Button, {
3856
+ type: "button",
3857
+ size: "icon",
3858
+ variant: "ghost",
3859
+ className: "size-7",
3860
+ "aria-label": label,
3861
+ title: label,
3862
+ disabled: busy,
3863
+ "data-testid": `list-link-detach:${resource}:${props.row.id}`,
3864
+ onClick: (event) => {
3865
+ event.stopPropagation();
3866
+ setBusy(true);
3867
+ detach.onDetach([props.row]).finally(() => setBusy(false));
3868
+ },
3869
+ children: jsx(IconUnlink, {
3870
+ className: "size-4",
3871
+ "aria-hidden": "true"
3872
+ })
3873
+ });
3874
+ }
3875
+ const linkDetachRow = (row) => jsx(LinkDetachRowButton, { row });
3824
3876
  function ListBodyView() {
3825
3877
  usePieceMount("body");
3826
3878
  const list = useResourceListContext();
3827
- const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly } = useListSurface();
3879
+ const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
3880
+ const detachable = readOnly && linkEditing?.detach !== void 0;
3881
+ const flowReachable = useBulkFlowReachable(resource);
3882
+ const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
3828
3883
  const item = useContext(ListItemContext);
3829
3884
  const rowsView = useMemo(() => rowsViewFor?.(item), [rowsViewFor, item]);
3830
3885
  const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
3831
3886
  const rows = policyRowsOf(list, list.rows);
3832
- return jsx(ResourceList, {
3887
+ if (query.unbound) return jsx(UnboundRelatedList, { resource });
3888
+ const view = (exportReachable) => jsx(ResourceList, {
3833
3889
  resource,
3834
3890
  data: rows,
3835
3891
  total: pagination.total,
@@ -3846,29 +3902,50 @@ function ListBodyView() {
3846
3902
  onRequestCount: count.available ? count.request : void 0,
3847
3903
  isCountingRows: count.isLoading,
3848
3904
  columns: columns.visible,
3905
+ columnUi: columns.ui,
3849
3906
  isLoading: query.isLoading,
3850
3907
  error: query.error,
3851
3908
  onRetry: query.refetch,
3852
3909
  createHref: route && createAvailable && createPage ? routing.createHref : void 0,
3853
3910
  ...rowProps,
3854
3911
  ...rowsView === void 0 ? {} : { rowsView },
3855
- selectable: tableView && !readOnly,
3856
- rowActions: !readOnly,
3912
+ selectable: selectable(exportReachable),
3913
+ rowActions: detachable ? linkDetachRow : !readOnly,
3857
3914
  selectedIds: selection.ids,
3858
3915
  onSelectionChange: selection.set,
3859
3916
  isFiltered: filters.isFiltered,
3860
- onClearFilter: filters.clearAll,
3917
+ onClearFilter: filters.canReset ? filters.reset : void 0,
3861
3918
  emptyState,
3862
3919
  sort: sorting.value ?? [],
3863
3920
  requiredColumns: columns.required,
3864
3921
  resetLayout: columns.resetLayout,
3922
+ layoutId,
3865
3923
  onHideColumn: columns.hide,
3866
3924
  onSort: sorting.toggle,
3925
+ sortable: sorting.sortable,
3867
3926
  renderCell
3868
3927
  });
3928
+ return jsx(BulkExportReach, {
3929
+ resource,
3930
+ children: view
3931
+ });
3869
3932
  }
3870
3933
  const ListBody = listPiece("ListBody", ListBodyView);
3871
3934
  //#endregion
3935
+ //#region src/resource/form-context.tsx
3936
+ const FormControlContext = createContext(null);
3937
+ const FormControlProvider = FormControlContext.Provider;
3938
+ function useFormControlOf(controller) {
3939
+ const latest = useRef(controller);
3940
+ latest.current = controller;
3941
+ return useMemo(() => ({ setValues: (patch) => {
3942
+ for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
3943
+ } }), []);
3944
+ }
3945
+ function useFormControl() {
3946
+ return useContext(FormControlContext);
3947
+ }
3948
+ //#endregion
3872
3949
  //#region src/record/route-screen-context.ts
3873
3950
  const RouteScreenClaimContext = createContext(false);
3874
3951
  const RouteScreenContext = createContext(false);
@@ -3992,7 +4069,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
3992
4069
  region,
3993
4070
  form,
3994
4071
  labelledBy: title === void 0 ? void 0 : titleId,
3995
- children: renderChildren(node.children)
4072
+ children: renderChildren(node.children, node)
3996
4073
  }) : null,
3997
4074
  ...title === void 0 ? {} : { title },
3998
4075
  ...description === void 0 ? {} : { description },
@@ -4027,10 +4104,12 @@ function TabsHost({ node, renderChildren }) {
4027
4104
  const state = useScreenState();
4028
4105
  const text = useNodeText();
4029
4106
  const panels = node.children.filter((child) => child.state !== "absent");
4030
- const open = panels.find((child) => child.open === true) ?? panels[0];
4107
+ const declared = compiled.nodes[node.index].options.default;
4108
+ const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
4109
+ const open = panels.find((child) => child.open === true) ?? effectiveDefault;
4031
4110
  const urlSelect = useTabsUrlSelect();
4032
4111
  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);
4112
+ if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
4034
4113
  else state?.openPanel(node.id, panelValue(panel));
4035
4114
  };
4036
4115
  const onSelect = (key) => {
@@ -4042,7 +4121,7 @@ function TabsHost({ node, renderChildren }) {
4042
4121
  key: panelValue(panel),
4043
4122
  title: titleOf(panel),
4044
4123
  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
4124
+ panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children, panel) }) : null
4046
4125
  }));
4047
4126
  const { view: View, id } = useNodeView(node, "tabs");
4048
4127
  const t = useFrameT(id);
@@ -4153,7 +4232,7 @@ function WizardHost({ node, renderChildren }) {
4153
4232
  region: "step-panel",
4154
4233
  form,
4155
4234
  labelledBy: titleId,
4156
- children: renderChildren(panel.children)
4235
+ children: renderChildren(panel.children, panel)
4157
4236
  }),
4158
4237
  ...description === void 0 ? {} : { description },
4159
4238
  titleId,
@@ -4199,18 +4278,23 @@ function WizardHost({ node, renderChildren }) {
4199
4278
  //#region src/screen/nodes.tsx
4200
4279
  function FailedNode({ node }) {
4201
4280
  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
- })]
4281
+ let error;
4282
+ for (const name of node.failedOn) {
4283
+ const source = controller.sources[name];
4284
+ if (source?.status === "failed") {
4285
+ error = source.error;
4286
+ break;
4287
+ }
4288
+ }
4289
+ return jsx(LoadError, {
4290
+ surface: "tab",
4291
+ error,
4292
+ retry: () => controller.retry(node.failedOn),
4293
+ fallback: {
4294
+ key: "screen.failed",
4295
+ default: "This section could not be loaded."
4296
+ },
4297
+ attributes: nodeAttributes(node)
4214
4298
  });
4215
4299
  }
4216
4300
  function failedOnlyOnMeta(node) {
@@ -4223,8 +4307,17 @@ function drawn(node, hiddenFields) {
4223
4307
  if (node.state === "absent") return false;
4224
4308
  return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
4225
4309
  }
4226
- function SectionChildren({ nodes }) {
4227
- const { hiddenFields } = useScreenTree();
4310
+ function SectionChildren({ nodes, owner }) {
4311
+ const { compiled, hiddenFields } = useScreenTree();
4312
+ const text = useNodeText();
4313
+ if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
4314
+ const empty = text(compiled.nodes[owner.index]?.options.empty);
4315
+ return empty === void 0 || empty === "" ? null : jsx("p", {
4316
+ "data-buro-region": "empty",
4317
+ className: "text-sm text-muted-foreground",
4318
+ children: empty
4319
+ });
4320
+ }
4228
4321
  const out = [];
4229
4322
  let run = [];
4230
4323
  const flush = () => {
@@ -4246,8 +4339,11 @@ function SectionChildren({ nodes }) {
4246
4339
  flush();
4247
4340
  return out;
4248
4341
  }
4249
- function renderSection(nodes) {
4250
- return jsx(SectionChildren, { nodes });
4342
+ function renderSection(nodes, owner) {
4343
+ return jsx(SectionChildren, {
4344
+ nodes,
4345
+ owner
4346
+ });
4251
4347
  }
4252
4348
  function Layout({ node }) {
4253
4349
  return jsx("div", {
@@ -4369,17 +4465,14 @@ function pageHost(page) {
4369
4465
  }
4370
4466
  //#endregion
4371
4467
  //#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) {
4468
+ function issueText(issue, caps, i18nPrefix) {
4377
4469
  return resolveValidationMessage({
4378
4470
  code: issue.code ?? "",
4379
4471
  message: issue.message,
4380
- ...issue.params === void 0 ? {} : { params: issue.params }
4472
+ ...issue.check === void 0 ? {} : { check: issue.check }
4381
4473
  }, {
4382
- t,
4474
+ has: caps.has,
4475
+ t: caps.t,
4383
4476
  ...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
4384
4477
  });
4385
4478
  }
@@ -4446,7 +4539,9 @@ function DeclarativeFormBody(props) {
4446
4539
  ...props.surface === void 0 ? {} : { surface: props.surface },
4447
4540
  ...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
4448
4541
  });
4449
- const t = useOptionalT();
4542
+ const formControl = useFormControlOf(form);
4543
+ const chromeT = useChromeT();
4544
+ const caps = useI18nCaps();
4450
4545
  const disabled = props.disabled === true;
4451
4546
  const uid = useId();
4452
4547
  const leafIds = useMemo(() => ({
@@ -4456,8 +4551,8 @@ function DeclarativeFormBody(props) {
4456
4551
  const formErrors = form.errors.filter((issue) => issue.field === "_form");
4457
4552
  const retryableChecks = [];
4458
4553
  for (const issue of form.checkOutcome?.blockers ?? []) {
4459
- const checkId = issue.params?.checkId;
4460
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
4554
+ const checkId = issue.check?.id;
4555
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
4461
4556
  if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
4462
4557
  }
4463
4558
  const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
@@ -4470,7 +4565,7 @@ function DeclarativeFormBody(props) {
4470
4565
  formErrors.length > 0 && jsx("div", {
4471
4566
  role: "alert",
4472
4567
  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))
4568
+ children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
4474
4569
  }),
4475
4570
  retryableChecks.length > 0 && jsx("div", {
4476
4571
  className: "mb-4 flex flex-wrap gap-2",
@@ -4480,25 +4575,28 @@ function DeclarativeFormBody(props) {
4480
4575
  size: "sm",
4481
4576
  "data-testid": `form-check-retry:${checkId}`,
4482
4577
  onClick: () => void form.revalidate(),
4483
- children: translateOrFallback(t, "actions.retry", "Retry")
4578
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4484
4579
  }, checkId))
4485
4580
  }),
4486
4581
  formWarnings.length > 0 && jsx("div", {
4487
4582
  role: "status",
4488
4583
  "aria-live": "polite",
4489
4584
  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))
4585
+ children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
4491
4586
  }),
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 }
4587
+ jsx(FormControlProvider, {
4588
+ value: formControl,
4589
+ children: jsx(ScreenTree, {
4590
+ region: "form-body",
4591
+ resource: prefix,
4592
+ mode: "create",
4593
+ compiled: form.screen,
4594
+ controller: form,
4595
+ leafIds,
4596
+ ...props.renderField === void 0 ? {} : { renderField: props.renderField },
4597
+ disabled,
4598
+ ...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
4599
+ })
4502
4600
  }),
4503
4601
  props.slots?.footer === void 0 ? jsx("div", {
4504
4602
  className: "mt-4 flex justify-end",
@@ -4506,7 +4604,7 @@ function DeclarativeFormBody(props) {
4506
4604
  type: "submit",
4507
4605
  disabled: !form.canSubmit || disabled,
4508
4606
  onMouseDown: (event) => event.preventDefault(),
4509
- children: props.submitLabel ?? translateOrFallback(t, "actions.save", "Save")
4607
+ children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
4510
4608
  })
4511
4609
  }) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
4512
4610
  ]
@@ -4591,7 +4689,8 @@ function ExportOptionsDirty({ dirty }) {
4591
4689
  return null;
4592
4690
  }
4593
4691
  function getNoRunState() {}
4594
- function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount, scope }) {
4692
+ function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
4693
+ const resource = routeResourceKey(resourceInput);
4595
4694
  const t = useOptionalT();
4596
4695
  const dialogTestId = useTestIds().exportDialog(resource);
4597
4696
  const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
@@ -4713,14 +4812,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4713
4812
  })] }), jsx(ExportProgressView, {
4714
4813
  state: runState,
4715
4814
  dialogTestId,
4716
- t
4815
+ t,
4816
+ caps
4717
4817
  })] }) : jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: titleText }), jsx(DialogDescription, {
4718
4818
  className: "sr-only",
4719
4819
  children: titleText
4720
4820
  })] }), !available ? jsx("p", {
4721
4821
  role: "alert",
4722
4822
  "data-testid": `${dialogTestId}:unavailable`,
4723
- children: error?.message ?? t("export.dialog.unavailable")
4823
+ children: error === void 0 || error === null ? t("export.dialog.unavailable") : resolveErrorText(error, caps)
4724
4824
  }) : jsxs(Fragment$1, { children: [
4725
4825
  loading ? jsx("p", {
4726
4826
  role: "status",
@@ -4799,7 +4899,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4799
4899
  })
4800
4900
  });
4801
4901
  }
4802
- function ExportProgressView({ state, dialogTestId, t }) {
4902
+ function ExportProgressView({ state, dialogTestId, t, caps }) {
4803
4903
  if (state === void 0) return null;
4804
4904
  if (state.status === "running") {
4805
4905
  const total = state.progress?.total;
@@ -4820,7 +4920,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4820
4920
  if (state.status === "failed") return jsx("p", {
4821
4921
  role: "alert",
4822
4922
  "data-testid": `${dialogTestId}:error`,
4823
- children: `${t("export.dialog.failed")}: ${state.error.message}`
4923
+ children: `${t("export.dialog.failed")}: ${resolveErrorText(state.error, caps)}`
4824
4924
  });
4825
4925
  if (state.status === "canceled") return jsx("p", {
4826
4926
  "data-testid": `${dialogTestId}:canceled`,
@@ -4833,6 +4933,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4833
4933
  function BulkSummary(props) {
4834
4934
  const loaded = useLoaded(loadBulkSummary);
4835
4935
  const t = useOptionalT();
4936
+ const chromeT = useChromeT();
4836
4937
  const failed = (retry) => jsxs("div", {
4837
4938
  role: "alert",
4838
4939
  className: "flex items-center gap-2 text-sm",
@@ -4841,7 +4942,7 @@ function BulkSummary(props) {
4841
4942
  size: "sm",
4842
4943
  variant: "outline",
4843
4944
  onClick: retry,
4844
- children: text(t, "actions.retry", "Retry")
4945
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4845
4946
  })]
4846
4947
  });
4847
4948
  if (loaded.error !== void 0) return failed(loaded.retry);
@@ -4859,21 +4960,26 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4859
4960
  const control = useFlowRowResolver(resource)(actionName, void 0);
4860
4961
  const chrome = useFlowChrome(resource, actionName);
4861
4962
  const def = app.registry.get(resource);
4862
- const each = useMemo(() => {
4963
+ const compiled = useMemo(() => {
4863
4964
  try {
4864
- return def === void 0 ? false : compileFlow(def, actionName).each;
4965
+ return def === void 0 ? void 0 : compileFlow(def, actionName);
4865
4966
  } catch {
4866
- return false;
4967
+ return;
4867
4968
  }
4868
4969
  }, [def, actionName]);
4970
+ const each = compiled?.each ?? false;
4869
4971
  const [running, setRunning] = useState(false);
4870
4972
  const [summary, setSummary] = useState(null);
4973
+ const afterSummary = useRef(null);
4871
4974
  const testid = tid.bulkAction(resource, actionName);
4872
4975
  if (control.state === "absent") return null;
4873
4976
  const variant = chrome.destructive ? "destructive" : "outline";
4874
4977
  const overLimit = each && ids.length > BULK_EACH_LIMIT;
4875
4978
  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,
4979
+ reason: overLimit ? t("flow.bulkLimit", {
4980
+ defaultValue: `Select at most ${BULK_EACH_LIMIT}.`,
4981
+ count: BULK_EACH_LIMIT
4982
+ }) : control.reason,
4877
4983
  label: chrome.label,
4878
4984
  icon: chrome.icon,
4879
4985
  testid,
@@ -4887,11 +4993,20 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4887
4993
  const finish = (result, progress) => {
4888
4994
  setRunning(false);
4889
4995
  if (progress === void 0) return;
4890
- if (result.outcome !== "success") setSummary({
4996
+ const summarize = result.outcome !== "success" || progress.rereadSkipped.some((entry) => entry.policy === "warn");
4997
+ if (summarize) setSummary({
4891
4998
  progress,
4892
4999
  total: ids.length
4893
5000
  });
4894
- onSelectionChange?.(ids.filter((id) => !progress.success.includes(id)));
5001
+ const remaining = ids.filter((id) => !progress.success.includes(id));
5002
+ if (summarize && remaining.length === 0) afterSummary.current = () => onSelectionChange?.([]);
5003
+ else onSelectionChange?.(remaining);
5004
+ };
5005
+ const closeSummary = () => {
5006
+ setSummary(null);
5007
+ const pending = afterSummary.current;
5008
+ afterSummary.current = null;
5009
+ pending?.();
4895
5010
  };
4896
5011
  return jsxs(Fragment$1, { children: [
4897
5012
  jsx(Button, {
@@ -4921,7 +5036,7 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4921
5036
  jsx(Dialog, {
4922
5037
  open: summary !== null,
4923
5038
  onOpenChange: (open) => {
4924
- if (!open) setSummary(null);
5039
+ if (!open) closeSummary();
4925
5040
  },
4926
5041
  children: jsxs(DialogContent, {
4927
5042
  "data-testid": `${tid.bulkSummary(resource, actionName)}:dialog`,
@@ -4935,14 +5050,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4935
5050
  actionName,
4936
5051
  label: chrome.labelText,
4937
5052
  total: summary.total,
4938
- progress: summary.progress
5053
+ progress: summary.progress,
5054
+ texts: compiled?.flow.texts
4939
5055
  }),
4940
5056
  jsx(DialogFooter, { children: jsx(Button, {
4941
5057
  type: "button",
4942
5058
  variant: "outline",
4943
5059
  "data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
4944
- onClick: () => setSummary(null),
4945
- children: text(t, "flow.close", "Close")
5060
+ onClick: closeSummary,
5061
+ children: t("flow.close", { defaultValue: "Close" })
4946
5062
  }) })
4947
5063
  ]
4948
5064
  })
@@ -4958,10 +5074,12 @@ function orderBulkActions(actions) {
4958
5074
  seq: index
4959
5075
  })).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
4960
5076
  }
4961
- function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope }) {
5077
+ function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
5078
+ const resource = routeResourceKey(resourceInput);
4962
5079
  const app = useApp();
4963
5080
  const tid = useTestIds();
4964
5081
  const t = useOptionalT();
5082
+ const chromeT = useChromeT();
4965
5083
  const def = app.registry.get(resource);
4966
5084
  const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
4967
5085
  const ids = selectedIds ?? records.map((record) => record.id);
@@ -4994,7 +5112,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
4994
5112
  className: "mx-0.5 h-6 w-px bg-border",
4995
5113
  "aria-hidden": "true"
4996
5114
  }),
4997
- bulkActions.map((action) => jsx(BulkFlowButton, {
5115
+ actions !== void 0 ? actions : null,
5116
+ actions === void 0 && bulkActions.map((action) => jsx(BulkFlowButton, {
4998
5117
  resource,
4999
5118
  actionName: action.name,
5000
5119
  ids,
@@ -5003,7 +5122,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5003
5122
  else if (next.length === 0) handleClear();
5004
5123
  }
5005
5124
  }, action.name)),
5006
- jsx(BulkExportButton, {
5125
+ actions === void 0 && jsx(BulkExportButton, {
5007
5126
  resource,
5008
5127
  ids,
5009
5128
  visibleColumns: visibleColumns ?? [],
@@ -5020,7 +5139,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5020
5139
  "aria-hidden": "true"
5021
5140
  }), jsx("span", {
5022
5141
  className: "hidden sm:inline",
5023
- children: t("actions.clearSelection", { defaultValue: "Clear" })
5142
+ children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
5024
5143
  })]
5025
5144
  })
5026
5145
  ]
@@ -5080,12 +5199,36 @@ function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
5080
5199
  }
5081
5200
  //#endregion
5082
5201
  //#region src/list-kit/pieces/list-bulk-bar.tsx
5202
+ function LinkDetachBulkButton(props) {
5203
+ const { selection } = useResourceListContext();
5204
+ const { resource } = useListSurface();
5205
+ const t = useOptionalT();
5206
+ const [busy, setBusy] = useState(false);
5207
+ return jsxs(Button, {
5208
+ type: "button",
5209
+ size: "sm",
5210
+ variant: "ghost",
5211
+ className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
5212
+ disabled: busy,
5213
+ "data-testid": `list-link-detach-bulk:${resource}`,
5214
+ onClick: () => {
5215
+ setBusy(true);
5216
+ props.onDetach(selection.records).finally(() => setBusy(false));
5217
+ },
5218
+ children: [jsx(IconUnlink, {
5219
+ className: "size-4",
5220
+ "aria-hidden": "true"
5221
+ }), t("field.link.detach", { defaultValue: "Detach" })]
5222
+ });
5223
+ }
5083
5224
  function ListBulkBarView() {
5084
5225
  usePieceMount("bulk-bar");
5085
5226
  const { selection, columns, scope } = useResourceListContext();
5086
- const { resource, tableView, readOnly } = useListSurface();
5087
- if (!tableView || readOnly || selection.ids.length === 0) return null;
5227
+ const { resource, tableView, readOnly, linkEditing } = useListSurface();
5228
+ const detach = readOnly ? linkEditing?.detach : void 0;
5229
+ if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
5088
5230
  return jsx(BulkActionToolbar, {
5231
+ ...detach === void 0 ? {} : { actions: jsx(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
5089
5232
  resource,
5090
5233
  selectedRecords: selection.records,
5091
5234
  selectedIds: selection.ids,
@@ -5174,7 +5317,8 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
5174
5317
  }
5175
5318
  //#endregion
5176
5319
  //#region src/list-kit/columns-toggle.tsx
5177
- function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5320
+ function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5321
+ const resource = routeResourceKey(resourceInput);
5178
5322
  const t = useT();
5179
5323
  const columnsCaps = useI18nCaps(resource);
5180
5324
  const app = useApp();
@@ -5281,12 +5425,16 @@ function ListColumnsView() {
5281
5425
  const ListColumns = listPiece("ListColumns", ListColumnsView);
5282
5426
  //#endregion
5283
5427
  //#region src/resource/create-button.tsx
5284
- function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
5428
+ function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
5429
+ const resource = routeResourceKey(resourceInput);
5285
5430
  const t = useOptionalT();
5286
5431
  const Link = useLinkComponent();
5287
5432
  const availability = useAvailability(resource, "create");
5288
5433
  const gate = useEntryGate();
5289
- const text = label ?? t("actions.create", { defaultValue: "Create" });
5434
+ const text = label ?? t("actions.create", {
5435
+ resource,
5436
+ defaultValue: "Create"
5437
+ });
5290
5438
  if (onOpen !== void 0) {
5291
5439
  const answer = gate(resource, "create");
5292
5440
  if (answer.state === "disabled") return jsx(DisabledAction, {
@@ -5357,7 +5505,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
5357
5505
  //#endregion
5358
5506
  //#region src/export/export-button.tsx
5359
5507
  const warnedProviderQueryResources = new Set();
5360
- function ExportButton({ resource, className }) {
5508
+ function ExportButton({ resource: resourceInput, className }) {
5509
+ const resource = routeResourceKey(resourceInput);
5361
5510
  if (!(useOptionalDataRuntime() !== null)) return null;
5362
5511
  return jsx(ExportButtonActive, {
5363
5512
  resource,
@@ -5378,36 +5527,19 @@ function ExportButtonActive({ resource, className }) {
5378
5527
  const tid = useTestIds();
5379
5528
  const t = useOptionalT();
5380
5529
  const [open, setOpen] = React.useState(false);
5381
- const searchContext = React.useMemo(() => ({
5382
- resource,
5383
- def: list.def
5384
- }), [resource, list.def]);
5530
+ const sent = list.query.sent;
5385
5531
  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;
5532
+ if (sent === null) return null;
5533
+ const { filter, sort } = sent;
5395
5534
  return {
5396
5535
  kind: "filter",
5397
5536
  ...filter === void 0 ? {} : { filter },
5398
5537
  ...sort.length > 0 ? { sort } : {}
5399
5538
  };
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
- ]);
5539
+ }, [sent]);
5540
+ const lastTarget = React.useRef(target);
5541
+ if (target !== null) lastTarget.current = target;
5542
+ const dialogTarget = target ?? lastTarget.current;
5411
5543
  React.useEffect(() => {
5412
5544
  if (!available || !capability.allowed) return;
5413
5545
  if (list.providerQuery.value === void 0) return;
@@ -5421,7 +5553,9 @@ function ExportButtonActive({ resource, className }) {
5421
5553
  resource
5422
5554
  ]);
5423
5555
  if (!available || !capability.allowed || list.isForbidden) return null;
5424
- return jsxs(Fragment$1, { children: [jsx(Button, {
5556
+ if (target === null && !open) return null;
5557
+ if (dialogTarget === null) return null;
5558
+ return jsxs(Fragment$1, { children: [target === null ? null : jsx(Button, {
5425
5559
  type: "button",
5426
5560
  variant: "outline",
5427
5561
  size: "icon",
@@ -5440,7 +5574,7 @@ function ExportButtonActive({ resource, className }) {
5440
5574
  open,
5441
5575
  onOpenChange: setOpen,
5442
5576
  resource,
5443
- target,
5577
+ target: dialogTarget,
5444
5578
  visibleColumns: list.columns.visible,
5445
5579
  estimatedCount: list.pagination.total,
5446
5580
  scope: list.scope
@@ -5458,8 +5592,9 @@ function ListExportView() {
5458
5592
  const ListExport = listPiece("ListExport", ListExportView);
5459
5593
  //#endregion
5460
5594
  //#region src/list-kit/list-search-input.tsx
5461
- function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5462
- const t = useT();
5595
+ function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
5596
+ const resource = routeResourceKey(resourceInput);
5597
+ const caps = useI18nCaps(resource);
5463
5598
  const [text, setText] = useState(value);
5464
5599
  const lastPushedRef = useRef(value);
5465
5600
  const timerRef = useRef(null);
@@ -5474,7 +5609,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5474
5609
  }, []);
5475
5610
  return jsx(Input, {
5476
5611
  type: "search",
5477
- placeholder: placeholder ?? t("toolbar.searchBy", { params: { name: label } }),
5612
+ placeholder: placeholder ?? resolveContentSlot(caps, ["list.search"]) ?? "Search",
5478
5613
  value: text,
5479
5614
  "data-testid": `list-search:${resource}`,
5480
5615
  className: "h-8 w-full min-[420px]:w-72",
@@ -5492,24 +5627,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5492
5627
  //#endregion
5493
5628
  //#region src/list-kit/filter-builder/filter-region.tsx
5494
5629
  function FilterRegion({ search } = {}) {
5495
- const { resource, def, filters, pagination, access } = useResourceListContext();
5496
- const t = useT();
5497
- const listCaps = useI18nCaps(resource);
5630
+ const { resource, def, filters, pagination } = useResourceListContext();
5631
+ const chromeT = useChromeT();
5498
5632
  const testIdBase = `list-filter:${resource}`;
5633
+ const caps = useI18nCaps(resource);
5634
+ const placeholder = search === void 0 ? resolveContentSlot(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
5499
5635
  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
5636
  return jsxs("div", {
5507
5637
  className: "flex flex-wrap items-center gap-2",
5508
5638
  children: [
5509
5639
  searchDecl !== void 0 && filters.searchEnabled && jsx(ListSearchInput, {
5510
5640
  resource,
5511
- label: searchLabel,
5512
- placeholder: searchPlaceholder,
5641
+ ...placeholder === void 0 ? {} : { placeholder },
5513
5642
  value: filters.search,
5514
5643
  onCommit: (v) => {
5515
5644
  pagination.setPage(1);
@@ -5521,19 +5650,21 @@ function FilterRegion({ search } = {}) {
5521
5650
  setGroup: filters.setGroup,
5522
5651
  available: filters.available,
5523
5652
  fixed: filters.fixed,
5653
+ presets: filters.presets,
5654
+ setPreset: filters.setPreset,
5524
5655
  pinnedFields: filters.pinnedFields,
5525
5656
  pinnedValue: filters.value,
5526
5657
  setPinnedValue: filters.set,
5527
5658
  setPage: pagination.setPage,
5528
5659
  testIdBase
5529
5660
  }),
5530
- filters.isFiltered && jsx(Button, {
5661
+ filters.canReset && jsx(Button, {
5531
5662
  type: "button",
5532
5663
  variant: "ghost",
5533
5664
  size: "sm",
5534
5665
  "data-testid": `${testIdBase}:clear-all`,
5535
- onClick: filters.clearAll,
5536
- children: t("filters.clearAll")
5666
+ onClick: filters.reset,
5667
+ children: chromeT("filters.clearAll")
5537
5668
  })
5538
5669
  ]
5539
5670
  });
@@ -5663,6 +5794,7 @@ function ViewItem({ view, caps, resource, onClick, testid }) {
5663
5794
  });
5664
5795
  }
5665
5796
  function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
5797
+ const chromeT = useChromeT();
5666
5798
  const canRename = entry.permissions.update && onRename !== void 0;
5667
5799
  const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
5668
5800
  const canDelete = entry.permissions.remove && onDelete !== void 0;
@@ -5708,8 +5840,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
5708
5840
  canDelete && jsx("button", {
5709
5841
  type: "button",
5710
5842
  onClick: onDelete,
5711
- "aria-label": tr("actions.delete", "Delete"),
5712
- title: tr("actions.delete", "Delete"),
5843
+ "aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
5844
+ title: chromeT("actions.delete", { defaultValue: "Delete" }),
5713
5845
  "data-testid": deleteTestId,
5714
5846
  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
5847
  children: jsx(IconTrash, {
@@ -5749,7 +5881,8 @@ function groupByScope(entries) {
5749
5881
  }
5750
5882
  return order.map((id) => byId.get(id));
5751
5883
  }
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" }) {
5884
+ 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" }) {
5885
+ const resource = resourceInput === void 0 ? void 0 : routeResourceKey(resourceInput);
5753
5886
  const t = useOptionalT();
5754
5887
  const caps = useI18nCaps(resource);
5755
5888
  const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
@@ -5963,9 +6096,10 @@ function useOptionalFrameChrome() {
5963
6096
  //#endregion
5964
6097
  //#region src/record/frame-pieces.tsx
5965
6098
  function useFrameId() {
5966
- const { recordId, record } = useRecordFrame();
6099
+ const { resource, recordId, record } = useRecordFrame();
6100
+ const def = useApp().registry.get(resource);
5967
6101
  if (recordId !== void 0) return recordId;
5968
- const id = record?.id;
6102
+ const id = recordKeyOf(def, record);
5969
6103
  return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
5970
6104
  }
5971
6105
  function RecordTitleView() {
@@ -5976,7 +6110,7 @@ function RecordTitleView() {
5976
6110
  const t = useOptionalT();
5977
6111
  const title = useRecordTitle(resource, mode === "create" ? void 0 : id, record);
5978
6112
  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;
6113
+ const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) }) : title;
5980
6114
  if (text === "") return null;
5981
6115
  return jsx("h2", {
5982
6116
  className: "min-w-0 break-words text-xl font-semibold tracking-tight",
@@ -5989,10 +6123,7 @@ function RecordActionsView() {
5989
6123
  const id = useFrameId();
5990
6124
  const holder = useScreenState()?.inline;
5991
6125
  if (mode === "create" || record === void 0 || id === void 0) return null;
5992
- const row = {
5993
- ...policyRecordOf(controller, record),
5994
- id
5995
- };
6126
+ const row = withRecordKeys(policyRecordOf(controller) ?? record, { id });
5996
6127
  holder?.declare(inlineFlowNames(controller.screen, controller.plan));
5997
6128
  return jsxs("div", {
5998
6129
  className: "flex flex-wrap items-center gap-2",
@@ -6000,6 +6131,7 @@ function RecordActionsView() {
6000
6131
  resource,
6001
6132
  record: row,
6002
6133
  surface: mode,
6134
+ sources: controller.sources,
6003
6135
  ...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
6004
6136
  }), mode === "show" ? extraActions : null]
6005
6137
  });
@@ -6029,24 +6161,38 @@ function FormActionsView() {
6029
6161
  const app = useApp();
6030
6162
  const navigate = useRouteNavigate();
6031
6163
  const t = useOptionalT();
6164
+ const chromeT = useChromeT();
6165
+ const caps = useI18nCaps();
6166
+ const labelCaps = useI18nCaps(resource);
6032
6167
  const tid = useTestIds();
6033
6168
  const id = useFrameId();
6169
+ const back = useReturnTarget(resource, mode === "create" ? "create" : "edit", mode === "edit" ? id : void 0);
6170
+ const failedIssues = chrome.serverIssues;
6171
+ const livePlan = controller.plan;
6172
+ const placed = useMemo(() => new Set(livePlan?.placedPaths ?? []), [livePlan]);
6173
+ const alertIssues = useMemo(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : routeServerIssues({ issues: failedIssues }, (segment) => segment, (field, path) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active" && deepestPlacedPath(path, placed) !== void 0)).form, [
6174
+ failedIssues,
6175
+ livePlan,
6176
+ placed
6177
+ ]);
6178
+ const fieldDefs = app.registry.get(resource)?.fields;
6034
6179
  if (mode === "show") return null;
6035
- const busy = controller.isSubmitting;
6180
+ const busy = controller.isSubmitting || chrome.created === true;
6036
6181
  const cancel = () => {
6037
6182
  if (chrome.onCancel !== void 0) chrome.onCancel();
6038
- else navigate(mode === "edit" && id !== void 0 ? recordReturnTarget(app.registry, resource, id) : listTarget(resource));
6183
+ else navigate(back, { origin: "inherit" });
6039
6184
  };
6040
6185
  const plan = controller.plan;
6041
6186
  const undrawn = (field) => plan === null || plan.fields[field] === void 0 || plan.fields[field] === "inactive";
6042
- const recordLevel = (issue) => issue.field === "_form" || undrawn(issue.field);
6187
+ const recordLevel = (issue) => issue.field === "_form" || undrawn(pathHead(issue.field)) || !placed.has(issue.field);
6043
6188
  const undrawnBlockers = (controller.checkOutcome?.blockers ?? []).flatMap((issue) => {
6044
6189
  const field = typeof issue.path?.[0] === "string" ? issue.path[0] : "_form";
6045
- if (field !== "_form" && !undrawn(field)) return [];
6190
+ if (field !== "_form" && !undrawn(field) && deepestPlacedPath(issue.path, placed) !== void 0) return [];
6046
6191
  if (controller.errors.some((own) => own.code === issue.code && own.field === field)) return [];
6047
6192
  return [{
6048
6193
  ...issue,
6049
- field
6194
+ field,
6195
+ path: field === "_form" ? [] : issue.path.slice(1)
6050
6196
  }];
6051
6197
  });
6052
6198
  const formErrors = firstPreferred([...controller.errors.filter(recordLevel), ...undrawnBlockers]);
@@ -6054,21 +6200,44 @@ function FormActionsView() {
6054
6200
  const text = (issue) => resolveValidationMessage({
6055
6201
  code: issue.code ?? "",
6056
6202
  message: issue.message,
6057
- ...issue.params === void 0 ? {} : { params: issue.params }
6058
- }, { t });
6203
+ ...issue.check === void 0 ? {} : { check: issue.check }
6204
+ }, {
6205
+ has: caps.has,
6206
+ t: caps.t
6207
+ });
6059
6208
  const retryable = [];
6060
6209
  for (const issue of controller.checkOutcome?.blockers ?? []) {
6061
- const checkId = issue.params?.checkId;
6062
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
6210
+ const checkId = issue.check?.id;
6211
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
6063
6212
  if (!retryable.includes(checkId)) retryable.push(checkId);
6064
6213
  }
6065
- const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t);
6214
+ const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
6215
+ const serverIssues = bySeverity(alertIssues).flatMap((issue) => {
6216
+ const head = issue.path[0];
6217
+ const name = typeof head === "string" ? head : void 0;
6218
+ const declared = name === void 0 ? void 0 : fieldDefs?.[name];
6219
+ const message = resolveValidationMessage({
6220
+ code: issue.code,
6221
+ message: issue.message,
6222
+ ...issue.check === void 0 ? {} : { check: issue.check }
6223
+ }, {
6224
+ has: caps.has,
6225
+ t: caps.t,
6226
+ ...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
6227
+ });
6228
+ if (issue.path.length === 0 && message === failure) return [];
6229
+ return [{
6230
+ label: declared === void 0 || name === void 0 ? void 0 : resolveFieldLabel(labelCaps, name, declaredText("field", declared, "label")),
6231
+ message,
6232
+ severity: issue.severity
6233
+ }];
6234
+ });
6066
6235
  const steps = controller.steps;
6067
6236
  const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
6068
6237
  return jsxs("div", {
6069
6238
  className: "flex flex-col gap-3",
6070
6239
  children: [
6071
- failure === void 0 && formErrors.length === 0 ? null : jsxs("div", {
6240
+ failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : jsxs("div", {
6072
6241
  role: "alert",
6073
6242
  className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
6074
6243
  children: [
@@ -6078,10 +6247,22 @@ function FormActionsView() {
6078
6247
  }),
6079
6248
  jsxs("div", {
6080
6249
  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)))]
6250
+ children: [
6251
+ failure === void 0 ? null : jsx("p", {
6252
+ className: "font-medium",
6253
+ children: failure
6254
+ }),
6255
+ serverIssues.map((issue, index) => jsxs("p", {
6256
+ "data-testid": `form-server-issue:${resource}:${mode}:${index}`,
6257
+ "data-buro-severity": issue.severity,
6258
+ className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
6259
+ children: [issue.label === void 0 ? null : jsxs("span", {
6260
+ className: "font-medium",
6261
+ children: [issue.label, ": "]
6262
+ }), issue.message]
6263
+ }, index)),
6264
+ formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))
6265
+ ]
6085
6266
  }),
6086
6267
  failure !== void 0 && steps !== null ? jsx(Button, {
6087
6268
  type: "button",
@@ -6090,7 +6271,7 @@ function FormActionsView() {
6090
6271
  className: "ml-auto",
6091
6272
  onMouseDown: (event) => event.preventDefault(),
6092
6273
  onClick: () => void chrome.submit(),
6093
- children: t("actions.retry", { defaultValue: "Retry" })
6274
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6094
6275
  }) : null
6095
6276
  ]
6096
6277
  }),
@@ -6102,7 +6283,7 @@ function FormActionsView() {
6102
6283
  size: "sm",
6103
6284
  "data-testid": `form-check-retry:${checkId}`,
6104
6285
  onClick: () => void controller.revalidate(),
6105
- children: t("actions.retry", { defaultValue: "Retry" })
6286
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6106
6287
  }, checkId))
6107
6288
  }),
6108
6289
  formWarnings.length === 0 ? null : jsxs("div", {
@@ -6128,7 +6309,7 @@ function FormActionsView() {
6128
6309
  children: [jsx(IconLoader2, {
6129
6310
  className: "h-3.5 w-3.5 animate-spin",
6130
6311
  "aria-hidden": "true"
6131
- }), jsx("span", { children: t("actions.validating", { defaultValue: "Validating…" }) })]
6312
+ }), jsx("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
6132
6313
  }) : null,
6133
6314
  jsx(Button, {
6134
6315
  type: "button",
@@ -6136,7 +6317,7 @@ function FormActionsView() {
6136
6317
  onClick: cancel,
6137
6318
  onMouseDown: (event) => event.preventDefault(),
6138
6319
  "data-testid": tid.formCancel(resource, mode),
6139
- children: t("actions.cancel", { defaultValue: "Cancel" })
6320
+ children: chromeT("actions.cancel", { defaultValue: "Cancel" })
6140
6321
  }),
6141
6322
  midSteps ? null : jsxs(Button, {
6142
6323
  type: "submit",
@@ -6153,37 +6334,71 @@ function FormActionsView() {
6153
6334
  children: [busy ? jsx(IconLoader2, {
6154
6335
  className: "mr-2 h-4 w-4 animate-spin",
6155
6336
  "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" }))]
6337
+ }) : 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
6338
  })
6158
6339
  ]
6159
6340
  })
6160
6341
  ]
6161
6342
  });
6162
6343
  }
6344
+ const NO_ALERT_ISSUES = Object.freeze([]);
6345
+ const SEVERITY_RANK = {
6346
+ error: 0,
6347
+ warning: 1,
6348
+ info: 2
6349
+ };
6350
+ function bySeverity(issues) {
6351
+ return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
6352
+ }
6163
6353
  function firstPreferred(issues) {
6164
- const first = issues.find((issue) => issue.params?.checkId === void 0) ?? issues[0];
6354
+ const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
6165
6355
  return first === void 0 ? [] : [first];
6166
6356
  }
6167
6357
  function issueKey(issue) {
6168
6358
  return JSON.stringify([
6169
6359
  issue.code ?? "",
6170
6360
  issue.message.key,
6171
- issue.params ?? null
6361
+ issue.check?.id ?? null
6172
6362
  ]);
6173
6363
  }
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" });
6364
+ function failureText(error, settled, t, caps) {
6365
+ const texts = technicalTexts(error).filter((text) => text.length <= 1024);
6366
+ const strings = friendlyStringsFrom(t);
6367
+ const friendly = texts.map((text) => toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? toFriendlyError(texts[0], void 0, strings);
6368
+ const applied = settled ?? ownDataProperty(error, "applied");
6369
+ if (applied === "partial" || applied === "unknown") {
6370
+ const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
6371
+ 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.";
6372
+ const lead = texts.some(isNetworkFailure) ? strings.offline : "";
6373
+ return lead === "" ? sentence : `${lead} ${sentence}`;
6374
+ }
6375
+ const code = ownDataProperty(error, "code");
6376
+ const kind = ownDataProperty(error, "kind");
6377
+ const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(RECORD_KIND_KEYS, kind);
6378
+ const message = ownDataProperty(error, "message");
6379
+ if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? specificErrorText({
6380
+ code,
6381
+ ...isMessageReference(message) ? { message } : {}
6382
+ }, caps, { record: true }) : void 0) ?? friendly.title;
6383
+ if (typeof code !== "string") return strings.title;
6384
+ const reference = {
6385
+ code,
6386
+ ...isMessageReference(message) ? { message } : {}
6387
+ };
6388
+ return specificErrorText(reference, caps, { record: true }) ?? (typeof kind === "string" ? kindErrorText(kind, caps, { record: true }) : void 0) ?? strings.title;
6177
6389
  }
6178
- function safeMessage(error) {
6390
+ function ownDataProperty(error, name) {
6179
6391
  if (error === null || typeof error !== "object") return void 0;
6180
6392
  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;
6393
+ const descriptor = Object.getOwnPropertyDescriptor(error, name);
6394
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
6183
6395
  } catch {
6184
6396
  return;
6185
6397
  }
6186
6398
  }
6399
+ function isMessageReference(value) {
6400
+ return value !== null && typeof value === "object" && typeof value.key === "string";
6401
+ }
6187
6402
  function inlineFlowNames(screen, plan) {
6188
6403
  if (screen === null) return [];
6189
6404
  const names = [];
@@ -6456,31 +6671,35 @@ function PlannedFrame(props) {
6456
6671
  })
6457
6672
  }, name);
6458
6673
  const { PlacementScope } = page;
6674
+ const claimedRoute = useContext(RouteSourceContext);
6459
6675
  const itemNode = page.plan.slots.item?.[0];
6460
6676
  const item = useMemo(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
6461
6677
  return jsx(RouteSourceContext.Provider, {
6462
6678
  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
6679
+ children: jsx(ActionRouteContext.Provider, {
6680
+ value: claimedRoute,
6681
+ children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
6682
+ resource,
6683
+ mode: "show",
6684
+ compiled,
6685
+ page,
6686
+ children: jsx(ListItemContext.Provider, {
6687
+ value: item,
6688
+ children: jsx(FrameView, {
6689
+ resource,
6690
+ compiled,
6691
+ slots
6692
+ })
6474
6693
  })
6475
- })
6476
- }) })
6694
+ }) })
6695
+ })
6477
6696
  });
6478
6697
  }
6479
6698
  function ListFrameHost({ resource }) {
6480
6699
  const compiled = useCompiledListScreen(resource);
6481
6700
  const hasDataRuntime = useOptionalDataRuntime() !== null;
6482
6701
  const { def } = useResourceListContext();
6483
- const rowsView = (def?.views?.list)?.view ?? "table";
6702
+ const rowsView = def?.views?.list?.initial?.view ?? "table";
6484
6703
  if (compiled === null) return jsx(Arrangement, {});
6485
6704
  if (compiled.nodes.length === 0 && rowsView === "table") {
6486
6705
  const empty = {};
@@ -6499,8 +6718,12 @@ function ListFrameHost({ resource }) {
6499
6718
  }
6500
6719
  //#endregion
6501
6720
  //#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 }));
6721
+ function declaresDisplay(field) {
6722
+ return field !== void 0 && resolveSurfaceUi(field, "column").use !== void 0;
6723
+ }
6724
+ const NO_INITIAL_FILTERS = Object.freeze({});
6725
+ const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BvkD5OcC.js")).RelatedCards }));
6726
+ const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-DCrwN9oO.js")).RelatedTimeline }));
6504
6727
  function discriminatorOptionLabel(field, value) {
6505
6728
  const text = fieldOptionText(field, value);
6506
6729
  if (text !== void 0) return text;
@@ -6508,10 +6731,17 @@ function discriminatorOptionLabel(field, value) {
6508
6731
  const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
6509
6732
  return typeof label === "string" ? label : value;
6510
6733
  }
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 }) {
6734
+ function declaredInitialSort(def) {
6735
+ return def?.views?.list?.initial?.sort;
6736
+ }
6737
+ function declaredRouteView(def) {
6738
+ return def?.views?.list?.initial?.view;
6739
+ }
6740
+ 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
6741
  const route = variant === "route";
6513
6742
  const t = useT();
6514
6743
  const optionalT = useOptionalT();
6744
+ const chromeT = useChromeT();
6515
6745
  const tid = useTestIds();
6516
6746
  const listCaps = useI18nCaps(resource);
6517
6747
  const RouteFiltersSlot = useListSlot("filters", resource);
@@ -6526,17 +6756,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6526
6756
  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
6757
  }, [removedBody, resource]);
6528
6758
  const searchDecl = useMemo(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
6529
- const list = useResourceList(resource, route ? { meta } : {
6759
+ const appRegistry = useApp().registry;
6760
+ const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
6761
+ const timeline = effectiveView === "timeline";
6762
+ const initialKey = JSON.stringify(initial ?? null);
6763
+ const placementInitial = useMemo(() => route ? void 0 : {
6764
+ ...initial,
6765
+ filters: initial?.filters ?? NO_INITIAL_FILTERS
6766
+ }, [route, initialKey]);
6767
+ const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
6768
+ const columnNames = useMemo(() => columnEntries?.map(columnName), [columnEntries]);
6769
+ const list = useResourceList(resource, route ? {
6770
+ meta,
6771
+ ...timeline ? { timeline } : {}
6772
+ } : {
6773
+ ...timeline ? { timeline } : {},
6530
6774
  ...state === void 0 ? {} : { state },
6531
6775
  ...fixedFilters === void 0 ? {} : { fixedFilters },
6532
- ...pageSize === void 0 ? {} : { pageSize },
6776
+ ...placementInitial === void 0 ? {} : { initial: placementInitial },
6533
6777
  ...searchDecl === void 0 ? {} : { search: searchDecl },
6534
- ...columnNames === void 0 ? {} : { columns: columnNames },
6778
+ ...columnEntries === void 0 ? {} : { columns: columnEntries },
6535
6779
  ...scope === void 0 ? {} : { scope },
6536
6780
  ...countPolicy === void 0 ? {} : { countPolicy }
6537
6781
  });
6538
6782
  const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
6539
6783
  policyRowsOf(list, list.rows);
6784
+ const selectionRef = useRef(selection);
6785
+ selectionRef.current = selection;
6786
+ const onDetach = linkEditing?.detach?.onDetach;
6787
+ const surfaceLinkEditing = useMemo(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
6788
+ const left = await onDetach(detached);
6789
+ if (left.length > 0) {
6790
+ const gone = new Set(left);
6791
+ const current = selectionRef.current;
6792
+ if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
6793
+ }
6794
+ return left;
6795
+ } } }, [onDetach]);
6796
+ const onAttach = linkEditing?.attach?.onAttach;
6797
+ const attachBusy = linkEditing?.attach?.busy === true;
6540
6798
  const hasDataRuntime = useOptionalDataRuntime() !== null;
6541
6799
  const scopeLabel = (scope) => scope.id === LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
6542
6800
  const createAvailable = useAvailability(resource, "create").state === "available";
@@ -6584,7 +6842,6 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6584
6842
  resource,
6585
6843
  routing.rowHref
6586
6844
  ]);
6587
- const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
6588
6845
  const tableView = effectiveView === "table";
6589
6846
  const rowsViewFor = useMemo(() => tableView ? void 0 : (planned) => ({
6590
6847
  skeleton: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
@@ -6596,11 +6853,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6596
6853
  item: route ? planned : item,
6597
6854
  columns: route ? columns.visible : columnNames,
6598
6855
  rowQuick,
6599
- rowHref
6856
+ rowHref,
6857
+ ...empty === void 0 ? {} : { empty }
6600
6858
  };
6601
6859
  return jsx(Suspense, {
6602
6860
  fallback: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
6603
- children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, { ...common })
6861
+ children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
6862
+ ...common,
6863
+ axis: timelineAxis
6864
+ })
6604
6865
  });
6605
6866
  }
6606
6867
  }), [
@@ -6613,7 +6874,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6613
6874
  columns.visible,
6614
6875
  columnNames,
6615
6876
  rowQuick,
6616
- rowHref
6877
+ rowHref,
6878
+ empty,
6879
+ timelineAxis
6617
6880
  ]);
6618
6881
  const pageTitle = resolveResourceLabel(listCaps, resource, "plural", def);
6619
6882
  const [saveOpen, setSaveOpen] = useState(false);
@@ -6695,6 +6958,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6695
6958
  toast.error(t("toolbar.viewsForkFailed"));
6696
6959
  }
6697
6960
  };
6961
+ const columnNamesDisplay = (col, field) => {
6962
+ const resolved = list.columns.ui[col];
6963
+ return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
6964
+ };
6698
6965
  const renderCellLadder = (col, value, row) => {
6699
6966
  if (def?.types && col === def.types.by) {
6700
6967
  const raw = typeof value === "string" ? value : "";
@@ -6713,13 +6980,14 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6713
6980
  children: label
6714
6981
  });
6715
6982
  }
6983
+ const custom = (renderCell ?? CellSlot)?.(col, value, row);
6984
+ if (custom !== void 0) return custom;
6716
6985
  const fieldDef = def?.fields[col];
6717
- const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
6718
- if (enumName && typeof value === "string") return jsx(EnumBadge, {
6986
+ const enumName = enumBindingKey(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
6987
+ if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return jsx(EnumBadge, {
6719
6988
  enumName,
6720
6989
  value
6721
6990
  });
6722
- return (renderCell ?? CellSlot)?.(col, value, row);
6723
6991
  };
6724
6992
  const surface = {
6725
6993
  resource,
@@ -6747,10 +7015,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6747
7015
  }
6748
7016
  },
6749
7017
  rowsViewFor,
6750
- emptyState,
7018
+ emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? jsx("p", {
7019
+ "data-buro-region": "empty",
7020
+ className: "text-sm text-muted-foreground",
7021
+ children: empty
7022
+ }) : emptyState,
6751
7023
  renderCell: renderCellLadder,
6752
7024
  readOnly: readOnly === true,
6753
- exportable: exportable !== false
7025
+ exportable: exportable !== false,
7026
+ ...layoutId === void 0 ? {} : { layoutId },
7027
+ ...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
6754
7028
  };
6755
7029
  return jsx(ResourceListProvider, {
6756
7030
  value: list,
@@ -6763,22 +7037,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6763
7037
  resource,
6764
7038
  mode: "list",
6765
7039
  title: pageTitle
6766
- }) : title === void 0 && onCreate === void 0 ? null : jsxs("div", {
7040
+ }) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : jsxs("div", {
6767
7041
  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
- })]
7042
+ children: [
7043
+ title === void 0 ? jsx("span", {}) : jsx("h3", {
7044
+ className: "text-sm font-semibold",
7045
+ children: title
7046
+ }),
7047
+ onAttach === void 0 ? null : jsxs(Button, {
7048
+ type: "button",
7049
+ size: "sm",
7050
+ variant: "outline",
7051
+ "data-testid": `list-link-attach:${resource}`,
7052
+ onClick: onAttach,
7053
+ disabled: attachBusy,
7054
+ "aria-busy": attachBusy,
7055
+ children: [attachBusy ? jsx(IconLoader2, {
7056
+ className: "mr-1 h-4 w-4 animate-spin",
7057
+ "aria-hidden": "true"
7058
+ }) : jsx(IconLink, {
7059
+ className: "mr-1 h-4 w-4",
7060
+ "aria-hidden": "true"
7061
+ }), optionalT("field.link.attach", { defaultValue: "Attach" })]
7062
+ }),
7063
+ onCreate === void 0 ? null : jsxs(Button, {
7064
+ type: "button",
7065
+ size: "sm",
7066
+ variant: "outline",
7067
+ "data-testid": `list-create:${resource}`,
7068
+ onClick: onCreate,
7069
+ children: [jsx(IconPlus, {
7070
+ className: "mr-1 h-4 w-4",
7071
+ "aria-hidden": "true"
7072
+ }), optionalT("actions.create", {
7073
+ resource,
7074
+ defaultValue: "Create"
7075
+ })]
7076
+ })
7077
+ ]
6782
7078
  }),
6783
7079
  hasDataRuntime && jsx(ExportRunWatcherMount, {}),
6784
7080
  droppedPredicates > 0 && jsx("p", {
@@ -6827,13 +7123,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6827
7123
  variant: "outline",
6828
7124
  onClick: () => setSaveOpen(false),
6829
7125
  "data-testid": `list-saved-view-save-cancel:${resource}`,
6830
- children: t("actions.cancel")
7126
+ children: chromeT("actions.cancel")
6831
7127
  }), jsx(Button, {
6832
7128
  type: "button",
6833
7129
  onClick: handleSaveConfirm,
6834
7130
  disabled: saveLabel.trim().length === 0,
6835
7131
  "data-testid": `list-saved-view-save-confirm:${resource}`,
6836
- children: t("actions.save")
7132
+ children: chromeT("actions.save")
6837
7133
  })] })
6838
7134
  ]
6839
7135
  })
@@ -6870,7 +7166,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6870
7166
  variant: "outline",
6871
7167
  onClick: () => handleDeleteUserView(entry.id),
6872
7168
  "data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
6873
- children: t("actions.delete")
7169
+ children: chromeT("actions.delete")
6874
7170
  }) : jsx("span", {
6875
7171
  className: "shrink-0 text-xs text-muted-foreground",
6876
7172
  children: t("toolbar.viewsReadOnly")
@@ -6882,7 +7178,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6882
7178
  variant: "outline",
6883
7179
  onClick: () => setManageOpen(false),
6884
7180
  "data-testid": `list-saved-view-manage-close:${resource}`,
6885
- children: t("actions.close")
7181
+ children: chromeT("actions.close")
6886
7182
  }) })
6887
7183
  ]
6888
7184
  })
@@ -6915,13 +7211,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6915
7211
  setRenameTarget(null);
6916
7212
  },
6917
7213
  "data-testid": `list-saved-view-rename-cancel:${resource}`,
6918
- children: t("actions.cancel")
7214
+ children: chromeT("actions.cancel")
6919
7215
  }), jsx(Button, {
6920
7216
  type: "button",
6921
7217
  onClick: handleRenameConfirm,
6922
7218
  disabled: renameLabel.trim().length === 0,
6923
7219
  "data-testid": `list-saved-view-rename-confirm:${resource}`,
6924
- children: t("actions.save")
7220
+ children: chromeT("actions.save")
6925
7221
  })] })
6926
7222
  ]
6927
7223
  })
@@ -6956,7 +7252,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6956
7252
  setForkTarget(null);
6957
7253
  },
6958
7254
  "data-testid": `list-saved-view-fork-cancel:${resource}`,
6959
- children: t("actions.cancel")
7255
+ children: chromeT("actions.cancel")
6960
7256
  }), jsx(Button, {
6961
7257
  type: "button",
6962
7258
  onClick: handleForkConfirm,
@@ -6979,6 +7275,582 @@ function ExportRunWatcherMount() {
6979
7275
  return null;
6980
7276
  }
6981
7277
  //#endregion
7278
+ //#region src/fields/use-relation-picker.tsx
7279
+ const noop = () => void 0;
7280
+ function safeKey(input) {
7281
+ if (input === void 0 || input === null) return void 0;
7282
+ try {
7283
+ return resourceKey(input);
7284
+ } catch {
7285
+ return;
7286
+ }
7287
+ }
7288
+ function sessionIdentity(runtime) {
7289
+ const state = runtime.dataSession.getState();
7290
+ const snapshot = state.status === "ready" ? state.snapshot : state.target;
7291
+ const selection = snapshot.scope?.selection;
7292
+ const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
7293
+ let scopeKey;
7294
+ try {
7295
+ scopeKey = JSON.stringify(partition) ?? "";
7296
+ } catch {
7297
+ scopeKey = String(snapshot.scopeKey);
7298
+ }
7299
+ return {
7300
+ sessionKey: snapshot.sessionKey,
7301
+ scopeKey
7302
+ };
7303
+ }
7304
+ function PickerHost(props) {
7305
+ const { register } = props;
7306
+ React.useEffect(() => register(), [register]);
7307
+ return jsx(Fragment$1, { children: props.children });
7308
+ }
7309
+ function useRelationPicker(options) {
7310
+ const app = useApp();
7311
+ const runtime = useDataRuntime();
7312
+ const frame = useOptionalRecordFrame();
7313
+ const frames = useRecordScopeFrames();
7314
+ const multiple = options.multiple === true;
7315
+ const fieldName = options.field;
7316
+ let to;
7317
+ let declaredBy;
7318
+ let declaredCandidates;
7319
+ if (fieldName !== void 0) {
7320
+ const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
7321
+ to = safeKey(meta?.to);
7322
+ declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
7323
+ declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
7324
+ } else to = safeKey(options.to);
7325
+ const target = to ?? "";
7326
+ const by = options.valueField ?? declaredBy;
7327
+ const scope = options.scope ?? "context";
7328
+ const selection = useCandidateSelection({
7329
+ to,
7330
+ candidates: options.candidates ?? declaredCandidates,
7331
+ sources: frame?.controller.sources,
7332
+ resource: frame?.resource ?? "",
7333
+ fieldName: fieldName ?? target,
7334
+ fixedFilters: options.fixedFilters
7335
+ });
7336
+ const held = selection.gate !== void 0;
7337
+ const pendingRef = React.useRef(null);
7338
+ const [active, setActive] = React.useState(null);
7339
+ const activeRef = React.useRef(null);
7340
+ activeRef.current = active;
7341
+ const rowsRef = React.useRef(new Map());
7342
+ const createdRowRef = React.useRef(void 0);
7343
+ const alive = React.useRef(true);
7344
+ const mounts = React.useRef(0);
7345
+ const sequence = React.useRef(0);
7346
+ const latest = React.useRef({
7347
+ multiple,
7348
+ select: options.select,
7349
+ idField: "id",
7350
+ valueField: "id",
7351
+ available: false,
7352
+ frames
7353
+ });
7354
+ const project = React.useCallback((row) => {
7355
+ const select = latest.current.select;
7356
+ if (select === void 0) return { ...row };
7357
+ const out = {};
7358
+ const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
7359
+ if (key !== void 0) out.id = key;
7360
+ const { valueField } = latest.current;
7361
+ if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
7362
+ for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
7363
+ return out;
7364
+ }, []);
7365
+ const settle = React.useCallback((result) => {
7366
+ const pending = pendingRef.current;
7367
+ if (pending === null) return;
7368
+ pendingRef.current = null;
7369
+ pending.detach();
7370
+ if (alive.current) setActive(null);
7371
+ pending.resolve(result);
7372
+ }, []);
7373
+ const toggle = React.useCallback((key, row) => {
7374
+ setActive((current) => {
7375
+ if (current === null) return current;
7376
+ const has = current.selected.some((k) => String(k) === String(key));
7377
+ if (has) rowsRef.current.delete(String(key));
7378
+ else if (row !== void 0) rowsRef.current.set(String(key), row);
7379
+ const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
7380
+ const next = {
7381
+ ...current,
7382
+ selected
7383
+ };
7384
+ activeRef.current = next;
7385
+ return next;
7386
+ });
7387
+ }, []);
7388
+ const onValue = React.useCallback((value) => {
7389
+ const row = createdRowRef.current;
7390
+ createdRowRef.current = void 0;
7391
+ if (latest.current.multiple) {
7392
+ const current = activeRef.current;
7393
+ if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
7394
+ return;
7395
+ }
7396
+ settle({
7397
+ kind: "selected",
7398
+ keys: [value],
7399
+ rows: row === void 0 ? [] : [project(row)]
7400
+ });
7401
+ }, [
7402
+ project,
7403
+ settle,
7404
+ toggle
7405
+ ]);
7406
+ const wiring = useRelationSelect({
7407
+ to: target,
7408
+ by,
7409
+ fixedFilters: selection.fixedFilters,
7410
+ scope,
7411
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7412
+ held,
7413
+ onValue
7414
+ });
7415
+ latest.current = {
7416
+ multiple,
7417
+ select: options.select,
7418
+ idField: wiring.idField,
7419
+ valueField: wiring.valueField,
7420
+ available: to !== void 0 && wiring.targetDef !== void 0 && !held,
7421
+ frames
7422
+ };
7423
+ const { create: shared } = wiring;
7424
+ const create = React.useMemo(() => ({
7425
+ ...shared,
7426
+ start: (openCreate) => shared.start(() => {
7427
+ const opened = openCreate();
7428
+ opened.then((row) => {
7429
+ createdRowRef.current = row === null ? void 0 : { ...row };
7430
+ }, noop);
7431
+ return opened;
7432
+ })
7433
+ }), [shared]);
7434
+ React.useEffect(() => runtime.dataSession.subscribe(() => {
7435
+ const pending = pendingRef.current;
7436
+ if (pending === null) return;
7437
+ const now = sessionIdentity(runtime);
7438
+ if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
7439
+ kind: "cancelled",
7440
+ reason: "session-changed"
7441
+ });
7442
+ }), [runtime, settle]);
7443
+ React.useEffect(() => {
7444
+ alive.current = true;
7445
+ return () => {
7446
+ alive.current = false;
7447
+ settle({
7448
+ kind: "cancelled",
7449
+ reason: "unmounted"
7450
+ });
7451
+ };
7452
+ }, [settle]);
7453
+ React.useEffect(() => {
7454
+ if (held) settle({
7455
+ kind: "cancelled",
7456
+ reason: "unavailable"
7457
+ });
7458
+ }, [held, settle]);
7459
+ const register = React.useCallback(() => {
7460
+ mounts.current += 1;
7461
+ return () => {
7462
+ mounts.current -= 1;
7463
+ if (mounts.current === 0) settle({
7464
+ kind: "cancelled",
7465
+ reason: "unmounted"
7466
+ });
7467
+ };
7468
+ }, [settle]);
7469
+ const open = React.useCallback((opts = {}) => new Promise((resolve) => {
7470
+ settle({
7471
+ kind: "cancelled",
7472
+ reason: "superseded"
7473
+ });
7474
+ if (mounts.current === 0) {
7475
+ 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.");
7476
+ resolve({
7477
+ kind: "cancelled",
7478
+ reason: "not-mounted"
7479
+ });
7480
+ return;
7481
+ }
7482
+ const { signal } = opts;
7483
+ if (signal?.aborted === true) {
7484
+ resolve({
7485
+ kind: "cancelled",
7486
+ reason: "aborted"
7487
+ });
7488
+ return;
7489
+ }
7490
+ if (!latest.current.available) {
7491
+ resolve({
7492
+ kind: "cancelled",
7493
+ reason: "unavailable"
7494
+ });
7495
+ return;
7496
+ }
7497
+ sequence.current += 1;
7498
+ const id = sequence.current;
7499
+ const onAbort = () => {
7500
+ if (pendingRef.current?.id === id) settle({
7501
+ kind: "cancelled",
7502
+ reason: "aborted"
7503
+ });
7504
+ };
7505
+ signal?.addEventListener("abort", onAbort, { once: true });
7506
+ pendingRef.current = {
7507
+ id,
7508
+ ...sessionIdentity(runtime),
7509
+ resolve,
7510
+ detach: () => signal?.removeEventListener("abort", onAbort)
7511
+ };
7512
+ rowsRef.current = new Map();
7513
+ createdRowRef.current = void 0;
7514
+ const next = {
7515
+ id,
7516
+ frames: latest.current.frames,
7517
+ selected: opts.selected ?? []
7518
+ };
7519
+ activeRef.current = next;
7520
+ setActive(next);
7521
+ }), [runtime, settle]);
7522
+ const onSelectRow = React.useCallback((row) => {
7523
+ const field = wiring.valueField;
7524
+ const value = field === "id" ? row.id : row[field];
7525
+ if (typeof value !== "string" && typeof value !== "number") return;
7526
+ settle({
7527
+ kind: "selected",
7528
+ keys: [value],
7529
+ rows: [project(row)]
7530
+ });
7531
+ }, [
7532
+ wiring.valueField,
7533
+ settle,
7534
+ project
7535
+ ]);
7536
+ const onDone = React.useCallback(() => {
7537
+ const keys = activeRef.current?.selected ?? [];
7538
+ const rows = keys.flatMap((key) => {
7539
+ const row = rowsRef.current.get(String(key));
7540
+ return row === void 0 ? [] : [project(row)];
7541
+ });
7542
+ settle({
7543
+ kind: "selected",
7544
+ keys,
7545
+ rows
7546
+ });
7547
+ }, [settle, project]);
7548
+ const onOpenChange = React.useCallback((next) => {
7549
+ if (!next) settle({
7550
+ kind: "cancelled",
7551
+ reason: "dismissed"
7552
+ });
7553
+ }, [settle]);
7554
+ return {
7555
+ open,
7556
+ element: jsx(PickerHost, {
7557
+ register,
7558
+ children: active === null || held ? null : jsxs(RecordScopeChain, {
7559
+ frames: active.frames,
7560
+ children: [jsx(LazyRelationPickerDialog, {
7561
+ to: target,
7562
+ open: true,
7563
+ onOpenChange,
7564
+ onSelect: noop,
7565
+ onSelectRow,
7566
+ valueField: wiring.valueField,
7567
+ ...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
7568
+ scope,
7569
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7570
+ ...options.create === void 0 ? {} : {
7571
+ create,
7572
+ ...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
7573
+ },
7574
+ fieldName: fieldName ?? target,
7575
+ ...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
7576
+ ...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
7577
+ ...multiple ? {
7578
+ multiple: true,
7579
+ selected: active.selected,
7580
+ onToggle: (value, row) => toggle(value, row),
7581
+ onDone
7582
+ } : {}
7583
+ }), wiring.reader]
7584
+ }, active.id)
7585
+ })
7586
+ };
7587
+ }
7588
+ //#endregion
7589
+ //#region src/blocks/link-write-summary.tsx
7590
+ function LinkWriteSummary(props) {
7591
+ const { report, titles, onClose } = props;
7592
+ const testid = props.testid ?? "link-write-summary";
7593
+ const t = useOptionalT();
7594
+ const caps = useI18nCaps();
7595
+ const chromeT = useChromeT();
7596
+ const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
7597
+ return jsx(Dialog, {
7598
+ open: true,
7599
+ onOpenChange: (open) => {
7600
+ if (!open) onClose();
7601
+ },
7602
+ children: jsxs(DialogContent, {
7603
+ "data-testid": testid,
7604
+ className: "flex max-h-[85vh] flex-col overflow-hidden",
7605
+ children: [
7606
+ jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), jsx(DialogDescription, {
7607
+ className: "sr-only",
7608
+ children: heading
7609
+ })] }),
7610
+ jsx("ul", {
7611
+ className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
7612
+ children: report.failed.map(({ id, error }) => jsxs("li", {
7613
+ "data-testid": `${testid}:item:${id}`,
7614
+ className: "text-sm",
7615
+ children: [jsx("span", {
7616
+ className: "font-medium",
7617
+ children: titles[id] ?? id
7618
+ }), jsx("span", {
7619
+ className: "text-destructive",
7620
+ children: `: ${resolveErrorText(error, caps)}`
7621
+ })]
7622
+ }, id))
7623
+ }),
7624
+ jsx(DialogFooter, { children: jsx(Button, {
7625
+ type: "button",
7626
+ onClick: onClose,
7627
+ "data-testid": `${testid}:close`,
7628
+ children: chromeT("actions.close", { defaultValue: "Close" })
7629
+ }) })
7630
+ ]
7631
+ })
7632
+ });
7633
+ }
7634
+ //#endregion
7635
+ //#region src/blocks/link-attach.tsx
7636
+ const NO_ISSUES = Object.freeze([]);
7637
+ function useLinkPanelWrites(options) {
7638
+ const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
7639
+ const app = useApp();
7640
+ const runtime = useDataRuntime();
7641
+ const t = useOptionalT();
7642
+ const rights = useLinkRights(owner, link, {
7643
+ surface: "panel",
7644
+ record,
7645
+ ...placement === void 0 ? {} : { placement }
7646
+ });
7647
+ const writer = useLinkWriter(owner, link);
7648
+ const linkedKeys = useLinkedKeys(owner, link);
7649
+ const reverse = useMemo(() => reverseLinkOf((name) => app.registry.get(name)?.fields, owner, link), [
7650
+ app.registry,
7651
+ owner,
7652
+ link
7653
+ ]);
7654
+ let liveHides = false;
7655
+ if (reverse !== void 0) try {
7656
+ const { providerId } = runtime.app.resolveDataBinding(target);
7657
+ liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && linkCapabilitiesOf(runtime, providerId)?.links?.filter.none !== false;
7658
+ } catch {
7659
+ liveHides = false;
7660
+ }
7661
+ const liveHidesRef = useRef(liveHides);
7662
+ liveHidesRef.current = liveHides;
7663
+ const [frozenHides, setFrozenHides] = useState(null);
7664
+ const hides = frozenHides ?? liveHides;
7665
+ const ownerKeyId = JSON.stringify(ownerKey ?? null);
7666
+ const hidden = useMemo(() => hides && reverse !== void 0 ? [{
7667
+ field: reverse,
7668
+ op: "hasNone",
7669
+ value: [ownerKey]
7670
+ }] : void 0, [
7671
+ hides,
7672
+ reverse,
7673
+ ownerKeyId
7674
+ ]);
7675
+ const disabledKeys = useCallback((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
7676
+ const disabledLabel = useCallback((count) => t("field.link.alreadyLinked", {
7677
+ count,
7678
+ defaultValue: `${count} already linked`
7679
+ }), [t]);
7680
+ const picker = useRelationPicker({
7681
+ to: target,
7682
+ multiple: true,
7683
+ valueField,
7684
+ scope,
7685
+ ...hidden === void 0 ? {
7686
+ disabledKeys,
7687
+ disabledLabel
7688
+ } : { fixedFilters: hidden }
7689
+ });
7690
+ const openPicker = picker.open;
7691
+ const [summary, setSummary] = useState(null);
7692
+ const finish = useCallback((report, titles, done) => {
7693
+ if (report.outcome !== "success") {
7694
+ setSummary({
7695
+ report,
7696
+ titles
7697
+ });
7698
+ return;
7699
+ }
7700
+ const count = report.succeeded.length;
7701
+ if (count === 0) return;
7702
+ toast.success(t(`field.link.${done}`, {
7703
+ count,
7704
+ defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
7705
+ }));
7706
+ }, [t]);
7707
+ const attaching = useRef(false);
7708
+ const [attachWriting, setAttachWriting] = useState(false);
7709
+ const onAttach = useCallback(() => {
7710
+ if (attaching.current) return;
7711
+ attaching.current = true;
7712
+ (async () => {
7713
+ try {
7714
+ setFrozenHides(liveHidesRef.current);
7715
+ let result;
7716
+ try {
7717
+ result = await openPicker();
7718
+ } finally {
7719
+ setFrozenHides(null);
7720
+ }
7721
+ if (result.kind !== "selected" || result.keys.length === 0) return;
7722
+ const titles = {};
7723
+ for (const row of result.rows) {
7724
+ const key = valueField === "id" ? row.id : row[valueField];
7725
+ const title = row[displayField];
7726
+ if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
7727
+ }
7728
+ setAttachWriting(true);
7729
+ try {
7730
+ finish(await writer.attach(result.keys, record), titles, "attached");
7731
+ } finally {
7732
+ setAttachWriting(false);
7733
+ }
7734
+ } finally {
7735
+ attaching.current = false;
7736
+ }
7737
+ })();
7738
+ }, [
7739
+ displayField,
7740
+ finish,
7741
+ openPicker,
7742
+ record,
7743
+ valueField,
7744
+ writer
7745
+ ]);
7746
+ const pending = useRef(null);
7747
+ const [confirming, setConfirming] = useState(null);
7748
+ const detaching = useRef(false);
7749
+ const [busy, setBusy] = useState(false);
7750
+ const onDetach = useCallback((rows) => new Promise((resolve) => {
7751
+ pending.current?.resolve([]);
7752
+ pending.current = {
7753
+ rows,
7754
+ resolve
7755
+ };
7756
+ setConfirming(rows.length);
7757
+ }), []);
7758
+ useEffect(() => () => {
7759
+ pending.current?.resolve([]);
7760
+ pending.current = null;
7761
+ }, []);
7762
+ const cancel = useCallback(() => {
7763
+ if (detaching.current) return;
7764
+ pending.current?.resolve([]);
7765
+ pending.current = null;
7766
+ setConfirming(null);
7767
+ }, []);
7768
+ const proceed = useCallback(() => {
7769
+ const current = pending.current;
7770
+ if (current === null || detaching.current) return;
7771
+ detaching.current = true;
7772
+ setBusy(true);
7773
+ (async () => {
7774
+ try {
7775
+ const titles = {};
7776
+ for (const row of current.rows) {
7777
+ const id = row[junctionIdField];
7778
+ if (id === void 0 || id === null) continue;
7779
+ const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
7780
+ if (title !== void 0 && title !== null) titles[String(id)] = String(title);
7781
+ }
7782
+ const report = await writer.detach(current.rows, record);
7783
+ pending.current = null;
7784
+ setConfirming(null);
7785
+ finish(report, titles, "detached");
7786
+ const left = new Set(report.succeeded);
7787
+ current.resolve(current.rows.filter((row) => {
7788
+ const id = row[junctionIdField];
7789
+ return id !== void 0 && id !== null && left.has(String(id));
7790
+ }).map((row) => String(row.id)));
7791
+ } finally {
7792
+ detaching.current = false;
7793
+ setBusy(false);
7794
+ }
7795
+ })();
7796
+ }, [
7797
+ displayField,
7798
+ finish,
7799
+ junctionIdField,
7800
+ record,
7801
+ targetKey,
7802
+ writer
7803
+ ]);
7804
+ const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
7805
+ const count = confirming ?? 0;
7806
+ const gate = {
7807
+ resource: junction,
7808
+ open: confirming !== null,
7809
+ busy,
7810
+ header: t("field.link.confirmDetach", {
7811
+ count,
7812
+ defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
7813
+ }),
7814
+ blockers: NO_ISSUES,
7815
+ confirmable: NO_ISSUES,
7816
+ proceed,
7817
+ cancel
7818
+ };
7819
+ const attachAllowed = rights.attach.allowed;
7820
+ const detachAllowed = rights.detach.allowed;
7821
+ return {
7822
+ linkEditing: useMemo(() => !attachAllowed && !detachAllowed ? void 0 : {
7823
+ ...attachAllowed ? { attach: {
7824
+ onAttach,
7825
+ busy: attachWriting
7826
+ } } : {},
7827
+ ...detachAllowed ? { detach: { onDetach } } : {}
7828
+ }, [
7829
+ attachAllowed,
7830
+ attachWriting,
7831
+ detachAllowed,
7832
+ onAttach,
7833
+ onDetach
7834
+ ]),
7835
+ element: jsxs(Fragment$1, { children: [
7836
+ picker.element,
7837
+ jsx(CheckConfirmDialog, {
7838
+ state: gate,
7839
+ testid: `link-detach-confirm:${junction}`,
7840
+ title: detachLabel,
7841
+ proceedLabel: detachLabel,
7842
+ tone: "destructive"
7843
+ }),
7844
+ summary === null ? null : jsx(LinkWriteSummary, {
7845
+ report: summary.report,
7846
+ titles: summary.titles,
7847
+ onClose: () => setSummary(null),
7848
+ testid: `link-write-summary:${junction}`
7849
+ })
7850
+ ] })
7851
+ };
7852
+ }
7853
+ //#endregion
6982
7854
  //#region src/quick/peek-filter.ts
6983
7855
  function peekFieldDefinition(def, name) {
6984
7856
  return declaredField(def, name);
@@ -7002,32 +7874,12 @@ function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
7002
7874
  return peekRow(def, row, idField, capabilityFor, fieldVisible);
7003
7875
  }
7004
7876
  //#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
7877
  //#region src/blocks/link-panel.tsx
7025
7878
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7026
7879
  const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
7027
- function qualify(ref, owner) {
7880
+ function qualify(ref) {
7028
7881
  try {
7029
- const namespace = normalizeEntityRef(owner).namespace;
7030
- return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref, { defaultNamespace: namespace }));
7882
+ return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref));
7031
7883
  } catch {
7032
7884
  return;
7033
7885
  }
@@ -7046,7 +7898,7 @@ function linkField(registry, owner, link) {
7046
7898
  function linkTarget(registry, owner, link) {
7047
7899
  const found = linkField(registry, owner, link);
7048
7900
  if (found === void 0 || owner === void 0) return void 0;
7049
- return qualify(found.field.meta?.to, owner);
7901
+ return qualify(found.field.meta?.to);
7050
7902
  }
7051
7903
  function idFieldOf(def) {
7052
7904
  const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
@@ -7062,37 +7914,47 @@ function targetColumns(registry, target, def) {
7062
7914
  const source = def;
7063
7915
  const views = isRecord(def.views) ? def.views : void 0;
7064
7916
  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) => {
7917
+ return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
7918
+ const name = isRecord(entry) ? entry.field : entry;
7919
+ return typeof name === "string" ? [name] : [];
7920
+ }) : dtoFields(source, "list")).filter((name) => {
7066
7921
  if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && resolveColumnField(registry, target, name) !== void 0;
7067
7922
  const field = peekFieldDefinition(source, name);
7068
7923
  return isRecord(field) && !isRelationField(field) && fieldSensitivity(field) === void 0;
7069
7924
  });
7070
7925
  }
7071
7926
  function LinkPanel(props) {
7072
- const { link, owner, own } = props;
7927
+ const { link, owner, own, empty } = props;
7073
7928
  const app = useApp();
7074
7929
  const frame = usePlacementFrame();
7075
7930
  const routeScreen = useIsRouteScreen();
7076
7931
  const record = useOptionalRecordFrame()?.record;
7932
+ const runtime = useOptionalDataRuntime();
7077
7933
  const storage = useMemo(() => {
7078
7934
  const found = linkField(app.registry, owner, link);
7079
7935
  if (found === void 0) return void 0;
7080
7936
  const { declaration } = found;
7081
- const junction = qualify(declaration.junction, owner);
7082
- const target = qualify(found.field.meta?.to, owner);
7937
+ const junction = qualify(declaration.junction);
7938
+ const target = qualify(found.field.meta?.to);
7083
7939
  if (junction === void 0 || target === void 0) return void 0;
7084
7940
  const junctionDef = app.registry.get(junction);
7085
7941
  if (junctionDef === void 0) return void 0;
7086
7942
  const junctionFields = junctionDef.fields;
7943
+ const targetDef = app.registry.get(target);
7944
+ const targetBy = byOf(junctionFields[declaration.targetKey]);
7945
+ const declaredScope = found.field.meta?.scope;
7087
7946
  return {
7088
7947
  junction,
7089
7948
  target,
7090
7949
  sourceKey: declaration.sourceKey,
7091
7950
  targetKey: declaration.targetKey,
7092
7951
  ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
7093
- targetKeyedBy: byOf(junctionFields[declaration.targetKey]) !== void 0,
7952
+ targetKeyedBy: targetBy !== void 0,
7953
+ targetValueField: targetBy ?? idFieldOf(targetDef),
7954
+ junctionIdField: idFieldOf(junctionDef),
7094
7955
  where: declaration.where,
7095
- targetDef: app.registry.get(target)
7956
+ targetDef,
7957
+ pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
7096
7958
  };
7097
7959
  }, [
7098
7960
  app.registry,
@@ -7104,10 +7966,11 @@ function LinkPanel(props) {
7104
7966
  const fixedFilters = useMemo(() => {
7105
7967
  if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
7106
7968
  const { sourceKey, targetKey } = storage;
7969
+ const placed = whereToFilter(own.where ?? [], (field) => `${targetKey}.${field}`);
7107
7970
  return [
7108
7971
  whereEntryToFilter(sourceKey, "eq", ownerKey),
7109
7972
  ...storage.where.map((entry) => whereEntryToFilter(entry.field, entry.op, entry.value)),
7110
- ...(own.where ?? []).map((entry) => whereEntryToFilter(`${targetKey}.${entry.field}`, entry.op, entry.value))
7973
+ ...placed === void 0 ? [] : [placed]
7111
7974
  ];
7112
7975
  }, [
7113
7976
  storage,
@@ -7117,7 +7980,12 @@ function LinkPanel(props) {
7117
7980
  const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
7118
7981
  const columns = useMemo(() => {
7119
7982
  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 ?? []];
7983
+ const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
7984
+ const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
7985
+ ...entry,
7986
+ field: `${storage.targetKey}.${entry.field}`
7987
+ };
7988
+ return [...shown.map(across), ...own.linkColumns ?? []];
7121
7989
  }, [
7122
7990
  storage,
7123
7991
  columnsKey,
@@ -7127,31 +7995,76 @@ function LinkPanel(props) {
7127
7995
  resource: storage.target,
7128
7996
  field: storage.targetKey
7129
7997
  }, [storage]);
7130
- const unbound = fixedFilters === void 0 || own.where?.some((entry) => entry.value === null || entry.value === void 0) === true;
7998
+ const unbound = fixedFilters === void 0 || own.where !== void 0 && hasNullRef(own.where);
7131
7999
  if (storage === void 0 || unbound) return jsx(UnboundRelatedList, {
7132
8000
  resource: storage?.target ?? "",
7133
- title: own.title
8001
+ title: own.title,
8002
+ ...empty === void 0 ? {} : { empty }
7134
8003
  });
7135
8004
  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, {
8005
+ let providerCounts;
8006
+ if (runtime !== null) try {
8007
+ providerCounts = linkCapabilitiesOf(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
8008
+ } catch {
8009
+ providerCounts = void 0;
8010
+ }
8011
+ const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
8012
+ const layoutId = frame === null ? void 0 : placementLayoutId({
8013
+ owner,
8014
+ key: frame.key
8015
+ });
8016
+ const surface = {
7138
8017
  resource: storage.junction,
7139
8018
  variant: "placement",
7140
8019
  state,
7141
8020
  fixedFilters,
7142
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
8021
+ ...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
7143
8022
  ...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
7144
8023
  ...columns === void 0 ? {} : { columns },
7145
8024
  open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
7146
8025
  ...rowTarget === void 0 ? {} : { rowTarget },
7147
8026
  ...frame === null ? {} : { placementKey: frame.key },
8027
+ ...layoutId === void 0 ? {} : { layoutId },
7148
8028
  ...own.title === void 0 ? {} : { title: own.title },
8029
+ ...empty === void 0 ? {} : { empty },
7149
8030
  scope: placementScope(own.scope, owner),
7150
8031
  ...count === void 0 ? {} : { count },
7151
8032
  readOnly: true,
7152
8033
  exportable: false
8034
+ };
8035
+ if (runtime === null || record === void 0) return jsx(ResourceListSurface, { ...surface });
8036
+ return jsx(WritableLinkPanel, {
8037
+ surface,
8038
+ owner,
8039
+ link,
8040
+ storage,
8041
+ record,
8042
+ ownerKey,
8043
+ placement: own.linkActions
7153
8044
  });
7154
8045
  }
8046
+ function WritableLinkPanel(props) {
8047
+ const { surface, owner, link, storage, record, ownerKey, placement } = props;
8048
+ const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
8049
+ const { linkEditing, element } = useLinkPanelWrites({
8050
+ owner,
8051
+ junction: storage.junction,
8052
+ target: storage.target,
8053
+ link,
8054
+ targetKey: storage.targetKey,
8055
+ valueField: storage.targetValueField,
8056
+ junctionIdField: storage.junctionIdField,
8057
+ displayField,
8058
+ scope: storage.pickerScope ?? "context",
8059
+ record,
8060
+ ownerKey,
8061
+ placement
8062
+ });
8063
+ return jsxs(Fragment$1, { children: [jsx(ResourceListSurface, {
8064
+ ...surface,
8065
+ ...linkEditing === void 0 ? {} : { linkEditing }
8066
+ }), element] });
8067
+ }
7155
8068
  //#endregion
7156
8069
  //#region src/blocks/route-list-context.ts
7157
8070
  const RouteListContext = createContext(null);
@@ -7160,7 +8073,7 @@ function useRouteList() {
7160
8073
  }
7161
8074
  //#endregion
7162
8075
  //#region src/blocks/related-list-view.tsx
7163
- function RelatedListView({ props }) {
8076
+ function RelatedListView({ props, empty }) {
7164
8077
  const own = props;
7165
8078
  const route = useRouteList();
7166
8079
  const frame = usePlacementFrame();
@@ -7168,7 +8081,7 @@ function RelatedListView({ props }) {
7168
8081
  const linked = own.link !== void 0;
7169
8082
  const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : resourceKey(own.resource);
7170
8083
  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]);
8084
+ const fixedFilters = useMemo(() => own.where === void 0 || own.where.length === 0 ? void 0 : [whereToFilter(own.where)], [whereKey]);
7172
8085
  const columnsKey = JSON.stringify(own.columns ?? null);
7173
8086
  const columns = useMemo(() => own.columns, [columnsKey]);
7174
8087
  const routeScreen = useIsRouteScreen();
@@ -7180,20 +8093,23 @@ function RelatedListView({ props }) {
7180
8093
  const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
7181
8094
  const granted = ownerDef === void 0 ? "unknown" : grantedPlacementKeys(ownerDef);
7182
8095
  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;
8096
+ 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
8097
  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;
8098
+ const unbound = own.where !== void 0 && hasNullRef(own.where);
7186
8099
  if (linked) return frame?.owner === void 0 ? jsx(UnboundRelatedList, {
7187
8100
  resource,
7188
- title: own.title
8101
+ title: own.title,
8102
+ ...empty === void 0 ? {} : { empty }
7189
8103
  }) : jsx(LinkPanel, {
7190
8104
  link: own.link,
7191
8105
  owner: frame.owner,
7192
- own
8106
+ own,
8107
+ ...empty === void 0 ? {} : { empty }
7193
8108
  });
7194
8109
  if (route !== null) return jsx(ResourceListSurface, {
7195
8110
  resource,
7196
8111
  variant: "route",
8112
+ layoutId: "route",
7197
8113
  ...route.meta === void 0 ? {} : { meta: route.meta },
7198
8114
  ...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
7199
8115
  ...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
@@ -7206,24 +8122,28 @@ function RelatedListView({ props }) {
7206
8122
  });
7207
8123
  if (unbound) return jsx(UnboundRelatedList, {
7208
8124
  resource,
7209
- title: own.title
8125
+ title: own.title,
8126
+ ...empty === void 0 ? {} : { empty }
7210
8127
  });
7211
8128
  const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
7212
8129
  const scope = placementScope(own.scope, frame?.owner);
7213
8130
  const count = own.count === false ? "off" : own.count;
8131
+ const layoutId = placementLayoutId(frame);
7214
8132
  return jsx(ResourceListSurface, {
7215
8133
  resource,
7216
8134
  variant: "placement",
7217
8135
  state,
7218
8136
  ...fixedFilters === void 0 ? {} : { fixedFilters },
7219
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
8137
+ ...own.initial === void 0 ? {} : { initial: own.initial },
7220
8138
  ...own.search === void 0 ? {} : { search: own.search },
7221
8139
  ...columns === void 0 ? {} : { columns },
7222
8140
  open: own.open ?? "quick",
7223
8141
  ...own.view === void 0 ? {} : { view: own.view },
7224
8142
  ...isItemHandle(own.item) ? { item: own.item } : {},
7225
8143
  ...frame === null ? {} : { placementKey: frame.key },
8144
+ ...layoutId === void 0 ? {} : { layoutId },
7226
8145
  ...own.title === void 0 ? {} : { title: own.title },
8146
+ ...empty === void 0 ? {} : { empty },
7227
8147
  scope,
7228
8148
  ...count === void 0 ? {} : { count },
7229
8149
  ...onCreate === void 0 ? {} : { onCreate }
@@ -7258,7 +8178,7 @@ function Missing({ text }) {
7258
8178
  children: text ?? t("table.empty", { defaultValue: "No records" })
7259
8179
  });
7260
8180
  }
7261
- function RelatedRecordView({ props }) {
8181
+ function RelatedRecordView({ props, empty }) {
7262
8182
  const own = props;
7263
8183
  const app = useApp();
7264
8184
  const resource = resourceKey(own.resource);
@@ -7274,7 +8194,7 @@ function RelatedRecordView({ props }) {
7274
8194
  });
7275
8195
  const result = query.data;
7276
8196
  const raw = result?.data;
7277
- const dtoRow = useMemo(() => raw !== null && typeof raw === "object" && projection !== void 0 ? projectDto(raw, projection) : void 0, [raw, projection]);
8197
+ const dtoRow = useMemo(() => raw !== null && typeof raw === "object" && projection !== void 0 ? deriveRecord(raw, (record) => projectDto(record, projection)) : void 0, [raw, projection]);
7278
8198
  const access = useFieldAccess(resource, "record", dtoRow);
7279
8199
  const projected = useProjectedRecord(resource, dtoRow, { placeholder: query.isPlaceholderData === true });
7280
8200
  const fieldsKey = JSON.stringify(own.fields ?? null);
@@ -7292,15 +8212,15 @@ function RelatedRecordView({ props }) {
7292
8212
  capabilityFor,
7293
8213
  access
7294
8214
  ]);
7295
- if (id === null) return jsx(Missing, { text: own.missing });
8215
+ if (id === null) return jsx(Missing, { text: own.missing ?? empty });
7296
8216
  if (result === void 0) {
7297
- if (query.error != null) return jsx(Missing, { text: own.missing });
8217
+ if (query.error != null) return jsx(Missing, { text: own.missing ?? empty });
7298
8218
  return jsx(Skeleton, {
7299
8219
  "data-buro-skeleton": "",
7300
8220
  className: "h-9 w-full"
7301
8221
  });
7302
8222
  }
7303
- if (dtoRow === void 0) return jsx(Missing, { text: own.missing });
8223
+ if (dtoRow === void 0) return jsx(Missing, { text: own.missing ?? empty });
7304
8224
  if (projected.status === "pending" || projected.value === null) return jsx(Skeleton, {
7305
8225
  "data-buro-skeleton": "",
7306
8226
  className: "h-9 w-full"
@@ -7315,10 +8235,7 @@ function RelatedRecordView({ props }) {
7315
8235
  children: own.title
7316
8236
  }), own.toolbar === "item" ? jsx(ActionOverflow, {
7317
8237
  resource,
7318
- record: {
7319
- ...dtoRow,
7320
- id
7321
- },
8238
+ record: withRecordKeys(dtoRow, { id }),
7322
8239
  surface: "show"
7323
8240
  }) : null]
7324
8241
  }), jsx("dl", {
@@ -7333,13 +8250,21 @@ function RelatedRecordView({ props }) {
7333
8250
  children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
7334
8251
  }), jsx("dd", {
7335
8252
  className: "min-w-0 break-words text-sm",
7336
- children: jsx(ResolvedFieldDisplay, {
7337
- resource: own.resource,
7338
- name,
7339
- field,
8253
+ children: jsx(FieldValueFrame, {
8254
+ field: {
8255
+ ...field ?? {},
8256
+ name
8257
+ },
7340
8258
  value,
7341
- record: row,
7342
- fallback: toDisplayText(value).text
8259
+ surface: "display",
8260
+ children: jsx(ResolvedFieldDisplay, {
8261
+ resource: own.resource,
8262
+ name,
8263
+ field,
8264
+ value,
8265
+ record: row,
8266
+ fallback: toDisplayText(value).text
8267
+ })
7343
8268
  })
7344
8269
  })]
7345
8270
  }, name);
@@ -7410,28 +8335,39 @@ function Chrome({ node, size, state, children }) {
7410
8335
  });
7411
8336
  }
7412
8337
  function isAuthError(error) {
7413
- return isDataError(error) && error.kind === "auth";
8338
+ return normalizeDataError(error).kind === "auth";
7414
8339
  }
7415
- function FailedPlacement({ node, size, message, retry }) {
8340
+ const PLACE_NODE_MESSAGES = {
8341
+ failed: "This section could not be loaded.",
8342
+ sessionExpired: "Your session has expired. Sign in again to continue.",
8343
+ notImplemented: "This section is not available."
8344
+ };
8345
+ function FailedPlacement({ node, size, message, retry, error }) {
7416
8346
  const t = useOptionalT();
8347
+ const chromeT = useChromeT();
8348
+ const fallback = {
8349
+ key: `block.${message}`,
8350
+ default: PLACE_NODE_MESSAGES[message]
8351
+ };
7417
8352
  return jsx(Chrome, {
7418
8353
  node,
7419
8354
  size,
7420
8355
  state: "failed",
7421
- children: jsxs("div", {
8356
+ children: message === "notImplemented" ? jsxs("div", {
7422
8357
  role: "alert",
7423
8358
  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, {
8359
+ children: [jsx("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : jsx(Button, {
7429
8360
  type: "button",
7430
8361
  variant: "outline",
7431
8362
  size: "sm",
7432
8363
  onClick: retry,
7433
- children: t("actions.retry", { defaultValue: "Retry" })
8364
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
7434
8365
  })]
8366
+ }) : jsx(LoadError, {
8367
+ surface: "block",
8368
+ error,
8369
+ ...retry === void 0 ? {} : { retry },
8370
+ fallback
7435
8371
  })
7436
8372
  });
7437
8373
  }
@@ -7511,7 +8447,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7511
8447
  nested,
7512
8448
  ...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
7513
8449
  closure: compiled.sourceClosure,
7514
- ...owner === void 0 ? {} : { owner }
8450
+ ...owner === void 0 ? {} : { owner },
8451
+ sources: controller.sources
7515
8452
  }), [
7516
8453
  node.key,
7517
8454
  node.id,
@@ -7519,7 +8456,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7519
8456
  nested,
7520
8457
  declaredWhere,
7521
8458
  compiled.sourceClosure,
7522
- owner
8459
+ owner,
8460
+ controller.sources
7523
8461
  ]);
7524
8462
  return jsx(PlacementBoundary, {
7525
8463
  node,
@@ -7577,6 +8515,7 @@ function PlaceNode({ node }) {
7577
8515
  node,
7578
8516
  size,
7579
8517
  message: isAuthError(placement.error) ? "sessionExpired" : "failed",
8518
+ error: placement.error,
7580
8519
  ...placement.retry === void 0 ? {} : { retry: placement.retry }
7581
8520
  });
7582
8521
  if (implementation === void 0) return jsx(FailedPlacement, {
@@ -7592,6 +8531,6 @@ function PlaceNode({ node }) {
7592
8531
  });
7593
8532
  }
7594
8533
  //#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 };
8534
+ export { CursorLoadMore as $, ListSearchInput as A, AppFlowButton as At, ExportDialog as B, ListControls as C, useShownItemActions as Ct, ListFlows as D, EnumSettlingRow as Dt, ListLive as E, EnumBadge as Et, ListColumns as F, toDisplayText as Ft, RouteScreenClaimContext as G, ScreenTree as H, ColumnsToggle as I, ShellProvider as It, FormControlProvider as J, RouteScreenContext as K, DataTableViewOptions as L, defaultUserMapper as Lt, ExportButton as M, FieldDisplay as Mt, ListCreate as N, i18nPrefixToRef$1 as Nt, ListFilters as O, unknownEnumValueLabel as Ot, CreateButton as P, FieldValueFrame as Pt, ResourceList as Q, ListBulkBar as R, usePaletteState as Rt, useFrameChrome as S, ActionOverflow as St, SavedViewsDropdown as T, downloadArtifact as Tt, useTabsUrlState as U, DeclarativeForm as V, QuickScreenClaimContext as W, useFormControlOf as X, useFormControl as Y, ListBody as Z, CopyId as _, ShowPageHeader as _t, peekFieldDefinition as a, DataTable as at, RecordTitle as b, useDocumentTitle 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, PageHeader as gt, TypeChromeDrawnContext as h, EditPageHeader as ht, RouteListContext as i, useBuroDataTable as it, ListExport as j, Field as jt, FilterRegion as k, DeepLinkScope as kt, useRelationPicker as l, TabsList as lt, HeaderTypeNotice as m, RelatedTimelineSkeleton 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, resolveResourceTitle as vt, ListSavedViews as w, useExportRunWatcher as wt, FrameChromeContext as x, typography as xt, RecordActions as y, DocumentTitleProvider as yt, BulkActionToolbar as z, useShell as zt };
7596
8535
 
7597
- //# sourceMappingURL=place-node-NraNYKPx.js.map
8536
+ //# sourceMappingURL=place-node-BdxT-30h.js.map