@burojs/ui-shadcn 0.4.0 → 0.5.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 (137) hide show
  1. package/README.md +22 -5
  2. package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
  3. package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
  4. package/dist/bulk-summary-GxMDP4bw.js +108 -0
  5. package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
  6. package/dist/button-BxnnE28H.cjs +55 -0
  7. package/dist/button-BxnnE28H.cjs.map +1 -0
  8. package/dist/button-MWVeZaqj.js +44 -0
  9. package/dist/button-MWVeZaqj.js.map +1 -0
  10. package/dist/candidates-gate-CX2PBu6l.js +63 -0
  11. package/dist/candidates-gate-CX2PBu6l.js.map +1 -0
  12. package/dist/candidates-gate-DiFI52r7.cjs +70 -0
  13. package/dist/candidates-gate-DiFI52r7.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-CBsVcnPR.js +82 -0
  15. package/dist/candidates-relation-picker-CBsVcnPR.js.map +1 -0
  16. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +84 -0
  17. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +1 -0
  18. package/dist/check-confirm-dialog-BRByTFpg.cjs +1534 -0
  19. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-CkaVTPT9.js +1232 -0
  21. package/dist/check-confirm-dialog-CkaVTPT9.js.map +1 -0
  22. package/dist/dialog-B_rt9S1c.js +63 -0
  23. package/dist/dialog-B_rt9S1c.js.map +1 -0
  24. package/dist/dialog-DRNUq9EA.cjs +112 -0
  25. package/dist/dialog-DRNUq9EA.cjs.map +1 -0
  26. package/dist/filter-builder-B3mZFL-K.cjs +1761 -0
  27. package/dist/filter-builder-B3mZFL-K.cjs.map +1 -0
  28. package/dist/filter-builder-DOnaXNfP.js +1605 -0
  29. package/dist/filter-builder-DOnaXNfP.js.map +1 -0
  30. package/dist/flow-runtime-B6zOHYqI.cjs +255 -0
  31. package/dist/flow-runtime-B6zOHYqI.cjs.map +1 -0
  32. package/dist/flow-runtime-D8h99FSg.js +252 -0
  33. package/dist/flow-runtime-D8h99FSg.js.map +1 -0
  34. package/dist/flow-screen-CX05b3G9.cjs +705 -0
  35. package/dist/flow-screen-CX05b3G9.cjs.map +1 -0
  36. package/dist/flow-screen-CmbARSqg.js +701 -0
  37. package/dist/flow-screen-CmbARSqg.js.map +1 -0
  38. package/dist/index.cjs +2825 -11912
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1612 -1862
  41. package/dist/index.d.ts +1700 -1950
  42. package/dist/index.js +2498 -11605
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B7U1OJUe.cjs +112 -0
  45. package/dist/inline-flow-host-B7U1OJUe.cjs.map +1 -0
  46. package/dist/inline-flow-host-C3H2UpSA.js +112 -0
  47. package/dist/inline-flow-host-C3H2UpSA.js.map +1 -0
  48. package/dist/input-BI0czGyI.cjs +23 -0
  49. package/dist/input-BI0czGyI.cjs.map +1 -0
  50. package/dist/input-CUPStzZO.js +17 -0
  51. package/dist/input-CUPStzZO.js.map +1 -0
  52. package/dist/label-66W1qbke.js +29 -0
  53. package/dist/label-66W1qbke.js.map +1 -0
  54. package/dist/label-w6JYf2cF.cjs +36 -0
  55. package/dist/label-w6JYf2cF.cjs.map +1 -0
  56. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +14 -0
  57. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +1 -0
  58. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +19 -0
  59. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +1 -0
  60. package/dist/markdown/index.cjs +1 -1
  61. package/dist/markdown/index.js +1 -1
  62. package/dist/page-flow-route-CQ7HsbBm.cjs +277 -0
  63. package/dist/page-flow-route-CQ7HsbBm.cjs.map +1 -0
  64. package/dist/page-flow-route-CyXSvXSE.js +277 -0
  65. package/dist/page-flow-route-CyXSvXSE.js.map +1 -0
  66. package/dist/peek-filter-CnqHwTMU.cjs +51 -0
  67. package/dist/peek-filter-CnqHwTMU.cjs.map +1 -0
  68. package/dist/peek-filter-ErLfO153.js +28 -0
  69. package/dist/peek-filter-ErLfO153.js.map +1 -0
  70. package/dist/place-node--GdsP688.js +7315 -0
  71. package/dist/place-node--GdsP688.js.map +1 -0
  72. package/dist/place-node-SIp4XYQH.cjs +7833 -0
  73. package/dist/place-node-SIp4XYQH.cjs.map +1 -0
  74. package/dist/related-list-cards-B6x9HQQp.js +42 -0
  75. package/dist/related-list-cards-B6x9HQQp.js.map +1 -0
  76. package/dist/related-list-cards-nxuODlIA.cjs +42 -0
  77. package/dist/related-list-cards-nxuODlIA.cjs.map +1 -0
  78. package/dist/related-list-timeline-BYJKEbmA.cjs +142 -0
  79. package/dist/related-list-timeline-BYJKEbmA.cjs.map +1 -0
  80. package/dist/related-list-timeline-DEdfd-rr.js +142 -0
  81. package/dist/related-list-timeline-DEdfd-rr.js.map +1 -0
  82. package/dist/related-rows-B4levHcR.js +186 -0
  83. package/dist/related-rows-B4levHcR.js.map +1 -0
  84. package/dist/related-rows-zh5f_4QR.cjs +209 -0
  85. package/dist/related-rows-zh5f_4QR.cjs.map +1 -0
  86. package/dist/relation-create-BOaUe12G.cjs +362 -0
  87. package/dist/relation-create-BOaUe12G.cjs.map +1 -0
  88. package/dist/relation-create-Bg-eEJLs.js +337 -0
  89. package/dist/relation-create-Bg-eEJLs.js.map +1 -0
  90. package/dist/relation-many-picker-B-NCEUgX.cjs +465 -0
  91. package/dist/relation-many-picker-B-NCEUgX.cjs.map +1 -0
  92. package/dist/relation-many-picker-gdAOD-qK.js +464 -0
  93. package/dist/relation-many-picker-gdAOD-qK.js.map +1 -0
  94. package/dist/relation-picker-BAY77ivO.js +462 -0
  95. package/dist/relation-picker-BAY77ivO.js.map +1 -0
  96. package/dist/relation-picker-Da_uhFvA.cjs +479 -0
  97. package/dist/relation-picker-Da_uhFvA.cjs.map +1 -0
  98. package/dist/relation-picker-dialog-M0pQaWlp.cjs +300 -0
  99. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +1 -0
  100. package/dist/relation-picker-dialog-hA9rYrZB.js +299 -0
  101. package/dist/relation-picker-dialog-hA9rYrZB.js.map +1 -0
  102. package/dist/resource-page-router-OSQyRA5b.cjs +1779 -0
  103. package/dist/resource-page-router-OSQyRA5b.cjs.map +1 -0
  104. package/dist/resource-page-router-T4vS5gbJ.js +1582 -0
  105. package/dist/resource-page-router-T4vS5gbJ.js.map +1 -0
  106. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  107. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  108. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  109. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  110. package/dist/search-access-B7G5oR8n.cjs +812 -0
  111. package/dist/search-access-B7G5oR8n.cjs.map +1 -0
  112. package/dist/search-access-CURsWn-U.js +669 -0
  113. package/dist/search-access-CURsWn-U.js.map +1 -0
  114. package/dist/shell-BPlk8hEa.js +205 -0
  115. package/dist/shell-BPlk8hEa.js.map +1 -0
  116. package/dist/shell-DxhlsPwm.cjs +271 -0
  117. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  118. package/dist/skeleton-U2noefj5.js +13 -0
  119. package/dist/skeleton-U2noefj5.js.map +1 -0
  120. package/dist/skeleton-_1gP7wan.cjs +18 -0
  121. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  122. package/dist/styles.css +241 -9
  123. package/dist/toaster-C7CiYA4Z.cjs +248 -0
  124. package/dist/toaster-C7CiYA4Z.cjs.map +1 -0
  125. package/dist/toaster-CfCRLgR-.js +164 -0
  126. package/dist/toaster-CfCRLgR-.js.map +1 -0
  127. package/dist/use-dialog-filter-state-BBCC0OEK.cjs +92 -0
  128. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +1 -0
  129. package/dist/use-dialog-filter-state-BDcgS5Oz.js +87 -0
  130. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +1 -0
  131. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  132. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  133. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  134. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  135. package/package.json +16 -14
  136. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  137. package/dist/use-theme-IHmtrhEt.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,30 +1,29 @@
1
1
  import { t as THEME_STORAGE_KEY } from "./theme-storage-key-pYlp4aDS.js";
2
- import { ActionDensity, AppRuntime, Check, CheckSurface, CustomPageDef, Dispatcher, DocumentTemplate, EntityRef, ErasedResourceHandle, ExportArtifact, ExportTarget, FieldDeclaration, FieldLayoutSpec, FilterExpression, FilterGroup, FilterOperator, FixedFilterInput, HrefOpts, I18nCaps, LocalizedMessage, NavGroupDef, NavNode, PortableValue, ResolvedFixedFilter, ResourceDef, ResourceFieldMap, ResourceValidator, ResourceWizardDef, RouteMatch, SavedViewDef, SectionDef, SortExpression, StandardSchemaV1, TabView, TaskRunReport, UiSettingsIssue, ValidationIssues, ValueCodec, i18nPrefixToRef } from "@burojs/core";
3
- import { CheckGateState, FieldBinding, FieldImplementation, FieldPresentation, FilterFieldSpec, FilterKind, FormValues, RecentRecord, RowRecord, SavedViewEntry, UIPresentationHostProps, UIPresentationHosts, UseDeclarativeFormOptions, modeForSurface } from "@burojs/react";
2
+ import { ActionDensity, AnyFrameHandle, AppRuntime, AuthResult, Check, CheckSurface, CustomPageDef, DraftOption, EntityRef, ErasedResourceHandle, ExportArtifact, ExportTarget, FieldDeclaration, FilterExpression, FilterGroup, FilterOperator, FixedFilterInput, FormBody, FrameHandle, HrefOpts, I18nCaps, NavGroupDef, NavNode, PortableValue, RelationOpen, ResolvedFixedFilter, ResourceDef, ResourceFieldMap, ResourceHandle, RouteMatch, SavedViewDef, ScreenMode, ScreenSources, SearchDecl, SectionDef, SortExpression, StandardSchemaV1, TaskRunReport, UiSettingsIssue, ValidationIssues, ValueCodec, i18nPrefixToRef } from "@burojs/core";
3
+ import { BlockImplementationCatalog, CheckGateState, CollectionDisplayProps, CollectionInputProps, DEFAULT_LANGUAGE_STORAGE_KEY, FieldBinding, FieldImplementation, FieldImplementationLayout, FieldPresentation, FilterFieldSpec, FilterKind, FormValues, QuickSettledRow, RowRecord, SavedViewEntry, ScalarDisplayProps, ScalarInputProps, UIPresentationHostProps, UIPresentationHosts, UseFormControllerFormOptions, modeForSurface, useApp, useOptionalT, useTestIds } from "@burojs/react";
4
4
  import * as React$1 from "react";
5
- import { AnchorHTMLAttributes, Component as Component$1, ComponentType, JSX, ReactElement, ReactNode, SVGProps } from "react";
5
+ import { AnchorHTMLAttributes, ComponentProps, ComponentType, JSX, ReactElement, ReactNode, SVGProps } from "react";
6
6
  import { ClassValue } from "clsx";
7
7
  import * as PopoverPrimitive from "@radix-ui/react-popover";
8
- import { Slot } from "@radix-ui/react-slot";
8
+ import { Slot as Slot$1 } from "@radix-ui/react-slot";
9
9
  import { VariantProps } from "class-variance-authority";
10
10
  import { Command as Command$1 } from "cmdk";
11
11
  import * as DialogPrimitive from "@radix-ui/react-dialog";
12
12
  import * as SeparatorPrimitive from "@radix-ui/react-separator";
13
13
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
14
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
15
14
  import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
16
- import * as LabelPrimitive from "@radix-ui/react-label";
17
- import { ToasterProps as ToasterProps$1, toast } from "sonner";
18
- import * as SelectPrimitive from "@radix-ui/react-select";
15
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
19
16
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
17
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
18
+ import { ToasterProps as ToasterProps$1, toast } from "sonner";
19
+ import * as LabelPrimitive from "@radix-ui/react-label";
20
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
20
21
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
21
22
  import { Column, ColumnDef, ColumnFiltersState, ColumnOrderState, ColumnPinningState, ColumnSizingState, OnChangeFn, PaginationState, Row, RowSelectionState, SortingState, Table as Table$1, Updater, VisibilityState } from "@tanstack/react-table";
22
- import { isImage } from "@burojs/file-storage";
23
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
23
+ import * as SelectPrimitive from "@radix-ui/react-select";
24
24
  import { ControllerProps, FieldPath, FieldValues } from "react-hook-form";
25
25
  import { z } from "zod";
26
- import * as TabsPrimitive from "@radix-ui/react-tabs";
27
- import { LocaleRegistry } from "@burojs/i18n-core";
26
+ import { isImage } from "@burojs/file-storage";
28
27
  //#region src/doc/doc-info.d.ts
29
28
  interface DocInfoProps {
30
29
  /** Doc address, e.g. `document_document/fields/gtin`. */
@@ -188,7 +187,11 @@ declare function CommandPaletteDocsGroup({ locale, onSelect, heading }: CommandP
188
187
  //#region src/layout/app-layout.d.ts
189
188
  interface AppLayoutProps {
190
189
  children: ReactNode;
191
- /** The sidebar tree — typically <AppSidebar .../>. */
190
+ /**
191
+ * The sidebar tree — typically <AppSidebar .../>. `null` (or `undefined`/`false`) means no
192
+ * sidebar at all: the layout then mounts no `SidebarProvider`, so `useOptionalSidebar()` is
193
+ * null below it and `AppHeader` drops its trigger.
194
+ */
192
195
  sidebar: ReactNode;
193
196
  /** The header tree — typically <AppHeader .../>. */
194
197
  header?: ReactNode;
@@ -212,56 +215,31 @@ interface AppLayoutProps {
212
215
  */
213
216
  declare function AppLayout({ children, sidebar, header, docPanel }: AppLayoutProps): React.ReactElement;
214
217
  //#endregion
218
+ //#region src/shell/slot.d.ts
219
+ /**
220
+ * A shell component's slot. Omit it (or pass `undefined`, which is the same
221
+ * thing) to get the component's default; pass `false` to leave the slot
222
+ * empty; pass anything else to replace the default with it. `null` is an
223
+ * element like any other: it replaces the default and renders nothing.
224
+ */
225
+ type Slot = ReactNode | false;
226
+ //#endregion
215
227
  //#region src/layout/app-sidebar.d.ts
228
+ /** Every slot follows {@link Slot}: omit for the default, `false` for nothing, an element to replace. */
216
229
  interface AppSidebarProps {
217
- /** Top of the sidebar — typically a company/workspace switcher. */
218
- switcher?: ReactNode;
219
- /** Scope filters rendered under the switcher (company-mode only). */
220
- filters?: ReactNode;
221
- /** Main navigation — usually <NavMain />. */
222
- nav: ReactNode;
223
- /** Footer — usually <NavUser />. */
224
- footer?: ReactNode;
225
- }
226
- declare function AppSidebar({ switcher, filters, nav, footer }: AppSidebarProps): React.ReactElement;
227
- //#endregion
228
- //#region src/layout/sidebar.d.ts
229
- interface SidebarProps {
230
- className?: string;
231
- /** Current pathname for active-link highlight + aria-current. */
232
- currentPath?: string;
233
- /**
234
- * Slot for context pickers (company switcher, product-group, location, etc.)
235
- * Rendered between the app name and the resource nav links.
236
- */
237
- pickers?: ReactNode;
238
- /** Slot for content rendered below the resource nav links (footer, version, etc.) */
239
- footer?: ReactNode;
240
- /**
241
- * Optional static nav links rendered ABOVE the resource list (e.g. external
242
- * URLs, conditional links). Consumer provides the JSX. For declarative
243
- * registered pages, prefer `defineCustomPage` + `app.use(...)` — those are
244
- * auto-rendered between this slot and the Resources header. The two
245
- * coexist so adopters can migrate incrementally.
246
- */
247
- topNav?: ReactNode;
248
- /**
249
- * Optional icon resolver invoked for each resource link. Returns a
250
- * ReactNode (typically a Tabler icon at `h-4 w-4`) rendered to
251
- * the LEFT of the resource label, mirroring the iar-ui sidebar
252
- * pattern. Returning `undefined` for a particular resource is fine —
253
- * that link renders without an icon. The whole prop is optional;
254
- * adopters who don't supply it get the legacy text-only sidebar.
255
- */
256
- iconForResource?: (def: ResourceDef) => ReactNode;
257
- /** Same as `iconForResource` but for custom pages (top-level + sectioned). */
258
- iconForCustomPage?: (def: CustomPageDef) => ReactNode;
230
+ /** Top of the sidebar. Default `<SidebarBrand />` (the `ShellProvider` brand). */
231
+ header?: Slot;
232
+ /** Scope filters rendered under the header. No default. */
233
+ filters?: Slot;
234
+ /** Main navigation. Default `<NavMain />`. */
235
+ nav?: Slot;
236
+ /** Footer. Default `<UserMenu />`, which renders nothing without an auth provider. */
237
+ footer?: Slot;
259
238
  }
260
- declare function Sidebar({ className, currentPath, pickers, footer, topNav, iconForResource, iconForCustomPage }: SidebarProps): React.ReactElement;
239
+ declare function AppSidebar({ header, filters, nav, footer }: AppSidebarProps): React.ReactElement;
261
240
  //#endregion
262
241
  //#region src/layout/nav-main.d.ts
263
242
  interface NavMainProps {
264
- currentPath?: string;
265
243
  iconForResource?: (def: ResourceDef) => ReactNode;
266
244
  iconForCustomPage?: (def: CustomPageDef) => ReactNode;
267
245
  /** Icon for a collapsible section's trigger row (#16). */
@@ -287,7 +265,67 @@ interface NavMainProps {
287
265
  */
288
266
  topLabel?: string | null;
289
267
  }
290
- declare function NavMain({ topLabel, currentPath, iconForResource, iconForCustomPage, iconForSection, entryFilter }: NavMainProps): React.ReactElement;
268
+ declare function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection, entryFilter }: NavMainProps): React.ReactElement;
269
+ //#endregion
270
+ //#region src/layout/use-nav-tree.d.ts
271
+ /**
272
+ * How a group presents its children. `flat` and `collapsible` expand inline in
273
+ * the sidebar; any other value is the key of a registered content layout
274
+ * (`tabs`, `secondary-sidebar`, or an app's own), and the sidebar shows the
275
+ * group as ONE link into its area.
276
+ */
277
+ type NavTreeLayout = 'flat' | 'collapsible' | 'tabs' | 'secondary-sidebar' | (string & {});
278
+ /** One navigable entry. `icon` is the declared icon NAME; the renderer resolves it. */
279
+ type NavTreeEntry = {
280
+ kind: 'resource' | 'page';
281
+ key: string;
282
+ label: string;
283
+ href: string;
284
+ icon?: string;
285
+ active: boolean;
286
+ } | {
287
+ kind: 'group';
288
+ key: string;
289
+ label: string;
290
+ href?: string;
291
+ icon?: string;
292
+ /** For a content group: its link is the current location. Otherwise: an ancestor of it. */
293
+ active: boolean;
294
+ layout: NavTreeLayout;
295
+ /** Declared `defaultOpen`, or the active page lives inside. */
296
+ defaultOpen: boolean;
297
+ children: NavTreeEntry[];
298
+ };
299
+ /**
300
+ * One block of the sidebar. The first block is the ungrouped top level, with
301
+ * `key: null` and `label: null`: the renderer decides its heading. A block with
302
+ * a key and `label: null` is a content group's own link and has no heading.
303
+ */
304
+ interface NavTreeSection {
305
+ key: string | null;
306
+ label: string | null;
307
+ icon?: string;
308
+ collapsible: boolean;
309
+ /** Open on first render: declared `defaultOpen`, or `active`. */
310
+ defaultOpen: boolean;
311
+ /** One of the entries is the page currently shown. Drives re-opening a collapsed block. */
312
+ active: boolean;
313
+ entries: NavTreeEntry[];
314
+ }
315
+ interface UseNavTreeOptions {
316
+ /**
317
+ * Extra allow-filter applied to every nav entry (resources, custom pages and
318
+ * groups) on top of the built-in visibility rules. Return false to hide an
319
+ * entry. Defaults to always-true.
320
+ */
321
+ entryFilter?: (entry: NavNode) => boolean;
322
+ }
323
+ /**
324
+ * The sidebar's navigation as plain data: what `<NavMain>` draws, with every
325
+ * visibility rule, permission gate, sort, label and active flag already
326
+ * applied. Build a custom nav on it and it shows the same entries.
327
+ */
328
+ declare function useNavTree(opts?: UseNavTreeOptions): NavTreeSection[];
291
329
  //#endregion
292
330
  //#region src/layout/nav-slots.d.ts
293
331
  /** A resolved child entry a layout renderer draws (spec §8.2). */
@@ -308,33 +346,10 @@ interface NavLayoutProps {
308
346
  children: ReactNode;
309
347
  }
310
348
  type NavLayout = ComponentType<NavLayoutProps>;
311
- /** Components-registry slot key for a layout renderer. */
349
+ /** Components-registry slot key for a layout renderer: `layout.<key>`. */
312
350
  declare function layoutSlot(key: string): string;
313
- /** Components-registry slot key for a custom page's component. */
314
- declare function pageSlot(name: string): string;
315
- /**
316
- * Components-registry slot key for a `kind: 'component'` tab's body.
317
- *
318
- * The declaration and the registration have to name the SAME slot, and until
319
- * this existed both sides hand-wrote the `'tab.'` prefix — which is how two
320
- * conventions ended up in the wild (a bare component name, and a full
321
- * `'tab.…'` key) and why `DetailTabsShell` normalises them. Passing this
322
- * function's result on both sides makes the two agree by construction:
323
- *
324
- * ```ts
325
- * tabs: [{ kind: 'component', key: 'history', component: tabSlot('document-history') }]
326
- * components: { [tabSlot('document-history')]: DocumentHistoryTab }
327
- * ```
328
- *
329
- * Idempotent, so `tabSlot(tabSlot('x'))` is `tabSlot('x')` — the shell accepts
330
- * an already-prefixed key and so does this, rather than producing `tab.tab.x`
331
- * for an adopter migrating a hand-written literal.
332
- */
333
- declare function tabSlot(name: string): string;
334
351
  /** Resolve the registered layout renderer for a `layout` slot key, or undefined. */
335
352
  declare function useLayout(key: string): NavLayout | undefined;
336
- /** Resolve the registered component for a custom page, or undefined. */
337
- declare function usePageComponent(name: string): ComponentType<Record<string, never>> | undefined;
338
353
  /**
339
354
  * Non-hook-in-loop layout lookup: read the Components registry once, return a
340
355
  * resolver `(key) => NavLayout | undefined`. Lets `NavChrome` resolve a
@@ -446,59 +461,166 @@ interface ResourcePageRouterProps {
446
461
  }
447
462
  /**
448
463
  * Dispatch a resolved `RouteMatch` (from `matchPath`/`useMatch`) to the
449
- * framework component that renders it.
450
- *
451
- * ⚠️ **Scope, precisely** (core-flexibility Task B3.3 — read this before
452
- * trusting any branch without checking). Of the dispatch sites below, only
453
- * THREE are data-connected; the rest are stubs. Treating "renders
454
- * something" as "renders live data" here is exactly what let this
455
- * component's `list` branch render a permanent empty state for the whole
456
- * length of this program while looking, from the outside, like it worked.
457
- *
458
- * REAL — data-connected via `<ResourceListPage>` (self-fetches through
459
- * `useResourceList`; no `data` prop needed):
460
- * - `{ kind: 'resource', action: 'list' }`.
461
- * - `{ kind: 'group-index' }` falling back to a list — no page registered
462
- * for the group's index target, but the target IS a registered
463
- * resource (first-descendant fallback).
464
- *
465
- * There used to be a third entry here, `{ kind: 'singleton' }`, and it was
466
- * DEAD both ways: `matchPath` never produced that kind, so neither half of
467
- * the branch was reachable outside this file's own hand-built matches, and
468
- * the half that "worked" rendered the stub `<ResourceEdit>` below against a
469
- * constant record id. Both it and `ResourceConfig.singleton` are gone — read
470
- * that type for what a one-row screen is expressed with instead.
471
- *
472
- * STILL STUBS — framework GLUE, not data-connected:
473
- * - `show`, `edit`, `create`/`create-wizard`.
474
- * `ResourceShow`/`ResourceEdit`/`ResourceCreate` are prop-driven: they
475
- * take an already-loaded `record` and `onSave`/`onCreate` callbacks
476
- * rather than fetching anything themselves (data fetching is the HOST's
477
- * job — see the demo app's Next.js app-router pages for the
478
- * connected pattern). This router has no data provider to draw on for
479
- * these matches, so it renders the MINIMAL props derivable from the
480
- * match alone: a stub `record={{ id }}` and no-op save/create callbacks.
481
- * A host that wants live data for these routes should render its own
482
- * connected page instead of (or on top of) `<ResourcePageRouter>` — see
483
- * the playground's `_nav-area-page.tsx`, written for exactly that reason
484
- * (predating `<ResourceListPage>`, and — per its own dispatcher — still
485
- * necessary: it feeds the list page an app-level query `meta` and a
486
- * `renderCell` this generic router has no way to know about).
487
- *
488
- * `custom-page`, and `group-index` when a page IS registered, render
489
- * whatever the adopter registered for that slot — they own their own data
490
- * by construction, so "real vs. stub" does not apply to them.
464
+ * framework component that renders it. Every branch is data-connected:
465
+ *
466
+ * - `{ kind: 'resource', action: 'list' }` → `<ResourceListPage>`, which
467
+ * self-fetches through `useResourceList`;
468
+ * - `show`, `edit` and `create` → `<RecordRoute>` (the
469
+ * component `<RecordPage>` renders), which reads with `useOne`, writes with
470
+ * `useUpdateOne`/`useCreateOne`, navigates after a write and renders the
471
+ * record states, breadcrumbs and `?tab=`. A record route whose operation
472
+ * the resource turned off (`operations.show: false`, …) renders the
473
+ * not-found panel;
474
+ * - `{ kind: 'group-index' }` → the group's index page when it has a
475
+ * `body`, else that target's list when it is a resource;
476
+ * - `custom-page` → the page's `body` behind the nav gate (`<PageHost>`: a
477
+ * page whose `visible` or whose group's `visible` rejects it renders
478
+ * not-found and mounts nothing); a page without `body` renders not-found,
479
+ * because the host owns that route.
480
+ *
481
+ * A record route always renders the framework's record page: a different layout is a frame on
482
+ * the screen (`views.<mode>.frame`), never a replaced route (P6-56).
491
483
  */
492
484
  declare function ResourcePageRouter({ match }: ResourcePageRouterProps): ReactElement;
493
485
  /**
494
- * Resolves the active route (`useMatch`) and renders it inside the group-
495
- * chain chrome (`NavChrome`), dispatching to the right framework component
496
- * via `ResourcePageRouter`. No match → a not-found panel. Wrapped in a
497
- * `Suspense` boundary so a lazily-loaded custom page (`React.lazy`) degrades
498
- * to a skeleton inside the chrome instead of blowing past it.
486
+ * Resolves the active route (`useMatch`, from the router adapter) and renders
487
+ * it inside the group-chain chrome (`NavChrome`), dispatching to the right
488
+ * framework component via `ResourcePageRouter`. No match → a not-found panel;
489
+ * no router adapter at all → the same panel, and in development one
490
+ * `console.error` naming `RouterAdapterProvider` (once per app), because
491
+ * then EVERY route is not-found. Wrapped in a `Suspense` boundary so a
492
+ * lazily-loaded custom page (`React.lazy`) degrades to a skeleton inside the
493
+ * chrome.
499
494
  */
500
495
  declare function NavAreaPage(): ReactElement;
501
496
  //#endregion
497
+ //#region src/layout/record-page.d.ts
498
+ /**
499
+ * A record page's resource and action. Inside a record slot both default to
500
+ * the route (`<RecordPage defaults={{ type }} />`); outside one both are
501
+ * required, and a missing one renders not-found with a development error.
502
+ * `defaults` exists on `create` only: show and edit render the record as it
503
+ * is, so the type refuses it there rather than ignoring it.
504
+ */
505
+ type RecordPageProps = {
506
+ readonly resource?: string;
507
+ readonly action: Exclude<ScreenMode, 'create'>;
508
+ readonly defaults?: never;
509
+ } | {
510
+ readonly resource?: string;
511
+ readonly action?: Extract<ScreenMode, 'create'>;
512
+ /**
513
+ * The caller's tier of starting values, above each field's declared
514
+ * default, the matched type's `defaults` and URL prefill.
515
+ */
516
+ readonly defaults?: Record<string, unknown>;
517
+ };
518
+ /**
519
+ * The record page — show, edit or create — wired to data. It reads the id
520
+ * and the group chain from the current route match, reads with `useOne`,
521
+ * renders the record through `<RecordScreen>` (which writes with the
522
+ * resource's update or create), navigates after a write (show when the
523
+ * resource has one, else the list), and renders translated loading,
524
+ * not-found, session-expired and error states. It draws no breadcrumb trail:
525
+ * the header's `<HeaderBreadcrumbs>` does, and shows the record's title once
526
+ * this page has loaded it (the id until then). The open panel of a keyed tabs node lives in the URL as `?<tabsKey>=<panelKey>`.
527
+ *
528
+ * `<ResourcePageRouter>` renders the record route itself; a different layout is a frame on the
529
+ * screen (`views.<mode>.frame`), never a replaced route (P6-56).
530
+ */
531
+ declare function RecordPage(props: RecordPageProps): ReactElement;
532
+ //#endregion
533
+ //#region src/layout/record-breadcrumbs.d.ts
534
+ /**
535
+ * The record trail of the current route, the one `<HeaderBreadcrumbs>` draws
536
+ * there; record and flow pages draw none of their own.
537
+ *
538
+ * On a record route whose operation is available: the group chain, the
539
+ * resource, then the record (show), the record linking to show when show is
540
+ * available plus "Edit" (edit), or "Create" (create). The record crumb is the
541
+ * record's title once `<RecordPage>` has loaded it, and the id while it loads,
542
+ * has failed, or is not mounted at all.
543
+ *
544
+ * On a page-hosted flow's route: the record's edit-shaped trail ending with the
545
+ * flow's label, once the flow page shows its form or its "not available"
546
+ * notice; until then, and on every not-found it answers with, the record's
547
+ * show trail with the id, so a flow the user may not run is never named. A
548
+ * name no flow answers to is the framework 404, with no trail.
549
+ *
550
+ * Nothing on any other route.
551
+ */
552
+ declare function RecordBreadcrumbs(): ReactElement | null;
553
+ //#endregion
554
+ //#region src/record/record-screen.d.ts
555
+ type AnyResourceHandle = ResourceHandle<any, any, any, any>;
556
+ interface RecordScreenProps {
557
+ /** A canonical resource name, or its handle. */
558
+ readonly resource: string | AnyResourceHandle;
559
+ readonly mode: 'show' | 'edit' | 'create';
560
+ /** The record to read (show, edit); not needed when `record` is given. */
561
+ readonly recordId?: string;
562
+ /**
563
+ * The accepted type of a typed create. Without a router, changing it remounts the form for the
564
+ * new type and discards unsaved input without asking for confirmation.
565
+ */
566
+ readonly type?: string;
567
+ /** A row the caller already holds: nothing is read. */
568
+ readonly record?: Readonly<Record<string, unknown>>;
569
+ /** The caller's starting values of a create, above the field defaults. */
570
+ readonly defaults?: Readonly<Record<string, unknown>>;
571
+ /**
572
+ * User input for a create (a search text typed into a relation select): after the first settled
573
+ * plan each key whose field the plan holds `active` is set once, as the user typing it would
574
+ * (the form is dirty); any other key is dropped. Not a default, never trusted.
575
+ */
576
+ readonly input?: Readonly<Record<string, unknown>>;
577
+ /** Replaces the built-in update or create; receives the payload. */
578
+ readonly onSubmit?: (payload: Record<string, unknown>) => Promise<unknown>;
579
+ /** Called after a successful submit with what the write returned. */
580
+ readonly onSuccess?: (result: unknown) => void;
581
+ /** Replaces Cancel's default back navigation. */
582
+ readonly onCancel?: () => void;
583
+ /** The submit button's label. */
584
+ readonly submitLabel?: string;
585
+ /** Rendered after the record's actions in show. */
586
+ readonly extraActions?: ReactNode;
587
+ /**
588
+ * This instance's record frame (code only): used when its slots cover every slot the screen
589
+ * fills, else a development error and the screen's own frame (spec 3.3).
590
+ */
591
+ readonly frame?: FrameHandle<'record'> | AnyFrameHandle;
592
+ }
593
+ /**
594
+ * A record's show, edit or create screen outside a route (P1b+P1c decision
595
+ * 20): the planned screen in its frame (the screen's, the app's, the built-in
596
+ * one, or this instance's `frame`). Reads the record by `recordId` unless
597
+ * `record` is given; writes with the resource's update or create unless
598
+ * `onSubmit` is given. A resource the app does not know renders not-found.
599
+ */
600
+ declare function RecordScreen(props: RecordScreenProps): ReactElement;
601
+ //#endregion
602
+ //#region src/record/frame-pieces.d.ts
603
+ /**
604
+ * The record's title: its `recordTitle` / display field (show, edit), or
605
+ * "Create <label>" in create. Inside `<RecordScreen>` only; no props.
606
+ */
607
+ declare const RecordTitle: () => ReactElement | null;
608
+ /**
609
+ * The record's item actions: the resolved `<ActionOverflow>` of the mode's
610
+ * surface, then `<RecordScreen extraActions>`. Nothing in create or before
611
+ * the record is read.
612
+ */
613
+ declare const RecordActions: () => ReactElement | null;
614
+ /** Copies the record's id to the clipboard. Nothing without an id (create). */
615
+ declare const CopyId: () => ReactElement | null;
616
+ /**
617
+ * Submit and Cancel (edit, create): Submit runs the controller's submit and
618
+ * then `onSuccess`; Cancel calls `onCancel`, else navigates back through the
619
+ * navigation guard. Above them, the form-level messages and a failed write.
620
+ * Nothing in show.
621
+ */
622
+ declare const FormActions: () => ReactElement | null;
623
+ //#endregion
502
624
  //#region src/layout/default-nav-layouts.d.ts
503
625
  /** The built-in content-wrapping layouts. Merge into the app's Components registry. */
504
626
  declare const defaultNavLayouts: Record<string, NavLayout>;
@@ -545,6 +667,8 @@ declare function ContextPicker({ sliceName, label, options, value, onChange, pla
545
667
  /** What the user picked. `'system'` means "no explicit choice" — nothing is
546
668
  * persisted for it; its absence from storage IS the `system` state. */
547
669
  type ThemeChoice = 'light' | 'dark' | 'system';
670
+ /** Every theme choice, in the order a control offers them. */
671
+ declare const THEME_CHOICES: readonly ThemeChoice[];
548
672
  /** What is actually painted. Never `'system'` — every reader (Toaster,
549
673
  * CodeMirror, Mermaid) only ever needs to know light or dark. */
550
674
  type ResolvedTheme = 'light' | 'dark';
@@ -590,14 +714,28 @@ interface UseThemeResult {
590
714
  declare function useTheme(): UseThemeResult;
591
715
  //#endregion
592
716
  //#region src/layout/language-switch.d.ts
717
+ interface LanguageSwitchProps {
718
+ /**
719
+ * Called with the picked locale code AFTER the live locale has switched and
720
+ * the choice has been persisted.
721
+ */
722
+ onChange?: (code: string) => void;
723
+ className?: string;
724
+ }
593
725
  /**
594
- * Default localStorage key. Framework-neutral so multiple adopters on
595
- * the same domain don't collide. Override per-app via the `storageKey`
596
- * prop on `<LanguageSwitch>` and the matching arg on `loadLocale(key)` /
597
- * `saveLocale(key, code)` (or the legacy `loadLanguage`/`saveLanguage` pair
598
- * below).
726
+ * Segmented locale switcher: one button per locale the i18n facade lists
727
+ * (`I18nFacade.locales`, which `createI18nFromRegistry` fills from the
728
+ * registry), each labelled with the locale's own name (`Intl.DisplayNames`).
729
+ * A view over {@link useLanguageSwitch}: picking a locale switches the LIVE
730
+ * locale (no reload) and persists it under `<BuroProvider languageStorageKey>`.
731
+ *
732
+ * Reactivity: the active locale is read live, so the switcher always reflects
733
+ * the CURRENT locale, including a change made elsewhere. `tests/language-switch.test.tsx`
734
+ * guards that nothing here reloads the page.
599
735
  */
600
- declare const DEFAULT_LANGUAGE_STORAGE_KEY = "buro:language";
736
+ declare function LanguageSwitch({ onChange, className }: LanguageSwitchProps): React.ReactElement;
737
+ //#endregion
738
+ //#region src/layout/language-storage.d.ts
601
739
  /**
602
740
  * Legacy RU/EN-only pair. `<LanguageSwitch>` itself no longer uses these —
603
741
  * see its own docblock. New code that needs an arbitrary (not just 'en'/'ru')
@@ -669,142 +807,81 @@ declare function saveLanguage(code: LanguageCode, storageKey?: string): void;
669
807
  declare function loadLocale(storageKey?: string): string | undefined;
670
808
  /** Persist an arbitrary locale code. Generic counterpart to {@link saveLanguage}. */
671
809
  declare function saveLocale(code: string, storageKey?: string): void;
672
- interface LanguageSwitchProps {
673
- /**
674
- * The locale registry to build the switcher from. `registry.locales()`
675
- * is the ENTIRE set of buttons rendered — there is no separate list to
676
- * keep in sync, and no literal locale code anywhere in this component.
677
- * Adding a locale to the registry (a new contribution — see
678
- * `@burojs/i18n-core`'s `LocaleRegistry`) is the whole mechanism for it to
679
- * appear here.
680
- */
681
- registry: LocaleRegistry;
682
- /**
683
- * Namespace to read each locale's `nav.languageName` display name from
684
- * (see {@link localeDisplayName}). Defaults to `'common'` — the same
685
- * default namespace `LocaleRegistry` itself uses.
686
- */
687
- namespace?: string;
688
- className?: string;
689
- /**
690
- * localStorage key to persist the chosen locale under via
691
- * {@link saveLocale}. Defaults to `buro:language`.
692
- */
693
- storageKey?: string;
694
- /**
695
- * Called AFTER the locale is persisted to localStorage, INSTEAD of the
696
- * default live-switch behavior. Pass this if your app needs to do
697
- * something else on a language change (re-key a provider tree, push a
698
- * route); omit it to get the default: switch the live locale via
699
- * `useChangeLocale()`, no reload.
700
- */
701
- onChange?: (code: string) => void;
702
- }
703
- /**
704
- * Locale switcher built entirely from a {@link LocaleRegistry} — any number
705
- * of locales, not a fixed pair. A segmented control with one button per
706
- * `registry.locales()` entry.
707
- *
708
- * Reactivity: the active locale is read live via `useLocale()`
709
- * (`useSyncExternalStore` under the i18n facade's `onLocaleChange`), so this
710
- * component always reflects the CURRENT locale, including a change made
711
- * elsewhere (another switcher instance, a deep link, etc.) — there is no
712
- * local "current" state to fall out of sync. Picking a locale persists the
713
- * choice via `saveLocale` and switches the LIVE locale via
714
- * `useChangeLocale()` — no reload. Adopters who need to do something else
715
- * pass `onChange`, which takes over entirely.
716
- *
717
- * ⚠️ The two-button predecessor of this component used to reload the page on
718
- * every pick, and its docblock used to justify that as necessary because
719
- * "resource labels and the framework's built-in strings are consumed at
720
- * provider-boot registration time." That was true at v0.1 and is not true
721
- * now — the i18n content model made every label render-time. Nothing here
722
- * should reintroduce a reload; `tests/language-switch.test.tsx` guards it.
723
- */
724
- declare function LanguageSwitch({ registry, namespace, className, storageKey, onChange }: LanguageSwitchProps): React.ReactElement;
725
810
  //#endregion
726
811
  //#region src/layout/breadcrumbs.d.ts
727
- interface BreadcrumbsProps {
812
+ type Segment = {
813
+ label: string;
814
+ href?: string;
815
+ };
816
+ interface BreadcrumbsBaseProps {
817
+ className?: string;
728
818
  /**
729
- * Resource name (matches the registry key). When omitted the
730
- * component falls back to `useCurrentResource()` — which only
731
- * resolves when a `ResourceContext.Provider` wraps the tree
732
- * (currently set in tests only). Pass `resource` explicitly from
733
- * resource-aware renderers like `<ResourceShow>` so the breadcrumb
734
- * works in production code paths that don't (yet) provide the
735
- * context.
819
+ * Scopes the trail offers help for, outermost first. Defaults to the resource
820
+ * this breadcrumb is rooted at (none for a trail without a resource).
736
821
  */
737
- resource?: string;
822
+ docScopes?: string[];
738
823
  /**
739
- * Identifier for the current page (for testid only — display label
740
- * comes from `pageLabel`). Typical use: a record id on show/edit
741
- * pages.
824
+ * Segments rendered before the resource segment (or before the page label in
825
+ * a trail without one), each followed by a chevron — the group chain. A
826
+ * segment with an `href` renders as a link; without one, as muted text.
742
827
  */
828
+ leadingSegments?: Segment[];
829
+ /** Optional testid forwarded to the root nav. */
830
+ 'data-testid'?: string;
831
+ }
832
+ /** A trail that ends at, or passes through, a resource: its list link, then the record. */
833
+ interface ResourceBreadcrumbsProps extends BreadcrumbsBaseProps {
834
+ /** Resource name (the registry key). */
835
+ resource: string;
836
+ name?: never;
837
+ /** Identifier for the current page (testid only); a record id on show/edit pages. */
743
838
  recordId?: string;
744
839
  /**
745
- * Display label for the trailing breadcrumb segment (the current
746
- * page). When omitted, only the resource segment is rendered —
747
- * useful when the breadcrumb sits next to a page title that
748
- * already names the record.
840
+ * Display label for the trailing segment (the current page). When omitted
841
+ * the resource segment itself is the current crumb (a list page).
749
842
  */
750
843
  pageLabel?: string;
751
- className?: string;
752
- /**
753
- * Scopes the trail offers help for, outermost first. Defaults to the resource
754
- * this breadcrumb is rooted at. Pass a longer list when the trail crosses
755
- * several documented resources — the control then offers all of them, which
756
- * is the thing a per-entity icon structurally cannot do.
757
- */
758
- docScopes?: string[];
759
844
  /**
760
- * Optional segments rendered BEFORE the resource segment (each followed by a
761
- * chevron). Use for a grouping prefix such as a sidebar-section label — e.g.
762
- * a per-type document detail page roots its breadcrumb at
763
- * `Документы по типу › Заказы на производство › #id`. A segment with an `href`
764
- * renders as a link; without one it renders as muted static text. Undefined
765
- * (the default) → the breadcrumb starts at the resource segment, unchanged.
845
+ * Segments rendered between the resource link and the trailing page label —
846
+ * the record on an edit page («Documents › live-one › Edit»). A segment
847
+ * without `href` renders as plain text.
766
848
  */
767
- leadingSegments?: Array<{
768
- label: string;
769
- href?: string;
770
- }>;
771
- /**
772
- * Segments rendered BETWEEN the resource link and the trailing page label.
773
- *
774
- * `leadingSegments` sits ABOVE the resource (a section), which is wrong for
775
- * anything that belongs to it — putting the record there produced
776
- * «live-one › Документы › Изменение». An edit page needs
777
- * «Документы › live-one › Изменение», i.e. the record nested under its
778
- * resource. A segment without `href` renders as plain text, for records whose
779
- * resource has no standalone show page.
780
- */
781
- middleSegments?: Array<{
782
- label: string;
783
- href?: string;
784
- }>;
785
- /** Optional testid forwarded to the root nav. */
786
- 'data-testid'?: string;
849
+ middleSegments?: Segment[];
787
850
  }
851
+ /** A trail with no resource segment: a custom page or a group index. */
852
+ interface PageBreadcrumbsProps extends BreadcrumbsBaseProps {
853
+ resource?: never;
854
+ /** The test-id name of the current crumb: the custom page's or the group's key. */
855
+ name: string;
856
+ /** The current page's label, the trail's last crumb. */
857
+ pageLabel: string;
858
+ recordId?: never;
859
+ middleSegments?: never;
860
+ }
861
+ type BreadcrumbsProps = ResourceBreadcrumbsProps | PageBreadcrumbsProps;
862
+ /**
863
+ * The navigation breadcrumb, in two shapes. With `resource`: the leading
864
+ * segments, the resource (a link to its canonical, nesting-aware list href,
865
+ * or the current crumb on a bare list), the middle segments and the page
866
+ * label. Without: the leading segments and the page label only, test ids
867
+ * named by `name`. The last crumb is always the "you are here" marker — an
868
+ * emphasised, non-linked span with `aria-current="page"`. Every crumb carries
869
+ * its label as a `title` tooltip, so a truncated one can still be read.
870
+ */
871
+ declare function Breadcrumbs(props: BreadcrumbsProps): ReactElement | null;
872
+ //#endregion
873
+ //#region src/layout/header-breadcrumbs.d.ts
788
874
  /**
789
- * Compact navigation breadcrumb for resource detail pages. Adapts the
790
- * canonical shadcn `Breadcrumb` pattern onto our existing `<a>` +
791
- * Tabler primitives — we don't ship the full
792
- * `Breadcrumb`/`BreadcrumbList`/`BreadcrumbPage` factory because the
793
- * admin breadcrumb is always a 1- or 2-segment shape (resource → page)
794
- * keyed off the registry's current resource. Adopters that need
795
- * deeper hierarchies can compose `<a>` + `<IconChevronRight>` directly.
796
- *
797
- * Resource segment links to the canonical `hrefFor(registry, def.name)`
798
- * (nesting-aware — resolves to a group-prefixed path for grouped resources).
799
- * Trailing
800
- * `pageLabel` segment renders as a non-link `<span>` to mark "you
801
- * are here". Resource label resolves to
802
- * `def.label?.plural ?? humanizeKey(def.name)` so registered labels
803
- * propagate naturally — the prior implementation showed the raw
804
- * resource id which read poorly for resources like
805
- * `reference_locations`.
806
- */
807
- declare function Breadcrumbs({ resource, recordId, pageLabel, leadingSegments, middleSegments, className, docScopes, 'data-testid': testId }: BreadcrumbsProps): React.ReactElement | null;
875
+ * The page's one breadcrumb trail, and `<AppHeader>`'s default `breadcrumbs`:
876
+ * the group chain and the resource, custom page or group — and on a record
877
+ * route or a page-hosted flow's route the record trail (`<RecordBreadcrumbs>`,
878
+ * `data-testid="record-breadcrumbs"`), which ends with the record's title once
879
+ * the page has loaded it. Record and flow pages draw no trail of their own, so
880
+ * a page never shows two. Nothing — silently — without a match: a sidebar or
881
+ * header rendered in a story with no router is not a misconfiguration, and
882
+ * `<NavAreaPage>`'s own message is the one that names the fix.
883
+ */
884
+ declare function HeaderBreadcrumbs(): ReactElement | null;
808
885
  //#endregion
809
886
  //#region src/layout/command-palette.d.ts
810
887
  /**
@@ -860,11 +937,14 @@ declare function CommandPalette({ open, onOpenChange, children, placeholder, emp
860
937
  //#endregion
861
938
  //#region src/layout/command-palette-trigger.d.ts
862
939
  interface CommandPaletteTriggerProps {
863
- /** Opens the palette. Wire to `useCommandPalette().setOpen(true)`. */
864
- onOpen: () => void;
940
+ /**
941
+ * Opens the palette. Defaults to the `ShellProvider`'s `openPalette`; with
942
+ * neither, the trigger renders nothing.
943
+ */
944
+ onOpen?: () => void;
865
945
  /** Shortcut hint rendered in the `<kbd>` chip. Defaults to `⌘K`. */
866
946
  shortcut?: string;
867
- /** `data-testid` for the trigger button. */
947
+ /** `data-testid` for the trigger button. Default `'open-palette'`. */
868
948
  testid?: string;
869
949
  className?: string;
870
950
  }
@@ -877,7 +957,7 @@ interface CommandPaletteTriggerProps {
877
957
  * (`commandPalette.trigger` / `.triggerHint`) instead of hardcoded English.
878
958
  * Falls back to English when rendered outside a `BuroProvider`.
879
959
  */
880
- declare function CommandPaletteTrigger({ onOpen, shortcut, testid, className }: CommandPaletteTriggerProps): React.ReactElement;
960
+ declare function CommandPaletteTrigger({ onOpen, shortcut, testid, className }: CommandPaletteTriggerProps): React.ReactElement | null;
881
961
  //#endregion
882
962
  //#region src/layout/command-palette-navigation-groups.d.ts
883
963
  /**
@@ -902,24 +982,14 @@ type CommandPaletteNavigationTestIdRole = 'resource' | 'page' | 'section' | 'gro
902
982
  * `useVisibilityCtx()` (mirrors the sidebar's filter logic so an entry
903
983
  * hidden in the sidebar stays hidden in the palette).
904
984
  *
905
- * Navigation is router-agnostic: adopters pass a `navigate` callback
906
- * (typically `(href) => router.push(href)` from their app router). When
907
- * omitted, the component falls back to a full-document navigation via
908
- * `window.location.assign(href)` so it remains usable outside Next /
909
- * react-router shells; SPA shells should always supply `navigate` to
910
- * keep client-side routing intact.
985
+ * Navigation goes through `useRouteNavigate` — the `RouterAdapter` the host
986
+ * mounts above `<BuroProvider>` — so picks stay client-side with nothing to
987
+ * wire.
911
988
  */
912
989
  interface CommandPaletteNavigationGroupsProps {
913
990
  /**
914
- * Called to navigate to the picked entry's `href`. Wire this to your
915
- * app router (`router.push` for Next App Router, `navigate(to)` for
916
- * react-router, etc.). Optional — when omitted the component falls
917
- * back to `window.location.assign(href)` (full reload).
918
- */
919
- navigate?: (href: string) => void;
920
- /**
921
- * Called after the user picks a navigation entry, BEFORE `navigate`
922
- * fires. Adopters typically wire this to `setOpen(false)` to close
991
+ * Called after the user picks a navigation entry, BEFORE the
992
+ * navigation. Adopters typically wire this to `setOpen(false)` to close
923
993
  * the palette before the route change. Optional.
924
994
  */
925
995
  onSelect?: () => void;
@@ -939,7 +1009,7 @@ interface CommandPaletteNavigationGroupsProps {
939
1009
  *
940
1010
  * Visibility filtering mirrors `<Sidebar>` (visible predicates evaluated
941
1011
  * against `useVisibilityCtx()`, fail-closed for throwing predicates,
942
- * embedded subresources hidden from navigation entirely). An entry hidden
1012
+ * resources with no list page hidden from navigation entirely). An entry hidden
943
1013
  * in the sidebar is also hidden in the palette — that parity is the
944
1014
  * primary contract of this component.
945
1015
  *
@@ -967,7 +1037,6 @@ interface CommandPaletteNavigationGroupsProps {
967
1037
  *
968
1038
  * ```tsx
969
1039
  * 'use client';
970
- * import { useRouter } from 'next/navigation';
971
1040
  * import { useCommandPalette } from '@burojs/react';
972
1041
  * import {
973
1042
  * CommandPalette,
@@ -975,12 +1044,10 @@ interface CommandPaletteNavigationGroupsProps {
975
1044
  * } from '@burojs/ui-shadcn';
976
1045
  *
977
1046
  * function Shell() {
978
- * const router = useRouter();
979
1047
  * const { open, setOpen } = useCommandPalette();
980
1048
  * return (
981
1049
  * <CommandPalette open={open} onOpenChange={setOpen}>
982
1050
  * <CommandPaletteNavigationGroups
983
- * navigate={(href) => router.push(href)}
984
1051
  * onSelect={() => setOpen(false)}
985
1052
  * />
986
1053
  * </CommandPalette>
@@ -988,7 +1055,7 @@ interface CommandPaletteNavigationGroupsProps {
988
1055
  * }
989
1056
  * ```
990
1057
  */
991
- declare function CommandPaletteNavigationGroups({ navigate, onSelect, getTestId }: CommandPaletteNavigationGroupsProps): React.ReactElement | null;
1058
+ declare function CommandPaletteNavigationGroups({ onSelect, getTestId }: CommandPaletteNavigationGroupsProps): React.ReactElement | null;
992
1059
  //#endregion
993
1060
  //#region src/layout/command-palette-recent-group.d.ts
994
1061
  /**
@@ -996,35 +1063,21 @@ declare function CommandPaletteNavigationGroups({ navigate, onSelect, getTestId
996
1063
  * `useTestIds` pattern used by `<CommandPaletteNavigationGroups>`.
997
1064
  *
998
1065
  * - `recent` — a recently-visited record entry. `id` passed to the
999
- * builder is `<resource>:<record-id>`.
1066
+ * builder is `<canonical resource>:<record-id>`.
1000
1067
  */
1001
1068
  type CommandPaletteRecentTestIdRole = 'recent';
1002
1069
  /**
1003
1070
  * Props for `<CommandPaletteRecentGroup>`.
1004
1071
  *
1005
- * The component reads the recent list from `localStorage` (via
1006
- * `getRecentRecords`) and renders one `<CommandItem>` per entry. It
1007
- * re-reads on every mount + whenever its `userId` prop changes —
1008
- * single-tab consistency is preserved because the palette dialog
1009
- * tears down + remounts each time it opens.
1072
+ * The component reads the signed-in user's recent list (the records
1073
+ * `<RecordPage>` recorded as it read them) and renders one `<CommandItem>`
1074
+ * per entry. It re-reads on every mount, when the user changes and when
1075
+ * `refreshKey` changes — single-tab consistency is preserved because the
1076
+ * palette dialog tears down + remounts each time it opens.
1010
1077
  */
1011
1078
  interface CommandPaletteRecentGroupProps {
1012
1079
  /**
1013
- * Caller-supplied user id. Multi-user installs pass `auth.user?.id`;
1014
- * single-user / unauthenticated adopters omit and share a `'singleton'`
1015
- * bucket. The list is keyed by user so signing out + back in as a
1016
- * different user doesn't leak the previous user's history.
1017
- */
1018
- userId?: string | null;
1019
- /**
1020
- * Called to navigate to the picked record's URL. Wire this to your
1021
- * app router (`router.push` for Next App Router, `navigate(to)` for
1022
- * react-router, etc.). Optional — when omitted the component falls
1023
- * back to `window.location.assign(href)` (full reload).
1024
- */
1025
- navigate?: (href: string) => void;
1026
- /**
1027
- * Called BEFORE `navigate` fires. Adopters typically wire this to
1080
+ * Called BEFORE the navigation. Adopters typically wire this to
1028
1081
  * `setOpen(false)` to close the palette during the route change.
1029
1082
  */
1030
1083
  onSelect?: () => void;
@@ -1039,17 +1092,11 @@ interface CommandPaletteRecentGroupProps {
1039
1092
  * explicit string to override.
1040
1093
  */
1041
1094
  heading?: string;
1042
- /**
1043
- * Override the path generator. By default the component navigates
1044
- * to `/<resource>/<id>` — adopters with custom routes (e.g. nested
1045
- * subresources) can override.
1046
- */
1047
- recordHref?: (entry: RecentRecord) => string;
1048
1095
  /**
1049
1096
  * Re-read trigger. When this prop changes the component re-reads
1050
1097
  * `localStorage` (e.g. flip from `false` → `true` on palette open).
1051
- * Optional — when omitted the component reads only on mount /
1052
- * `userId` change, which is sufficient for the typical "palette
1098
+ * Optional — when omitted the component reads only on mount and when
1099
+ * the user changes, which is sufficient for the typical "palette
1053
1100
  * dialog re-mounts on each open" use case.
1054
1101
  */
1055
1102
  refreshKey?: unknown;
@@ -1063,10 +1110,10 @@ interface CommandPaletteRecentGroupProps {
1063
1110
  withSeparator?: boolean;
1064
1111
  }
1065
1112
  /**
1066
- * Render the "Recent" group inside a command palette: the last N
1067
- * record-show URLs the current user visited, populated by adopter
1068
- * code calling `useTrackRecentRecord(resource, record)` on the
1069
- * record show / edit pages.
1113
+ * Render the "Recent" group inside a command palette: the last records the
1114
+ * signed-in user opened. `<RecordPage>` records each show or edit visit
1115
+ * from the record it read — there is nothing to wire, and nothing is
1116
+ * recorded or shown without a signed-in user.
1070
1117
  *
1071
1118
  * Visibility:
1072
1119
  *
@@ -1074,18 +1121,18 @@ interface CommandPaletteRecentGroupProps {
1074
1121
  * "Recent" heading on a fresh user). Adopters that want a permanent
1075
1122
  * slot (with an empty-state row) can fork this component — the
1076
1123
  * surface is small.
1077
- * - Resource label resolution: looks up the entry's `resource` in
1078
- * `app.registry`, falls back to `humanizeKey(resource)` if the
1079
- * resource has been deleted from the registry. The recent entry
1080
- * still navigates — server-side 404 is the user's signal that the
1081
- * record / resource is gone.
1124
+ * - An entry opens the record's show route, else its edit route, built
1125
+ * from the registry. An entry whose resource the registry no longer
1126
+ * has, whose resource's `visible` now hides it from the user, or which
1127
+ * has neither route, is not rendered.
1082
1128
  *
1083
- * Example (router-independent — see `useRouterAdapter`, the same seam
1084
- * `useResourceUrlState` uses; wrap the app in `RouterAdapterProvider` once):
1129
+ * Example (router-independent — picks navigate through `useRouteNavigate`,
1130
+ * the `RouterAdapter` seam `useResourceUrlState` also uses; wrap the app in
1131
+ * `RouterAdapterProvider` once):
1085
1132
  *
1086
1133
  * ```tsx
1087
1134
  * 'use client';
1088
- * import { useRouterAdapter, useCommandPalette, useApp } from '@burojs/react';
1135
+ * import { useCommandPalette } from '@burojs/react';
1089
1136
  * import {
1090
1137
  * CommandPalette,
1091
1138
  * CommandPaletteRecentGroup,
@@ -1094,18 +1141,15 @@ interface CommandPaletteRecentGroupProps {
1094
1141
  * } from '@burojs/ui-shadcn';
1095
1142
  *
1096
1143
  * function Shell() {
1097
- * const router = useRouterAdapter();
1098
1144
  * const { open, setOpen } = useCommandPalette();
1099
1145
  * return (
1100
1146
  * <CommandPalette open={open} onOpenChange={setOpen}>
1101
1147
  * <CommandPaletteRecentGroup
1102
- * navigate={(href) => router.push(href)}
1103
1148
  * onSelect={() => setOpen(false)}
1104
1149
  * refreshKey={open}
1105
1150
  * />
1106
1151
  * <CommandSeparator />
1107
1152
  * <CommandPaletteNavigationGroups
1108
- * navigate={(href) => router.push(href)}
1109
1153
  * onSelect={() => setOpen(false)}
1110
1154
  * />
1111
1155
  * </CommandPalette>
@@ -1113,7 +1157,7 @@ interface CommandPaletteRecentGroupProps {
1113
1157
  * }
1114
1158
  * ```
1115
1159
  */
1116
- declare function CommandPaletteRecentGroup({ userId, navigate, onSelect, getTestId, heading, recordHref, refreshKey, withSeparator }: CommandPaletteRecentGroupProps): React.ReactElement | null;
1160
+ declare function CommandPaletteRecentGroup({ onSelect, getTestId, heading, refreshKey, withSeparator }: CommandPaletteRecentGroupProps): React.ReactElement | null;
1117
1161
  //#endregion
1118
1162
  //#region src/layout/command-palette-records-group.d.ts
1119
1163
  /**
@@ -1162,14 +1206,7 @@ interface CommandPaletteRecordsGroupProps {
1162
1206
  */
1163
1207
  debounceMs?: number;
1164
1208
  /**
1165
- * Called to navigate to the picked record's URL. Wire this to your
1166
- * app router (`router.push` for Next App Router, `navigate(to)` for
1167
- * react-router, etc.). Optional — when omitted the component falls
1168
- * back to `window.location.assign(href)` (full reload).
1169
- */
1170
- navigate?: (href: string) => void;
1171
- /**
1172
- * Called BEFORE `navigate` fires. Adopters typically wire this to
1209
+ * Called BEFORE the navigation. Adopters typically wire this to
1173
1210
  * `setOpen(false)` to close the palette during the route change.
1174
1211
  */
1175
1212
  onSelect?: () => void;
@@ -1207,7 +1244,7 @@ interface CommandPaletteRecordsGroupProps {
1207
1244
  * - Each resource hit is prefixed with the resource's label so an
1208
1245
  * ambiguous query (matching multiple resources) stays disambiguated.
1209
1246
  * - On pick: calls `onSelect` (typical: close the palette) THEN
1210
- * `navigate(href)`.
1247
+ * navigates to the record.
1211
1248
  *
1212
1249
  * Cancellation:
1213
1250
  *
@@ -1224,12 +1261,13 @@ interface CommandPaletteRecordsGroupProps {
1224
1261
  * that calls `useList` exactly once per render. The sub-component count
1225
1262
  * is stable, so no conditional/loop hook is created.
1226
1263
  *
1227
- * Example (router-independent — see `useRouterAdapter`, the same seam
1228
- * `useResourceUrlState` uses; wrap the app in `RouterAdapterProvider` once):
1264
+ * Example (router-independent — picks navigate through `useRouteNavigate`,
1265
+ * the `RouterAdapter` seam `useResourceUrlState` also uses; wrap the app in
1266
+ * `RouterAdapterProvider` once):
1229
1267
  *
1230
1268
  * ```tsx
1231
1269
  * 'use client';
1232
- * import { useRouterAdapter, useCommandPalette } from '@burojs/react';
1270
+ * import { useCommandPalette } from '@burojs/react';
1233
1271
  * import {
1234
1272
  * CommandPalette,
1235
1273
  * CommandPaletteRecordsGroup,
@@ -1237,17 +1275,14 @@ interface CommandPaletteRecordsGroupProps {
1237
1275
  * } from '@burojs/ui-shadcn';
1238
1276
  *
1239
1277
  * function Shell() {
1240
- * const router = useRouterAdapter();
1241
1278
  * const { open, setOpen } = useCommandPalette();
1242
1279
  * return (
1243
1280
  * <CommandPalette open={open} onOpenChange={setOpen}>
1244
1281
  * <CommandPaletteRecordsGroup
1245
1282
  * enabled={open}
1246
- * navigate={(href) => router.push(href)}
1247
1283
  * onSelect={() => setOpen(false)}
1248
1284
  * />
1249
1285
  * <CommandPaletteNavigationGroups
1250
- * navigate={(href) => router.push(href)}
1251
1286
  * onSelect={() => setOpen(false)}
1252
1287
  * />
1253
1288
  * </CommandPalette>
@@ -1255,7 +1290,7 @@ interface CommandPaletteRecordsGroupProps {
1255
1290
  * }
1256
1291
  * ```
1257
1292
  */
1258
- declare function CommandPaletteRecordsGroup({ enabled, pageSize, minQueryLength, debounceMs, navigate, onSelect, getTestId, heading, recordHref }: CommandPaletteRecordsGroupProps): React.ReactElement | null;
1293
+ declare function CommandPaletteRecordsGroup({ enabled, pageSize, minQueryLength, debounceMs, onSelect, getTestId, heading, recordHref }: CommandPaletteRecordsGroupProps): React.ReactElement | null;
1259
1294
  //#endregion
1260
1295
  //#region src/layout/command-palette-actions-group.d.ts
1261
1296
  /**
@@ -1279,8 +1314,8 @@ type CommandPaletteActionsTestIdRole = 'action';
1279
1314
  * about WHICH actions appear: it also reads `useAppActions()` and renders
1280
1315
  * those alongside the prop list — see the component's own docblock for the
1281
1316
  * merge order. Each action has a single activation path: either `href`
1282
- * (delegates to `navigate(href)`) OR `onRun` (custom handler). When both
1283
- * are provided `href` wins.
1317
+ * (navigates through `useRouteNavigate`) OR `onRun` (custom handler). When
1318
+ * both are provided `href` wins.
1284
1319
  */
1285
1320
  interface ActionItem {
1286
1321
  /** Stable id used for the cmdk `value` and the optional `getTestId` callback. */
@@ -1304,11 +1339,12 @@ interface ActionItem {
1304
1339
  */
1305
1340
  onRun?: () => void | Promise<void>;
1306
1341
  /**
1307
- * Optional href shortcut: when set, the component runs
1308
- * `navigate(href)` (passed through the same `onSelect` → close →
1309
- * navigate flow used elsewhere in the palette) and `onRun` is not
1310
- * called. Use this for actions that are really just navigation
1311
- * shortcuts (e.g. `"Create new <resource>"`).
1342
+ * Optional href shortcut: when set, the component navigates to it
1343
+ * through `useRouteNavigate` (the same navigate → `onSelect` → close
1344
+ * flow used elsewhere in the palette) and `onRun` is not called. An
1345
+ * external href (`https:`, `mailto:`) opens in a new window instead.
1346
+ * Use this for actions that are really just navigation shortcuts
1347
+ * (e.g. `"Create new <resource>"`).
1312
1348
  */
1313
1349
  href?: string;
1314
1350
  /**
@@ -1356,14 +1392,7 @@ interface CommandPaletteActionsGroupProps {
1356
1392
  */
1357
1393
  actions?: ActionItem[];
1358
1394
  /**
1359
- * Called to navigate when an action's `href` is set. Wire this to
1360
- * your app router (`router.push` for Next App Router, etc.). When
1361
- * omitted the component falls back to `window.location.assign(href)`
1362
- * (full reload). SPAs should always supply `navigate`.
1363
- */
1364
- navigate?: (href: string) => void;
1365
- /**
1366
- * Called AFTER the action's handler resolves (or `navigate(href)`
1395
+ * Called AFTER the action's handler resolves (or the navigation
1367
1396
  * fires). Adopters typically wire this to `setOpen(false)` so the
1368
1397
  * palette closes once the user has picked an action.
1369
1398
  */
@@ -1408,8 +1437,8 @@ interface CommandPaletteActionsGroupProps {
1408
1437
  * - Errors thrown / rejected from `onRun` are caught + logged via
1409
1438
  * `console.error` and the palette still closes — the user is never
1410
1439
  * stuck on an overlay after a misbehaving handler.
1411
- * - Actions with `href` set bypass `onRun` and delegate to
1412
- * `navigate(href)` (or `window.location.assign` as a fallback).
1440
+ * - Actions with `href` set bypass `onRun` and navigate through
1441
+ * `useRouteNavigate`; an external href opens in a new window.
1413
1442
  * - A registry entry the rule engine allowed-but-disabled (`disabled: true`)
1414
1443
  * stays keyboard-reachable and dimmed (`opacity-60`), and selecting it
1415
1444
  * shows its `disabledReason` via `toast` instead of running the action —
@@ -1436,12 +1465,11 @@ interface CommandPaletteActionsGroupProps {
1436
1465
  * keywords: 'logout sign out',
1437
1466
  * },
1438
1467
  * ]}
1439
- * navigate={(href) => router.push(href)}
1440
1468
  * onSelect={() => setOpen(false)}
1441
1469
  * />
1442
1470
  * ```
1443
1471
  */
1444
- declare function CommandPaletteActionsGroup({ actions, navigate, onSelect, getTestId, heading }: CommandPaletteActionsGroupProps): React.ReactElement | null;
1472
+ declare function CommandPaletteActionsGroup({ actions, onSelect, getTestId, heading }: CommandPaletteActionsGroupProps): React.ReactElement | null;
1445
1473
  //#endregion
1446
1474
  //#region src/layout/command-palette-create-group.d.ts
1447
1475
  /**
@@ -1458,19 +1486,12 @@ type CommandPaletteCreateTestIdRole = 'create';
1458
1486
  interface CommandPaletteCreateGroupProps {
1459
1487
  /**
1460
1488
  * Override which resources expose a Create entry. Default: every
1461
- * non-embedded resource that passes its `visible(ctx)` predicate.
1489
+ * resource with a list page that passes its `visible(ctx)` predicate.
1462
1490
  * Adopters who want to hide a specific resource's Create action
1463
1491
  * (e.g. read-only views, demo-only resources) supply a predicate
1464
1492
  * that returns `false` for that resource.
1465
1493
  */
1466
1494
  resourceFilter?: (def: ResourceDef) => boolean;
1467
- /**
1468
- * Called to navigate to `/${resource}/create`. Wire this to your
1469
- * app router (`router.push` for Next App Router, etc.). When omitted
1470
- * the component falls back to `window.location.assign(href)` (full
1471
- * reload). SPAs should always supply `navigate`.
1472
- */
1473
- navigate?: (href: string) => void;
1474
1495
  /**
1475
1496
  * Called AFTER the navigation fires. Adopters typically wire this
1476
1497
  * to `setOpen(false)` so the palette closes once the user has
@@ -1491,7 +1512,7 @@ interface CommandPaletteCreateGroupProps {
1491
1512
  /**
1492
1513
  * Override the path generator. By default each item navigates to
1493
1514
  * `/<resource>/create`. Adopters with custom create routes
1494
- * (e.g. wizards, modals) override per-resource.
1515
+ * (e.g. a dedicated flow, a modal) override per-resource.
1495
1516
  */
1496
1517
  createHref?: (def: ResourceDef) => string;
1497
1518
  /**
@@ -1504,14 +1525,12 @@ interface CommandPaletteCreateGroupProps {
1504
1525
  /**
1505
1526
  * Render the per-resource quick-action group inside a command
1506
1527
  * palette: one `"Create new <resource>"` item per registered
1507
- * (non-embedded, visible) resource.
1528
+ * visible resource with a list page.
1508
1529
  *
1509
1530
  * Filters applied (in order):
1510
1531
  *
1511
- * 1. `parent.embedded === true` resources are excluded — they don't
1512
- * have a standalone create route in the framework's routing
1513
- * convention; their create flow lives inside the parent's show
1514
- * page tab.
1532
+ * 1. Resources with no list page (`views.list.page: false`) are
1533
+ * excluded — they are reached through a parent's placement.
1515
1534
  * 2. `evaluateVisible(def.visible, visibilityCtx)` must return true.
1516
1535
  * Same predicate the sidebar + navigation group use, fail-closed
1517
1536
  * on throwing predicates. Hidden in sidebar → hidden here.
@@ -1525,13 +1544,12 @@ interface CommandPaletteCreateGroupProps {
1525
1544
  *
1526
1545
  * ```tsx
1527
1546
  * <CommandPaletteCreateGroup
1528
- * navigate={(href) => router.push(href)}
1529
1547
  * onSelect={() => setOpen(false)}
1530
1548
  * getTestId={(role, name) => `command-palette-${role}:${name}`}
1531
1549
  * />
1532
1550
  * ```
1533
1551
  */
1534
- declare function CommandPaletteCreateGroup({ resourceFilter, navigate, onSelect, getTestId, heading, createHref, prefix }: CommandPaletteCreateGroupProps): React.ReactElement | null;
1552
+ declare function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, heading, createHref, prefix }: CommandPaletteCreateGroupProps): React.ReactElement | null;
1535
1553
  //#endregion
1536
1554
  //#region src/layout/link-component.d.ts
1537
1555
  /**
@@ -1551,67 +1569,31 @@ type LinkComponentProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>
1551
1569
  children?: ReactNode;
1552
1570
  };
1553
1571
  type LinkComponent = ComponentType<LinkComponentProps>;
1554
- /**
1555
- * Imperative client-side navigation. Mirrors the `<Link>` mechanism for the
1556
- * cases a component must navigate from an event handler (e.g. a whole-row click
1557
- * on a list, #4) rather than render an anchor. The default performs a full-page
1558
- * `window.location.assign`; adopters install their router's push
1559
- * (`next/router`'s `router.push`, react-router's `navigate`) via
1560
- * `<LinkComponentProvider navigate={...}>` so it stays client-side.
1561
- */
1562
- type NavigateFn = (href: string) => void;
1563
- /**
1564
- * Read the imperative navigate function. Use for programmatic navigation from an
1565
- * event handler; prefer `useLinkComponent()` (a real anchor) whenever the target
1566
- * can be rendered as a link, since anchors give Cmd/Ctrl-click, middle-click and
1567
- * right-click "open in new tab" for free.
1568
- */
1569
- declare function useNavigate(): NavigateFn;
1570
- /**
1571
- * Read the app's current pathname. Supplied by `LinkComponentProvider`
1572
- * (`currentPath` prop, e.g. Next's `usePathname()` or react-router's
1573
- * `useLocation().pathname`). Defaults to '' when no provider is present
1574
- * (SSR-safe). This is the read side of the router seam — `useMatch` uses it
1575
- * to resolve the active route via `matchPath`.
1576
- */
1577
- declare function useCurrentPath(): string;
1578
1572
  interface LinkComponentProviderProps {
1579
1573
  /**
1580
- * The router-aware Link component to use throughout
1581
- * `@burojs/ui-shadcn` for internal navigation. Pass `Link` from
1582
- * `next/link` for Next.js App Router; wrap your router's link
1583
- * primitive once for react-router / TanStack Router.
1574
+ * The router-aware Link component to use throughout `@burojs/ui-shadcn` for
1575
+ * internal navigation. Pass `Link` from `next/link` for Next.js App Router;
1576
+ * wrap your router's link primitive once for react-router / TanStack Router.
1584
1577
  */
1585
1578
  linkComponent: LinkComponent;
1586
- /**
1587
- * Optional imperative navigate (client-side) for handler-driven navigation
1588
- * such as whole-row clicks. Defaults to a full-page `window.location.assign`.
1589
- */
1590
- navigate?: NavigateFn;
1591
- /**
1592
- * The app's current pathname; feed `usePathname()` (Next) or
1593
- * `useLocation().pathname` (react-router).
1594
- */
1595
- currentPath?: string;
1596
1579
  children: ReactNode;
1597
1580
  }
1598
1581
  /**
1599
- * Install a router-aware `LinkComponent` for every framework component
1600
- * that renders an internal navigation link (Sidebar entries, action
1601
- * links, relation cells, list-empty Create CTA, etc.). Without this
1602
- * provider the framework falls back to a raw `<a>` — works correctly
1603
- * but causes a full document reload on every click, which defeats the
1604
- * SPA experience a client-side router (Next.js, react-router) is meant
1605
- * to provide.
1582
+ * Install a router-aware `LinkComponent` for every framework component that
1583
+ * renders an internal navigation link (sidebar entries, action links, relation
1584
+ * cells, the list-empty Create CTA, …). Without it the framework falls back to
1585
+ * a raw `<a>` — correct, but a full document reload on every click.
1606
1586
  *
1607
- * Wire-up:
1587
+ * This is a link component and nothing else: the location and imperative
1588
+ * navigation are the `RouterAdapter`'s (`RouterAdapterProvider` and
1589
+ * `useRouteNavigate` from `@burojs/react`), the one router seam.
1608
1590
  *
1609
1591
  * import Link from 'next/link';
1610
1592
  * <LinkComponentProvider linkComponent={Link}>
1611
1593
  * <App />
1612
1594
  * </LinkComponentProvider>
1613
1595
  */
1614
- declare function LinkComponentProvider({ linkComponent, navigate, currentPath, children }: LinkComponentProviderProps): ReactElement;
1596
+ declare function LinkComponentProvider({ linkComponent, children }: LinkComponentProviderProps): ReactElement;
1615
1597
  /**
1616
1598
  * Read the current `LinkComponent`. Call this from any framework
1617
1599
  * component that needs to render an internal-navigation link, then
@@ -1629,52 +1611,87 @@ declare function useLinkComponent(): LinkComponent;
1629
1611
  /**
1630
1612
  * The app-scoped visibility predicate passed to the pure core resolvers so
1631
1613
  * that group-index → first-visible-child resolution and (later) chrome item
1632
- * lists honor `visible(ctx)`. Resources and groups consult `evaluateVisible`
1633
- * against the current visibility context; custom pages have no predicate and
1634
- * are always visible.
1614
+ * lists honor `visible(ctx)`. Resources, groups and custom pages consult
1615
+ * `evaluateVisible` against the current visibility context; a node without a
1616
+ * predicate is visible. The custom-page route gates on it too (`<PageHost>`).
1635
1617
  */
1636
1618
  declare function useNavVisible(): (node: NavNode) => boolean;
1637
1619
  /** Canonical href for a resource / custom page / group. Nesting-independent. */
1638
1620
  declare function useHref(name: string, opts?: Omit<HrefOpts, 'isVisible'>): string;
1639
- /** Resolve the active route from the current pathname, or null. */
1621
+ /**
1622
+ * Resolve the active route from the router adapter's pathname, or null. The
1623
+ * router adapter is the framework's only source of the location; without one
1624
+ * there is no location, so there is no match.
1625
+ */
1640
1626
  declare function useMatch(): RouteMatch | null;
1641
1627
  //#endregion
1642
- //#region src/layout/navigation-guard.d.ts
1628
+ //#region src/layout/navigation-guard-dialog.d.ts
1643
1629
  /**
1644
- * Arm/disarm the navigation guard from a component. Pass `true` while the
1645
- * component holds unsaved changes (e.g. a dirty form). No-op when no
1646
- * `NavigationGuardProvider` is mounted above.
1630
+ * The unsaved-changes guard's "leave?" dialog. The guard itself — which clicks
1631
+ * it holds, `beforeunload`, the back-button sentinel — is `@burojs/react`'s and
1632
+ * mounted by `<BuroProvider>`; this component registers itself as the guard's
1633
+ * dialog while mounted and answers the navigation the guard is holding.
1634
+ * `<AppLayout>` renders one, so an app on the layout mounts nothing; an app
1635
+ * without it renders this once inside `<BuroProvider>`. With no dialog
1636
+ * registered the guard asks through `window.confirm`.
1637
+ *
1638
+ * A second mounted dialog stays closed: the guard hands a held navigation to
1639
+ * the most recently registered dialog only, and back to the previous one when
1640
+ * that one unmounts.
1647
1641
  */
1648
- declare function useNavigationBlocker(active: boolean): void;
1649
- declare function NavigationGuardProvider({ children }: {
1650
- children: ReactNode;
1651
- }): React.ReactElement;
1642
+ declare function NavigationGuardDialog(): ReactElement;
1652
1643
  //#endregion
1653
- //#region src/layout/nav-user.d.ts
1654
- interface NavUserProps {
1655
- name: string;
1656
- email: string;
1657
- /** 1–2 char fallback shown in the avatar circle. */
1658
- avatarFallback: string;
1659
- /** Extra dropdown rows rendered above the logout item (theme/language/role, etc.). */
1660
- menu?: ReactNode;
1661
- onLogout: () => void;
1662
- logoutLabel?: string;
1644
+ //#region src/shell/refresh-button.d.ts
1645
+ interface RefreshButtonProps {
1646
+ readonly className?: string;
1663
1647
  }
1664
- declare function NavUser({ name, email, avatarFallback, menu, onLogout, logoutLabel }: NavUserProps): React.ReactElement;
1648
+ /** Ghost icon button that refetches the app's data; spins and disables while it runs. */
1649
+ declare function RefreshButton({ className }: RefreshButtonProps): ReactElement | null;
1650
+ //#endregion
1651
+ //#region src/shell/app-actions.d.ts
1652
+ /**
1653
+ * The app-level action strip: every `defineAppAction` entry that belongs to no resource, already
1654
+ * filtered by the rule engine (`useAppActions`). Only entries that resolve to a route render: a
1655
+ * `link`, or a flow hosted on a page (`hostHref`). A `modal` entry needs a presentation host and is
1656
+ * skipped. `disabledReason` keeps the action visible but inert, with the reason as its tooltip.
1657
+ */
1658
+ declare function AppActions(): ReactElement | null;
1659
+ //#endregion
1660
+ //#region src/shell/help-button.d.ts
1661
+ type HelpButtonProps = {
1662
+ readonly className?: string;
1663
+ } & ({
1664
+ readonly href: string;
1665
+ readonly onClick?: never;
1666
+ } | {
1667
+ readonly onClick: () => void;
1668
+ readonly href?: never;
1669
+ });
1670
+ /** Ghost icon button that opens help: a link when given `href`, a button when given `onClick`. */
1671
+ declare function HelpButton({ href, onClick, className }: HelpButtonProps): ReactElement;
1665
1672
  //#endregion
1666
1673
  //#region src/layout/app-header.d.ts
1674
+ /** Every slot follows {@link Slot}: omit for the default, `false` for nothing, an element to replace. */
1667
1675
  interface AppHeaderProps {
1668
- breadcrumbs?: ReactNode;
1669
- search?: ReactNode;
1670
- actions?: ReactNode;
1676
+ /** The breadcrumb trail. Default `<HeaderBreadcrumbs />`, the current route's trail. */
1677
+ breadcrumbs?: Slot;
1678
+ /**
1679
+ * The search chip. Default `<CommandPaletteTrigger />`, which renders only
1680
+ * when the `ShellProvider` has an `openPalette`.
1681
+ */
1682
+ search?: Slot;
1683
+ /**
1684
+ * The right-hand controls. Default `<AppActions />` and `<RefreshButton />`;
1685
+ * each renders nothing when it has nothing to show.
1686
+ */
1687
+ actions?: Slot;
1671
1688
  className?: string;
1672
1689
  }
1673
1690
  declare function AppHeader({ breadcrumbs, search, actions, className }: AppHeaderProps): React.ReactElement;
1674
1691
  //#endregion
1675
1692
  //#region src/layout/document-title.d.ts
1676
1693
  /**
1677
- * Register the desired `document.title` for the current page. Under a
1694
+ * Register the desired `document.title` for the current page (suffixed with ` · <brand name>` under a `ShellProvider` brand). Under a
1678
1695
  * `DocumentTitleProvider` the title is applied AND DEFENDED against Next's
1679
1696
  * metadata writes (Next re-applies the root `metadata.title` to `<title>` on
1680
1697
  * soft navigations — same-URL clicks, `?query` changes, post-hydration ticks —
@@ -1714,62 +1731,19 @@ declare function DocumentTitleProvider({ routeKey, children }: {
1714
1731
  //#region src/notification/toaster.d.ts
1715
1732
  interface ToasterProps extends ToasterProps$1 {}
1716
1733
  /**
1717
- * Shadcn-canonical Sonner setup. Bound to design tokens via
1718
- * `toastOptions.classNames` so toasts inherit the app's light/dark
1719
- * theme through the same CSS-variable cascade as the rest of the UI.
1734
+ * Shadcn-canonical Sonner setup, and — inside a `<BuroProvider>` — the app's
1735
+ * notification sink: the dispatcher's default `notify` (every handler's
1736
+ * `notify`, and a thrown handler's error) is delivered here. Outside a
1737
+ * provider it registers nothing and still renders anything sent with `toast()`.
1720
1738
  *
1721
- * Differences from the old default:
1722
- * - Dropped `richColors` (it set Sonner's own success/error palette,
1723
- * overriding the theme tokens we now wire in).
1724
- * - `theme` is reactive via `useTheme()` (packages/ui-shadcn/src/layout/use-theme.ts),
1725
- * so any writer flipping the app theme — the command palette, the user
1726
- * submenu — flips the toast theme without a remount.
1727
- * - Toast surface, border, shadow, and description text are pinned
1728
- * to `bg-background` / `text-foreground` / `border-border` / etc.,
1729
- * matching Card and AlertDialog primitives.
1739
+ * Bound to design tokens via `toastOptions.classNames`, so toasts inherit the
1740
+ * app's light/dark theme through the same CSS-variable cascade as the rest of
1741
+ * the UI; `theme` is reactive via `useTheme()`. The error catalogue follows the
1742
+ * ACTIVE i18n locale, read per toast so a language switch is honoured; with no
1743
+ * i18n provider it falls back to the locale `<LanguageSwitch>` persisted.
1730
1744
  */
1731
1745
  declare function Toaster(props: ToasterProps): React.ReactElement;
1732
1746
  //#endregion
1733
- //#region src/notification/notification-provider.d.ts
1734
- interface NotificationSink {
1735
- open(args: {
1736
- readonly type: 'success' | 'error' | 'progress';
1737
- readonly message: string;
1738
- readonly description?: string;
1739
- }): void;
1740
- close(key: string): void;
1741
- }
1742
- /**
1743
- * A legacy query adapter can build its default query-error notification outside
1744
- * our i18n bundle: in a Russian-primary app the user
1745
- * saw «Error (status code: undefined)» / «Failed to fetch», plus it duplicated
1746
- * the inline error state a list already renders. We can't localize a string we
1747
- * don't own, so `toFriendlyError` recognises it and substitutes plain language.
1748
- * The SAME helper backs the list's inline panel — see friendly-error.ts for why
1749
- * that matters.
1750
- */
1751
- interface NotificationProviderOptions {
1752
- /**
1753
- * How to find the ACTIVE locale when an error toast needs its catalogue.
1754
- * Defaults to the locale persisted by `<LanguageSwitch>` under
1755
- * {@link DEFAULT_LANGUAGE_STORAGE_KEY}.
1756
- *
1757
- * The escape hatch exists because the default is only right for an app that
1758
- * left `storageKey` alone: `<LanguageSwitch storageKey="acme:lang">` writes
1759
- * somewhere this factory does not look, and the toast would silently take the
1760
- * fallback catalogue forever. Such an app passes
1761
- * `{ resolveLocale: () => loadLocale('acme:lang') }` — or reads its own i18n
1762
- * facade directly, which is strictly better still.
1763
- *
1764
- * ⚠️ It is a FUNCTION, not a value, and that is load-bearing: both examples
1765
- * build this sink once (`React.useMemo(…, [])`) and it has no React context
1766
- * to subscribe to, so a locale captured at construction would freeze at
1767
- * whatever was active on the first render. Called per toast instead.
1768
- */
1769
- readonly resolveLocale?: () => string | undefined;
1770
- }
1771
- declare function createNotificationProvider(options?: NotificationProviderOptions): NotificationSink;
1772
- //#endregion
1773
1747
  //#region src/auth/login-form.d.ts
1774
1748
  declare const schema: z.ZodObject<{
1775
1749
  email: z.ZodString;
@@ -1793,7 +1767,10 @@ declare function LoginForm({ onSubmit, title, description, className }: LoginFor
1793
1767
  //#region src/auth/auth-page.d.ts
1794
1768
  interface AuthPageProps {
1795
1769
  children: ReactNode;
1796
- /** Optional brand mark / logo above the auth card. */
1770
+ /**
1771
+ * The brand above the auth card. Omit it for `<BrandMark />` (the `ShellProvider` brand, if
1772
+ * any); pass `false` for none; pass an element to replace it.
1773
+ */
1797
1774
  brand?: ReactNode;
1798
1775
  /** Optional footer text under the auth card (e.g. legal). */
1799
1776
  footer?: ReactNode;
@@ -1830,13 +1807,28 @@ declare const shadcnFieldImplementations: Readonly<{
1830
1807
  'default:date-time': FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1831
1808
  enum: FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1832
1809
  json: FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1833
- relation: FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1810
+ relation: Readonly<{
1811
+ Input?: import("react").ComponentType<ScalarInputProps<unknown, Readonly<Record<string, unknown>>>> | undefined;
1812
+ Display?: import("react").ComponentType<ScalarDisplayProps<unknown, Readonly<Record<string, unknown>>>> | undefined;
1813
+ CollectionInput?: import("react").ComponentType<CollectionInputProps<unknown, Readonly<Record<string, unknown>>>> | undefined;
1814
+ CollectionDisplay?: import("react").ComponentType<CollectionDisplayProps<unknown, Readonly<Record<string, unknown>>>> | undefined;
1815
+ layout?: FieldImplementationLayout;
1816
+ meta: import("@burojs/core").StandardSchemaV1<unknown, Readonly<Record<string, unknown>>>;
1817
+ }>;
1834
1818
  richText: FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1835
1819
  'rich-text': FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1836
1820
  'default:rich-text': FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1837
1821
  file: FieldImplementation<unknown, Readonly<Record<string, unknown>>>;
1838
1822
  }>;
1839
1823
  //#endregion
1824
+ //#region src/blocks/builtins.d.ts
1825
+ /**
1826
+ * The built-in block views (`buro:*`), keyed by block id. The renderer composes them first and
1827
+ * the app's `blockImplementations` over them: an app entry with a built-in id replaces that view
1828
+ * (with a development notice); a built-in cannot be dropped.
1829
+ */
1830
+ declare const shadcnBlockImplementations: BlockImplementationCatalog;
1831
+ //#endregion
1840
1832
  //#region src/fields/registry.d.ts
1841
1833
  interface FieldRendererProps {
1842
1834
  value: unknown;
@@ -1850,7 +1842,7 @@ interface FieldRendererProps {
1850
1842
  };
1851
1843
  options?: Array<{
1852
1844
  value: string;
1853
- label: LocalizedMessage;
1845
+ label: string;
1854
1846
  icon?: string;
1855
1847
  }>;
1856
1848
  /**
@@ -1878,6 +1870,15 @@ interface FieldRendererProps {
1878
1870
  }
1879
1871
  interface FieldInputProps {
1880
1872
  name: string;
1873
+ /** A relation select's create seeds (P6-31): the registry's `candidates` literals. */
1874
+ relationSeeds?: Readonly<Record<string, unknown>>;
1875
+ /** A relation select's extra filters from the form's `filter` function (P7 3.4). */
1876
+ fixedFilters?: readonly FixedFilterInput[];
1877
+ /**
1878
+ * The field owner's source states, projected by what the user may view: what a relation's
1879
+ * `candidates` refs are read from (P7 3.4).
1880
+ */
1881
+ metaSources?: ScreenSources;
1881
1882
  id?: string;
1882
1883
  value: unknown;
1883
1884
  /** True when this renderer is reached through a collection implementation. */
@@ -1886,7 +1887,7 @@ interface FieldInputProps {
1886
1887
  testid?: string;
1887
1888
  options?: Array<{
1888
1889
  value: string;
1889
- label: LocalizedMessage;
1890
+ label: string;
1890
1891
  icon?: string;
1891
1892
  }>;
1892
1893
  placeholder?: string;
@@ -1900,7 +1901,7 @@ interface FieldInputProps {
1900
1901
  pattern?: string;
1901
1902
  /**
1902
1903
  * When true, renders `aria-invalid="true"` on the underlying input
1903
- * control. Forwarded by `<ResourceForm>` for fields that have a
1904
+ * control. Forwarded by `<RecordScreen>` for fields that have a
1904
1905
  * non-null validation error AND have been touched (blurred or
1905
1906
  * post-submit). Renderers should pass this through to their root
1906
1907
  * input element so screen-readers can announce the invalid state.
@@ -1919,18 +1920,18 @@ interface FieldInputProps {
1919
1920
  * `aria-errormessage` on their root input element so screen-readers
1920
1921
  * with WAI-ARIA 1.2 support associate the error specifically.
1921
1922
  * `aria-invalid="true"` must also be set for `aria-errormessage` to
1922
- * be honored — `<ResourceForm>` already wires both together.
1923
+ * be honored — `<RecordScreen>` already wires both together.
1923
1924
  */
1924
1925
  ariaErrorMessage?: string;
1925
1926
  /**
1926
1927
  * Field metadata, mirroring `FieldRendererProps.field`. Forwarded by
1927
- * `<ResourceForm>` so renderers that depend on per-field meta (e.g.
1928
+ * `<RecordScreen>` so renderers that depend on per-field meta (e.g.
1928
1929
  * the relation picker reading `meta.to` for the target resource) can
1929
1930
  * read it. Renderers that don't care simply ignore the prop.
1930
1931
  *
1931
1932
  * The `ui` block carries the field-type-specific renderer config
1932
1933
  * (e.g. `<FileInput>` reads `ui.accept` / `ui.maxSize` /
1933
- * `ui.fileStorage`). Forwarded by `<ResourceForm>` from the field
1934
+ * `ui.fileStorage`). Forwarded by `<RecordScreen>` from the field
1934
1935
  * definition's `def.ui` block.
1935
1936
  *
1936
1937
  * Optional for backward compatibility — every existing renderer was
@@ -1975,8 +1976,8 @@ declare function DateDisplay({ value }: FieldRendererProps): React$1.ReactElemen
1975
1976
  declare function DateInput({ name, id, value, onChange, testid, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }: FieldInputProps): React$1.ReactElement;
1976
1977
  //#endregion
1977
1978
  //#region src/fields/enum.d.ts
1978
- declare function EnumDisplay({ value, options, field }: FieldRendererProps): React$1.ReactElement;
1979
- declare function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }: FieldInputProps): React$1.ReactElement;
1979
+ declare function EnumDisplay({ value, options, field, resource }: FieldRendererProps): React$1.ReactElement;
1980
+ declare function EnumInput({ name, id, value, onChange, testid, options, field, placeholder, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, resource }: FieldInputProps): React$1.ReactElement;
1980
1981
  //#endregion
1981
1982
  //#region src/fields/textarea.d.ts
1982
1983
  declare function TextareaDisplay({ value }: FieldRendererProps): React$1.ReactElement;
@@ -2042,6 +2043,25 @@ declare function RelationField({ to, value }: {
2042
2043
  /** FK value to resolve, or null/undefined/'' when empty. */
2043
2044
  value: unknown;
2044
2045
  }): React$1.ReactElement;
2046
+ /**
2047
+ * A to-many relation (`f.relationMany`): each key rendered as {@link RelationDisplay} renders
2048
+ * one — eager keys share the aggregator's batched read, lazy keys read nothing.
2049
+ */
2050
+ declare function RelationManyDisplay({ value, field }: FieldRendererProps): React$1.ReactElement;
2051
+ //#endregion
2052
+ //#region src/fields/candidates-gate.d.ts
2053
+ /** A select that must not read its target now (P7 3.4): why, and what it tells the user. */
2054
+ type CandidatesGate = {
2055
+ readonly kind: 'pending';
2056
+ } | {
2057
+ readonly kind: 'empty';
2058
+ readonly message: string;
2059
+ readonly testId: string;
2060
+ } | {
2061
+ readonly kind: 'forbidden';
2062
+ readonly message: string;
2063
+ readonly testId: string;
2064
+ };
2045
2065
  //#endregion
2046
2066
  //#region src/fields/relation-picker.d.ts
2047
2067
  /**
@@ -2074,8 +2094,13 @@ interface RelationPickerProps {
2074
2094
  to: string;
2075
2095
  /** Currently-selected FK value, or null/undefined when empty. */
2076
2096
  value: string | number | null | undefined;
2077
- /** Called with the new selected id, or null when cleared. */
2097
+ /** Called with the new selected id (or `by` value), or null when cleared. */
2078
2098
  onChange: (next: string | number | null) => void;
2099
+ /**
2100
+ * The target field the value holds (`meta.by`, spec 4.2): options write `row[by]` and the
2101
+ * selected value is resolved by it. Absent: the target's id field.
2102
+ */
2103
+ by?: string;
2079
2104
  /**
2080
2105
  * Field whose value is matched against the search text. Defaults to
2081
2106
  * the target's `displayField` (which is what the trigger label
@@ -2106,6 +2131,27 @@ interface RelationPickerProps {
2106
2131
  * user, and add `label` to render it as an undeletable badge instead.
2107
2132
  */
2108
2133
  fixedFilters?: readonly FixedFilterInput[];
2134
+ /**
2135
+ * The trusted `defaults` of a create opened from this select (P6-31): the registry field's
2136
+ * static `eq` literals, handed down by the form controller. Never read from `fixedFilters` or
2137
+ * `field.meta`.
2138
+ */
2139
+ createSeeds?: Readonly<Record<string, unknown>>;
2140
+ /**
2141
+ * What the field's `candidates` say about reading the target right now (P7 3.4): `pending`
2142
+ * draws a disabled trigger, `empty` a popover that says what to choose first, `forbidden` a
2143
+ * disabled trigger and why. While set, nothing is read for the options. Absent: read normally.
2144
+ */
2145
+ candidatesGate?: CandidatesGate;
2146
+ /**
2147
+ * Whether a loaded row fits the resolved candidate filters (already in `fixedFilters`):
2148
+ * `false` makes the field say the chosen row "does not match the current filter" (P7 3.4).
2149
+ */
2150
+ rowFits?: (row: Record<string, unknown>) => boolean | undefined;
2151
+ /** The form field holding the key (the "view" and "+ create" test ids); defaults to `name`. */
2152
+ fieldName?: string;
2153
+ /** The field's `ui.open` (P7 3.6): what the "view" button opens, or no button for `none`. */
2154
+ open?: RelationOpen;
2109
2155
  disabled?: boolean;
2110
2156
  required?: boolean;
2111
2157
  className?: string;
@@ -2116,7 +2162,7 @@ interface RelationPickerProps {
2116
2162
  /** Forwarded to the trigger button as `aria-errormessage` (WAI-ARIA 1.2). */
2117
2163
  ariaErrorMessage?: string;
2118
2164
  }
2119
- declare function RelationPicker({ to, value, onChange, searchField, pageSize, clearable, placeholder, name, id, testid, fixedFilters, disabled, required, className, ariaInvalid, ariaDescribedBy, ariaErrorMessage }: RelationPickerProps): React$1.ReactElement;
2165
+ declare function RelationPicker({ to, value, onChange, by, searchField, pageSize, clearable, placeholder, name, id, testid, fixedFilters, createSeeds, candidatesGate, rowFits, fieldName, open: openMode, disabled, required, className, ariaInvalid, ariaDescribedBy, ariaErrorMessage }: RelationPickerProps): React$1.ReactElement;
2120
2166
  /**
2121
2167
  * Form-field adapter wrapping `<RelationPicker>` for use as a default
2122
2168
  * Input renderer in the field registry. Reads `field.meta.to` for the
@@ -2126,6 +2172,20 @@ declare function RelationPicker({ to, value, onChange, searchField, pageSize, cl
2126
2172
  */
2127
2173
  declare function RelationPickerInput(props: FieldInputProps): React$1.ReactElement;
2128
2174
  //#endregion
2175
+ //#region src/fields/relation-create.d.ts
2176
+ /** What the picker and its dialog share: one pending create per select (P6-34). */
2177
+ interface SelectCreate {
2178
+ readonly pending: boolean;
2179
+ /**
2180
+ * The hand-back read is forbidden: its filter names a field P5 hides from the user (the same
2181
+ * rule that stops the picker's own option read), or the access map is not ready. A blocked
2182
+ * select offers no "+ Create" and starts none, so no predicate on a hidden field is ever sent.
2183
+ */
2184
+ readonly blocked: boolean;
2185
+ /** Runs `open` unless a create of this select is already pending or the select is gone. */
2186
+ start(open: () => Promise<QuickSettledRow>): void;
2187
+ }
2188
+ //#endregion
2129
2189
  //#region src/fields/relation-picker-dialog.d.ts
2130
2190
  /**
2131
2191
  * Advanced-search modal for the relation picker (#11 / #9). The inline popover
@@ -2176,11 +2236,34 @@ interface RelationPickerDialogProps {
2176
2236
  * renders it as an undeletable badge.
2177
2237
  */
2178
2238
  fixedFilters?: readonly FixedFilterInput[];
2179
- /** Create href for the "+ Create" shortcut. Defaults to the target basePath + /create. */
2180
- createHref?: string;
2239
+ /**
2240
+ * The select's one pending create (P6-34), shared with its popover. With it the footer offers
2241
+ * "+ Create" (the target's `quick.create`, handed back through the select's own query); without
2242
+ * it the dialog offers none. The dialog is not a dismiss scope and holds no form: the sheet a
2243
+ * create opens stacks above it.
2244
+ */
2245
+ create?: SelectCreate;
2246
+ /** The trusted `defaults` of that create: the registry field's static `eq` literals (P6-31). */
2247
+ createSeeds?: Readonly<Record<string, unknown>>;
2248
+ /** The form field holding the key (the create button's test id). */
2249
+ fieldName?: string;
2250
+ /** Runs before a create opens: the select returns focus to its trigger. */
2251
+ onBeforeCreate?: () => void;
2181
2252
  pageSize?: number;
2253
+ /**
2254
+ * A to-many select's choice (P7 3.5): a click toggles the row in `selected` through `onToggle`
2255
+ * and the dialog stays open until the user is done. Without it, a click picks one row and closes.
2256
+ */
2257
+ multiple?: boolean;
2258
+ /** The keys already chosen (`multiple` only): their rows are marked. */
2259
+ selected?: readonly (string | number)[];
2260
+ onToggle?: (value: string | number) => void;
2182
2261
  }
2183
- declare function RelationPickerDialog({ to, open, onOpenChange, onSelect, onSelectRow, searchField, valueField, fixedFilters, createHref, pageSize }: RelationPickerDialogProps): React$1.ReactElement;
2262
+ declare function RelationPickerDialog({ to, open, onOpenChange, onSelect, onSelectRow, searchField, valueField, fixedFilters, create, createSeeds, fieldName, onBeforeCreate, pageSize, multiple, selected, onToggle }: RelationPickerDialogProps): React$1.ReactElement;
2263
+ //#endregion
2264
+ //#region src/fields/lazy-relation-picker-dialog.d.ts
2265
+ /** The "Advanced search" dialog, loaded on the first open: nothing is drawn until it arrives. */
2266
+ declare function LazyRelationPickerDialog(props: ComponentProps<typeof RelationPickerDialog>): ReactElement;
2184
2267
  //#endregion
2185
2268
  //#region src/fields/use-dialog-filter-state.d.ts
2186
2269
  /** The portable erased AST — the only shape this module deals in. */
@@ -2222,6 +2305,10 @@ interface DialogFilterState {
2222
2305
  readonly fixed: readonly ResolvedFixedFilter[];
2223
2306
  /** The single expression to hand the data provider. */
2224
2307
  readonly filter: ErasedExpression | undefined;
2308
+ /** Whether the target has a search for this user (no field left means no search box). */
2309
+ readonly searchEnabled: boolean;
2310
+ /** The read must not be sent: undecided access, or a filter naming a field the user may not view. */
2311
+ readonly blocked: boolean;
2225
2312
  readonly reset: () => void;
2226
2313
  }
2227
2314
  /**
@@ -2322,6 +2409,12 @@ interface ResolvedFieldDisplayProps {
2322
2409
  readonly testId?: string;
2323
2410
  readonly fallback: ReactNode;
2324
2411
  readonly presentation?: FieldPresentation;
2412
+ /**
2413
+ * The owner's own source states, by name (P7 3.2): a form controller's projected `record` and
2414
+ * `values`, a flow's `with` and `args`. They replace the sources built from `record` and
2415
+ * `form`, so a field P5 withholds stays pending in a `meta` ref as it does everywhere else.
2416
+ */
2417
+ readonly metaSources?: ScreenSources;
2325
2418
  }
2326
2419
  interface ResolvedFieldInputProps extends Omit<ResolvedFieldDisplayProps, 'fallback'> {
2327
2420
  readonly inputId?: string;
@@ -2340,6 +2433,18 @@ interface ResolvedFieldInputProps extends Omit<ResolvedFieldDisplayProps, 'fallb
2340
2433
  readonly touched?: boolean;
2341
2434
  /** Optional canonical issues; localized errorMessage remains the fallback. */
2342
2435
  readonly issues?: ValidationIssues;
2436
+ /**
2437
+ * A relation select's create seeds (P6-31), from the form controller's binding. A separate
2438
+ * channel from `field.meta`: the seeds' provenance is the code registry, and `meta` can be
2439
+ * written by a layer.
2440
+ */
2441
+ readonly relationSeeds?: Readonly<Record<string, unknown>>;
2442
+ /**
2443
+ * A relation select's extra filters from the form's `filter` function (P7 3.4): code, handed
2444
+ * down by the form controller on its own channel, applied on top of the field's `candidates`.
2445
+ * Never read from `field.meta`, which a layer can write.
2446
+ */
2447
+ readonly fixedFilters?: readonly FixedFilterInput[];
2343
2448
  readonly fallback: ReactNode;
2344
2449
  }
2345
2450
  //#endregion
@@ -2349,15 +2454,12 @@ type FieldProps = Omit<ResolvedFieldInputProps, 'resource' | 'fallback'> & {
2349
2454
  readonly i18nPrefix?: string;
2350
2455
  readonly fallback?: ReactNode;
2351
2456
  /**
2352
- * A relation field's RESOLVED scope (design doc §5, `@burojs/react`'s
2353
- * `FieldBinding.fixedFilters`) — merged into the field's `meta.filters`
2354
- * before it reaches `ResolvedFieldInput`, the channel `RelationPickerInput`
2355
- * already reads off `field.meta.filters`
2356
- * (`relation-picker.tsx:478-487,526`). Any static `meta.filters` already
2357
- * on the field def is overridden, not merged, when this is supplied — the
2358
- * resolved per-render scope wins. `<DeclarativeForm>` never sets this
2359
- * explicitly: it spreads the whole `FieldBinding` (`{...binding}`), which
2360
- * already carries `fixedFilters` when the field declared a `filter` opt.
2457
+ * A relation field's `filter` function RESOLVED against the form's values (design doc §5,
2458
+ * `@burojs/react`'s `FieldBinding.fixedFilters`). It reaches the select on its own channel (a
2459
+ * prop of the input, never `field.meta`, which a layer can write) and applies on top of the
2460
+ * field's `candidates`. `<DeclarativeForm>` never sets this explicitly: it spreads the whole
2461
+ * `FieldBinding` (`{...binding}`), which already carries `fixedFilters` when the field
2462
+ * declared a `filter` function.
2361
2463
  *
2362
2464
  * `FixedFilterInput`, not the bare AST — matches `FieldBinding.fixedFilters`
2363
2465
  * (`@burojs/react`) and `RelationPickerProps`/`RelationPickerDialogProps`
@@ -2399,9 +2501,15 @@ type FieldDisplayProps = Omit<ResolvedFieldDisplayProps, 'resource' | 'fallback'
2399
2501
  declare function FieldDisplay({ resource, i18nPrefix, fallback, value, ...rest }: FieldDisplayProps): ReactElement;
2400
2502
  //#endregion
2401
2503
  //#region src/forms/declarative-form.d.ts
2402
- /** A footer slot may need the controller's submit-gate to build its own submit control — see {@link DeclarativeFormSlots.footer}. */
2504
+ /**
2505
+ * A footer slot may need the controller's submit-gate to build its own submit
2506
+ * control — see {@link DeclarativeFormSlots.footer}.
2507
+ */
2403
2508
  interface DeclarativeFormFooterCtx {
2404
- /** `form.canSubmit` already ANDed with the whole-form `disabled` lock — a footer never has to re-derive that combination itself. */
2509
+ /**
2510
+ * `form.canSubmit` already ANDed with the whole-form `disabled` lock — a
2511
+ * footer never has to re-derive that combination itself.
2512
+ */
2405
2513
  readonly canSubmit: boolean;
2406
2514
  }
2407
2515
  /**
@@ -2417,152 +2525,118 @@ interface DeclarativeFormSlots {
2417
2525
  * function form exists for the common case a plain node cannot serve: a
2418
2526
  * CUSTOM footer (e.g. Cancel + Submit side by side) that still needs the
2419
2527
  * framework's own disabled-while-invalid gate — `slots` is built by the
2420
- * CALLER before `useDeclarativeForm` has run, so a plain node has no way
2528
+ * CALLER before the form controller has run, so a plain node has no way
2421
2529
  * to read `form.canSubmit`. Called once per render with `{ canSubmit }`
2422
2530
  * (already ANDed with the whole-form `disabled` lock) instead of forcing
2423
2531
  * every such caller to either reimplement the gate or stand up a second,
2424
- * parallel `useDeclarativeForm` instance just to read it (a real risk:
2425
- * two independent async-validation instances observably disagreeing for
2426
- * a tick — the first caller this was added for was `EntityDialog` in the
2427
- * since-deleted `apps/l2-web-ui`).
2532
+ * parallel controller just to read it.
2428
2533
  */
2429
2534
  readonly footer?: ReactNode | ((ctx: DeclarativeFormFooterCtx) => ReactNode);
2430
2535
  readonly [slot: string]: ReactNode | ((ctx: DeclarativeFormFooterCtx) => ReactNode) | undefined;
2431
2536
  }
2432
- interface DeclarativeFormProps {
2537
+ interface DeclarativeFormProps<F extends ResourceFieldMap = ResourceFieldMap> {
2433
2538
  /** The same field-def map shape `defineResource`'s `f` builder produces. */
2434
- readonly fields: ResourceFieldMap;
2435
- /** Controlled values. Presence (not truthiness) selects controlled mode — see `useDeclarativeForm`. */
2539
+ readonly fields: F;
2540
+ /**
2541
+ * The form's tree (`(l) => [...]`, the same builder a resource's
2542
+ * `views.<mode>.body` uses); absent, every field in one column, in
2543
+ * declaration order. A field under a `when` that does not hold is inactive:
2544
+ * it is not drawn and not submitted.
2545
+ */
2546
+ readonly body?: FormBody<F>;
2547
+ /** The draft identity; `draft` without it is a development error. */
2548
+ readonly formId?: string;
2549
+ readonly draft?: DraftOption;
2550
+ /** Controlled values: presence (not truthiness) selects controlled mode. */
2436
2551
  readonly value?: FormValues;
2437
2552
  /** Uncontrolled seed. Ignored once `value` is supplied. */
2438
- readonly defaultValue?: FormValues;
2553
+ readonly defaults?: FormValues;
2439
2554
  readonly onChange?: (values: FormValues) => void;
2440
- /** Drives label i18n, the doc address, and the `<Field>` EntityRef — see `<Field>`'s own docblock. */
2441
- readonly i18nPrefix?: string;
2442
2555
  readonly schema?: StandardSchemaV1;
2556
+ /** Cross-field validation; may return warnings as well as errors. */
2557
+ readonly validate?: UseFormControllerFormOptions['validate'];
2443
2558
  /**
2444
- * Cross-field validation; may return warnings as well as errors. Typed off
2445
- * `UseDeclarativeFormOptions['validate']` (index access) rather than the
2446
- * controller's own `FormValidate` type, which `@burojs/react` keeps
2447
- * internal.
2559
+ * Conditional checks for this form, as a plain list (a resource-less form has
2560
+ * no `app.checkSource` entry); a confirmable warning asks through the
2561
+ * mounted `<CheckConfirmDialog>`.
2448
2562
  */
2449
- readonly validate?: UseDeclarativeFormOptions['validate'];
2563
+ readonly checks?: readonly Check[];
2564
+ /** The check surface; default `'create'`. */
2565
+ readonly surface?: CheckSurface;
2566
+ /** Receives the payload: the active declared fields only. */
2450
2567
  readonly onSubmit?: (values: FormValues) => unknown | Promise<unknown>;
2451
2568
  /**
2452
- * Whole-form lock: every field renders disabled, OR-ed with that field's
2453
- * own resolved `disabled`. Distinct from a read-only VIEW, which renders
2454
- * display components instead of disabled inputs (`<DeclarativeDisplay>`,
2455
- * Task 7).
2569
+ * Text slots and the doc address key as `resources.<i18nPrefix>.…`; the
2570
+ * leaf ids are `<i18nPrefix>:field:<name>:hint` / `:info`. Default `'form'`.
2456
2571
  */
2457
- readonly disabled?: boolean;
2458
- /** Simple column-count switch. Defaults to a single column. */
2459
- readonly layout?: 'single' | 'two-column';
2460
- /**
2461
- * Class applied to the `<form>` element itself.
2462
- *
2463
- * Exists so a host can make the form a layout container rather than a
2464
- * plain block — the case that motivated it is a dialog taller than the
2465
- * viewport, where the form must become `flex flex-col` so its body can
2466
- * scroll between a fixed header and a fixed footer. Without it the host
2467
- * can only scroll the whole dialog, which drags the submit button out of
2468
- * reach.
2469
- */
2470
- readonly className?: string;
2572
+ readonly i18nPrefix?: string;
2471
2573
  /**
2472
- * Class applied to the FIELDS container — the element holding the field
2473
- * grid, between the form-level issue summary and the footer.
2474
- *
2475
- * This is the scroll region for a form too tall to fit: pair it with
2476
- * `className` (`flex flex-col`) and give this `flex-1 overflow-y-auto
2477
- * min-h-0`. Kept separate from `className` because scrolling the whole
2478
- * form would scroll its footer away too, which is the bug rather than the
2479
- * fix.
2574
+ * Whole-form lock: every field renders disabled, OR-ed with that field's
2575
+ * own resolved `disabled`. Distinct from a read-only VIEW
2576
+ * (`<DeclarativeDisplay>`), which renders displays instead of inputs.
2480
2577
  */
2481
- readonly bodyClassName?: string;
2482
- readonly slots?: DeclarativeFormSlots;
2578
+ readonly disabled?: boolean;
2483
2579
  /**
2484
2580
  * Per-field render override — swap ONE field's node without abandoning
2485
- * the managed layout (label, unreserved message slot, submit gating all
2486
- * still apply around it). Receives the raw binding plus the default
2487
- * `<Field/>` node this renderer would have used, so an override can
2488
- * wrap/decorate rather than reimplement.
2581
+ * the managed leaf (label, message slot, hint): receives the binding and
2582
+ * the default input node, so an override can wrap rather than reimplement.
2489
2583
  */
2490
2584
  readonly renderField?: Record<string, (binding: FieldBinding, defaultNode: ReactNode) => ReactNode>;
2585
+ readonly slots?: DeclarativeFormSlots;
2586
+ /** The submit caption; defaults to the framework bundle's `actions.save` ("Save"). */
2491
2587
  readonly submitLabel?: string;
2492
2588
  /**
2493
- * Conditional checks (design doc `docs/superpowers/specs/2026-08-06-
2494
- * conditional-checks-design.md`) for this form. Declarative forms are
2495
- * resource-less, so — unlike `<ResourceForm>`, which resolves checks off
2496
- * the resource def via `app.checkSource` — these are a plain prop, merged
2497
- * into `form.errors`/`form.warnings` on submit and gated behind
2498
- * `<CheckConfirmDialog>` (mounted below) when a confirmable warning fires.
2589
+ * Class applied to the `<form>` element itself — e.g. `flex flex-col` for a
2590
+ * dialog taller than the viewport, so the body can scroll between a fixed
2591
+ * header and footer.
2499
2592
  */
2500
- readonly checks?: readonly Check[];
2593
+ readonly className?: string;
2501
2594
  /**
2502
- * Explicit check surface (`'create' | 'edit' | ...`) — forwarded to
2503
- * `useDeclarativeForm`'s own `surface` option. Omit to use the default
2504
- * create/edit inference off `value`/`defaultValue` presence; supply this
2505
- * whenever that heuristic is wrong for this form — most commonly, a create
2506
- * form that ALSO seeds `defaultValue` (e.g. `defaultValue={{ status:
2507
- * 'draft' }}`), which the inference alone would misread as `'edit'`. See
2508
- * `UseDeclarativeFormOptions.surface`'s own doc comment (`@burojs/react`)
2509
- * for the full rationale.
2595
+ * Class applied to the fields region (`[data-buro-region="form-body"]`), the
2596
+ * scroll region of a form too tall to fit (`flex-1 overflow-y-auto min-h-0`).
2510
2597
  */
2511
- readonly surface?: CheckSurface;
2598
+ readonly bodyClassName?: string;
2512
2599
  }
2513
2600
  /**
2514
- * Batteries-included form renderer over `useDeclarativeForm` (the headless
2515
- * controller) and `<Field>` (the single-field brick) — design doc §4,
2516
- * Layer 3. Uncontrolled by default: `defaultValue` seeds, internal state
2517
- * runs, `onSubmit(values)` delivers the payload — the fast path is a real
2518
- * one-liner, `<DeclarativeForm fields={fields} onSubmit={save} />`.
2519
- * Controlled `value`/`onChange` remain available for the complex case.
2520
- *
2521
- * Renders every VISIBLE field (`form.fields` — hidden fields are already
2522
- * excluded by the controller) with its label, its `<Field/>` (or a
2523
- * `renderField` override), and ONE unreserved message slot: an error wins
2524
- * the slot in `text-destructive`, else the first warning renders in
2525
- * `text-warning-emphasis`, else nothing renders at all — mirroring
2526
- * `resource-form.tsx`'s single-slot rule so no field can silently double its
2527
- * layout's space, without reserving height for a message that may never
2528
- * appear. Form-level (`field === '_form'`) errors/warnings render in their
2529
- * own non-destructive/destructive summary above the fields. Submit is
2530
- * disabled on `!form.canSubmit` (errors only — warnings never block) or
2531
- * `props.disabled`.
2532
- */
2533
- declare function DeclarativeForm(props: DeclarativeFormProps): ReactElement;
2601
+ * Batteries-included resource-less form (P1b+P1c decision 21) over
2602
+ * `useFormController` and the screen renderer: the form's `body` tree (or one
2603
+ * column of every field), each leaf with its label, input, one message slot
2604
+ * (error over warning) and hint; a form-level error / warning summary above;
2605
+ * the submit footer below. Uncontrolled by default (`defaults` seeds,
2606
+ * `onSubmit(payload)` delivers the active declared values); controlled
2607
+ * `value` / `onChange` remain available. Submit is disabled on
2608
+ * `!form.canSubmit` (errors only — warnings never block) or `disabled`.
2609
+ */
2610
+ declare function DeclarativeForm<F extends ResourceFieldMap = ResourceFieldMap>(props: DeclarativeFormProps<F>): ReactElement;
2534
2611
  //#endregion
2535
2612
  //#region src/forms/declarative-display.d.ts
2536
- interface DeclarativeDisplayProps {
2613
+ interface DeclarativeDisplayProps<F extends ResourceFieldMap = ResourceFieldMap> {
2537
2614
  /** The same field-def map shape `defineResource`'s `f` builder produces. */
2538
- readonly fields: ResourceFieldMap;
2615
+ readonly fields: F;
2539
2616
  /** The values to display — this renderer owns no state of its own. */
2540
2617
  readonly values: FormValues;
2541
- /** Drives label i18n and the `<FieldDisplay>` EntityRef — see `<Field>`'s own docblock. */
2618
+ /**
2619
+ * Drives label i18n, the doc address and the leaf ids
2620
+ * (`<i18nPrefix>:field:<name>:hint` / `:info`). Default `'form'`.
2621
+ */
2542
2622
  readonly i18nPrefix?: string;
2543
- /** Simple column-count switch. Defaults to a single column. */
2544
- readonly layout?: 'single' | 'two-column';
2623
+ /**
2624
+ * The display's tree, as `<DeclarativeForm body>`; absent, every field in
2625
+ * one column, in declaration order. A field under a `when` that does not
2626
+ * hold over `values` is not shown.
2627
+ */
2628
+ readonly body?: FormBody<F>;
2545
2629
  }
2546
2630
  /**
2547
- * Read-only VIEW renderer over `<FieldDisplay>` (Task 1) — design doc §4,
2548
- * Layer 3, the "view" corner of the read/edit/locked distinction:
2549
- * **edit** (`<DeclarativeForm>`), **locked** (`<DeclarativeForm disabled>` —
2550
- * inputs, disabled), **view** (this — display components, never inputs).
2551
- *
2552
- * Deliberately does NOT pull in `useDeclarativeForm` — a view has no state,
2553
- * no validation, no submit lifecycle, so there is nothing for a controller
2554
- * instance to own here. It IS, however, a thin map over the exact same
2555
- * conditional-visibility resolution the controller runs internally
2556
- * (`resolveFieldFlags` + `useOptionalVisibilityCtx`, `@burojs/react`,
2557
- * Task 4) — reused as-is, not reimplemented, so a field hidden in
2558
- * `<DeclarativeForm>` is hidden here too for the same values snapshot.
2559
- *
2560
- * Renders every visible field, in `fields` declaration order, as a label
2561
- * plus `<FieldDisplay/>` — mirroring `<DeclarativeForm>`'s own per-field
2562
- * layout (label above the field's node) minus the parts a view has no use
2563
- * for (required marker, error/warning slot, submit).
2631
+ * Read-only VIEW of a resource-less field map (P1b+P1c S3e): the
2632
+ * read/edit/locked distinction's "view" corner — **edit** is
2633
+ * `<DeclarativeForm>`, **locked** `<DeclarativeForm disabled>` (inputs,
2634
+ * disabled), **view** this (displays, never inputs). Driven by the form
2635
+ * controller in `show` mode over `values`, rendered through the same screen
2636
+ * renderer, so a field hidden by a `when` in `<DeclarativeForm>` is hidden here
2637
+ * too for the same values.
2564
2638
  */
2565
- declare function DeclarativeDisplay(props: DeclarativeDisplayProps): ReactElement;
2639
+ declare function DeclarativeDisplay<F extends ResourceFieldMap = ResourceFieldMap>(props: DeclarativeDisplayProps<F>): ReactElement;
2566
2640
  //#endregion
2567
2641
  //#region src/export/export-dialog.d.ts
2568
2642
  interface ExportDialogProps {
@@ -2570,7 +2644,11 @@ interface ExportDialogProps {
2570
2644
  readonly onOpenChange: (open: boolean) => void;
2571
2645
  readonly resource: string;
2572
2646
  readonly target: ExportTarget;
2573
- /** The list's currently visible columns — what "as in table" (the default) sends as `request.columns`. */
2647
+ /**
2648
+ * The list's currently visible columns — what "as in table" (the default) sends as `request.columns`.
2649
+ * Empty means "unknown": the dialog then sends the same set as "All columns" (the resource's list
2650
+ * fields, minus any a `column` rule hides). `request.columns` is never omitted.
2651
+ */
2574
2652
  readonly visibleColumns: readonly string[];
2575
2653
  /** Known row estimate, shown in the filter-scope title and handed to the exporter as a progress total hint. */
2576
2654
  readonly estimatedCount?: number;
@@ -2580,14 +2658,15 @@ interface ExportDialogProps {
2580
2658
  * unregistered `UIPresentRequest.component` registry key silently renders
2581
2659
  * `null`, so a framework-owned dialog would do nothing until every adopter
2582
2660
  * registered it (see `packages/react/src/ui/ui-presentations.tsx:74,138`).
2583
- * `formModal` actions draw the identical line — self-hosted, built on the
2584
- * local `<Dialog>` primitives, no registry involved.
2661
+ * The flow host draws the opposite line (it IS presented, as `buro:flow`, and
2662
+ * `shadcnPresentationHosts` registers it); this dialog stays self-hosted, built
2663
+ * on the local `<Dialog>` primitives, no registry involved.
2585
2664
  *
2586
2665
  * A `mode` state (`'form' | 'progress' | 'confirm-close'`) swaps content
2587
2666
  * inside ONE `<DialogContent>`/one Radix focus scope. A second, nested
2588
2667
  * Radix root for the close-while-running confirm was tried elsewhere in this
2589
- * codebase and deadlocked the jsdom test runner
2590
- * (`packages/ui-shadcn/src/actions/action-button.tsx:114-127`) — this dialog
2668
+ * codebase and deadlocked the jsdom test runner (the old form-modal action's
2669
+ * menu-held dialog, deleted in P4) — this dialog
2591
2670
  * never repeats that shape.
2592
2671
  */
2593
2672
  declare function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount }: ExportDialogProps): React$1.ReactElement;
@@ -2808,7 +2887,7 @@ declare function EnumPicker({ enumName, value, onChange, renderAs, name, id, tes
2808
2887
  //#region src/primitives/button.d.ts
2809
2888
  declare const buttonVariants: (props?: ({
2810
2889
  variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
2811
- size?: "default" | "sm" | "lg" | "icon" | null | undefined;
2890
+ size?: "sm" | "lg" | "default" | "icon" | null | undefined;
2812
2891
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
2813
2892
  interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
2814
2893
  asChild?: boolean;
@@ -2860,40 +2939,12 @@ interface ActionButtonProps {
2860
2939
  children?: React.ReactNode;
2861
2940
  /**
2862
2941
  * Render the action as a `<DropdownMenuItem>` (icon + label) instead of a
2863
- * standalone `<Button>`, for use inside an overflow `…` menu. Every action
2864
- * kind keeps its full behavior: confirm dialogs and formModals stay mounted
2865
- * (the menu is held open while they show), links navigate, and modal actions
2866
- * open through the framework's `UIPresentationRuntime` (`useUIPresent`).
2942
+ * standalone `<Button>`, for use inside an overflow `…` menu. Links navigate,
2943
+ * modal actions open through the framework's `UIPresentationRuntime`
2944
+ * (`useUIPresent`), and a flow hands its run to a runner outside the menu.
2867
2945
  * Destructive actions get destructive item styling.
2868
2946
  */
2869
2947
  asMenuItem?: boolean;
2870
- /**
2871
- * Menu-mode only. Called instead of running the action directly, so the
2872
- * OWNER of the menu (`ActionOverflow`) can run the check gate and mount its
2873
- * dialog outside the dropdown (see {@link ActionConfirmRunner}). Called
2874
- * unconditionally — not only when the action carries a `confirm` — because
2875
- * a conditional check (Task 10) can fire with no `confirm` at all and still
2876
- * needs the dialog; `ActionConfirmRunner` is where that gate actually runs.
2877
- * Without it, a confirming or check-gated action rendered as a menu item
2878
- * cannot prompt (see {@link ActionConfirmDialog} for why the dialog must
2879
- * not live inside the menu).
2880
- */
2881
- onRequestConfirm?: () => void;
2882
- /**
2883
- * Menu-mode only, and the exact analogue of {@link onRequestConfirm} for
2884
- * `formModal` actions: called instead of opening the form, so the OWNER of
2885
- * the menu (`ActionOverflow`) mounts the Dialog OUTSIDE the dropdown.
2886
- *
2887
- * Without it, a formModal item had to render its own `<Dialog>` inside
2888
- * `DropdownMenuContent` and hold the menu open with `onSelect`'s
2889
- * `preventDefault()` so the dialog's own subtree wasn't torn down. That nests
2890
- * two Radix focus scopes (menu + dialog) inside one another: in a browser
2891
- * focus ping-pongs between them and typing into the form is unreliable; under
2892
- * jsdom the same construction deadlocked the test runner. The confirm dialog
2893
- * had this identical bug and was fixed the identical way — see
2894
- * {@link ActionConfirmRunner}.
2895
- */
2896
- onRequestForm?: () => void;
2897
2948
  }
2898
2949
  /**
2899
2950
  * A denial the rule asked to SHOW.
@@ -2905,9 +2956,8 @@ interface ActionButtonProps {
2905
2956
  * a tooltip: the click is already intercepted, so one path serves mouse,
2906
2957
  * keyboard and assistive tech alike.
2907
2958
  *
2908
- * Shared by every leaf renderer (`LinkActionButton` / `HandlerActionButton` /
2909
- * `ModalActionButton` / `FormModalActionButton`) rather than reimplemented in
2910
- * each — one component, both shapes (`asMenuItem` switches between a standalone
2959
+ * Shared by every leaf renderer (`LinkActionButton` / `ModalActionButton` /
2960
+ * the flow button) rather than reimplemented in each — one component, both shapes (`asMenuItem` switches between a standalone
2911
2961
  * `<Button>` and a `<DropdownMenuItem>`).
2912
2962
  *
2913
2963
  * Exported (not a package-private leaf) because a `disabled` `Availability` is
@@ -2929,7 +2979,7 @@ interface DisabledActionProps {
2929
2979
  asMenuItem?: boolean;
2930
2980
  /**
2931
2981
  * Resource the denial belongs to — scopes the reason/fallback key lookup the
2932
- * same way `ResourceForm`'s field-lock reason is scoped, so a resource can
2982
+ * same way the record form's field-lock reason is scoped, so a resource can
2933
2983
  * override the generic wording via `resources.<res>.<key>`.
2934
2984
  */
2935
2985
  resource?: string;
@@ -2952,41 +3002,6 @@ interface DisabledActionProps {
2952
3002
  surface?: string;
2953
3003
  }
2954
3004
  declare function DisabledAction({ reason, label, icon, testid, size, variant, className, asMenuItem, resource, density, actionName, surface }: DisabledActionProps): React.ReactElement;
2955
- interface ActionConfirmDialogProps {
2956
- open: boolean;
2957
- onOpenChange: (open: boolean) => void;
2958
- title: React.ReactNode;
2959
- message: string;
2960
- isDestructive?: boolean;
2961
- busy?: boolean;
2962
- onConfirm: () => void;
2963
- /** Base testid; the dialog and its two buttons suffix `:confirm[-ok|-cancel]`. */
2964
- testid: string;
2965
- /**
2966
- * Label for the confirming button. Prefer the action's own VERB («Выполнить»)
2967
- * over a neutral «ОК»: a button that repeats what is about to happen is
2968
- * readable at a glance and matches the delete dialog, which already confirms
2969
- * with «Удалить». Falls back to the generic wording when absent.
2970
- */
2971
- confirmLabel?: React.ReactNode;
2972
- }
2973
- /**
2974
- * The confirm prompt shown before a confirming action runs.
2975
- *
2976
- * @deprecated Nothing in `@burojs/ui-shadcn` mounts this any more — both
2977
- * action call sites went through `useCheckGate` + `<CheckConfirmDialog>`,
2978
- * which carries the same affordances (`title`, `proceedLabel`, `tone`) and
2979
- * merges `action.confirm` with any firing check into ONE dialog. Still
2980
- * exported for a host that already depends on it directly.
2981
- *
2982
- * Deliberately a standalone component so it can be mounted OUTSIDE a
2983
- * `<DropdownMenuContent>`. Rendering a Radix Dialog inside an open Radix menu
2984
- * mounts two focus scopes / dismissable layers at once, which fight over focus
2985
- * (a hard deadlock under jsdom, broken dismiss behavior in a real browser) and
2986
- * risks the dialog being torn down with the menu. `ActionOverflow` therefore
2987
- * owns one of these as a SIBLING of the menu; see {@link ActionConfirmRunner}.
2988
- */
2989
- declare function ActionConfirmDialog({ open, onOpenChange, title, message, isDestructive, busy, onConfirm, testid, confirmLabel }: ActionConfirmDialogProps): React.ReactElement;
2990
3005
  declare function ActionButton(props: ActionButtonProps): React.ReactElement | null;
2991
3006
  //#endregion
2992
3007
  //#region src/actions/action-overflow.d.ts
@@ -3002,6 +3017,16 @@ interface ActionOverflowProps {
3002
3017
  */
3003
3018
  surface: string;
3004
3019
  className?: string;
3020
+ /**
3021
+ * Flows this screen presents inline (P6 3.12): their own button is not drawn here, the
3022
+ * placement is their control on this screen.
3023
+ */
3024
+ hideFlows?: (name: string) => boolean;
3025
+ /**
3026
+ * A row in a narrow list: every action goes into the one menu, so the actions column is a single
3027
+ * control wide. Default: {@link RowActionsCollapseContext}.
3028
+ */
3029
+ collapse?: boolean;
3005
3030
  }
3006
3031
  /**
3007
3032
  * Collapses an entity's actions into an overflow menu so tables and detail
@@ -3013,14 +3038,14 @@ interface ActionOverflowProps {
3013
3038
  * own control (at their resolved density — icon-only on a row, icon+label on
3014
3039
  * a toolbar); everything else collapses into one menu after them, with the
3015
3040
  * destructive tail set off by a separator. Menu items reuse
3016
- * `<ActionButton asMenuItem>` so every action kind (handler / confirm / link /
3017
- * modal / formModal) keeps working from inside the dropdown. Tone is NOT
3041
+ * `<ActionButton asMenuItem>` for a link or a modal and `<FlowMenuItem>` for a
3042
+ * flow, whose run continues outside the dropdown. Tone is NOT
3018
3043
  * hardcoded here (no more `variant="ghost"`): it comes from each action's own
3019
3044
  * `variant`, exactly like the menu items already respected it — a hardcoded
3020
3045
  * `ghost` used to strip a destructive standalone action of its destructive
3021
3046
  * look.
3022
3047
  */
3023
- declare function ActionOverflow({ resource, record, surface, className }: ActionOverflowProps): React.ReactElement | null;
3048
+ declare function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }: ActionOverflowProps): React.ReactElement | null;
3024
3049
  //#endregion
3025
3050
  //#region src/actions/action-surface.d.ts
3026
3051
  /**
@@ -3063,6 +3088,12 @@ interface BulkActionToolbarProps<TRecord extends BulkRecord = BulkRecord> {
3063
3088
  * itself via `table.resetRowSelection()`.
3064
3089
  */
3065
3090
  onClearSelection?: () => void;
3091
+ /**
3092
+ * Replaces the selection. A bulk flow calls it after its run: the ids that succeeded leave the
3093
+ * selection and the others stay (P4 spec 4.16). Without it, a run that succeeded for every id
3094
+ * clears the selection through `onClearSelection`.
3095
+ */
3096
+ onSelectionChange?: (ids: string[]) => void;
3066
3097
  className?: string;
3067
3098
  /**
3068
3099
  * The list's currently visible columns (Task 8) — forwarded to the
@@ -3070,33 +3101,81 @@ interface BulkActionToolbarProps<TRecord extends BulkRecord = BulkRecord> {
3070
3101
  * what "as shown in the table" (the dialog's default) sends as
3071
3102
  * `request.columns`. Optional: this toolbar is usable standalone, with no
3072
3103
  * `<ResourceListPage>`/list controller in reach — a caller that omits it
3073
- * gets an empty projection for that default, same as any other prop this
3074
- * component has no way to infer on its own.
3104
+ * gets the dialog's "All columns" set for that default: the resource's list
3105
+ * fields, minus any a `column` rule hides.
3075
3106
  */
3076
3107
  visibleColumns?: readonly string[];
3077
3108
  }
3078
- declare function BulkActionToolbar<TRecord extends BulkRecord = BulkRecord>({ resource, table, selectedRecords, selectedIds, onClearSelection, className, visibleColumns }: BulkActionToolbarProps<TRecord>): React.ReactElement | null;
3109
+ declare function BulkActionToolbar<TRecord extends BulkRecord = BulkRecord>({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns }: BulkActionToolbarProps<TRecord>): React.ReactElement | null;
3079
3110
  //#endregion
3080
- //#region src/actions/dispatcher-context.d.ts
3081
- interface DispatcherProviderProps {
3082
- dispatcher: Dispatcher;
3083
- children: ReactNode;
3111
+ //#region src/flows/flow-action-button.d.ts
3112
+ interface FlowActionButtonProps {
3113
+ readonly resource: string;
3114
+ /** The flow's name in the resource's `actions`. */
3115
+ readonly actionName: string;
3116
+ readonly record?: {
3117
+ id: string;
3118
+ } & Record<string, unknown>;
3119
+ /** The surface (`'row'`, `'show'`, …): the test id's namespace, as `ActionButton`'s. */
3120
+ readonly scope?: string;
3121
+ readonly asMenuItem?: boolean;
3122
+ readonly density?: ActionDensity;
3123
+ readonly variant?: ButtonProps['variant'];
3124
+ readonly size?: ButtonProps['size'];
3125
+ readonly className?: string;
3126
+ readonly children?: ReactNode;
3127
+ }
3128
+ /**
3129
+ * A flow's button or menu item (P4 spec 4.15, 4.17). The control is decided without a run — the
3130
+ * flow's gate (undecided while permissions load), the page's one batched read on a row (I8), its
3131
+ * `when` and pre-click checks — so the flow machine is not in the first screen. A hover or a
3132
+ * focus starts loading the flow runtime; the click (at the latest) starts the run, whose `with`
3133
+ * sources load then: until it has decided, the control is busy and a second click does nothing,
3134
+ * so one click sends at most once.
3135
+ */
3136
+ declare function FlowActionButton(props: FlowActionButtonProps): ReactElement | null;
3137
+ //#endregion
3138
+ //#region src/flows/app-flow-button.d.ts
3139
+ interface AppFlowButtonProps {
3140
+ /** The entry `useAppActions()` returned for a `kind: 'flow'` app action. */
3141
+ readonly action: {
3142
+ readonly name: string;
3143
+ readonly label: string;
3144
+ readonly icon?: string;
3145
+ readonly disabledReason?: string;
3146
+ /** A flow hosted on a custom page (P7 3.15): the control is a link to it, not a runner. */
3147
+ readonly hostHref?: string;
3148
+ };
3149
+ readonly variant?: ButtonProps['variant'];
3150
+ readonly size?: ButtonProps['size'];
3151
+ readonly className?: string;
3084
3152
  }
3085
- declare function DispatcherProvider({ dispatcher, children }: DispatcherProviderProps): React.ReactElement;
3086
- declare function useDispatcher(): Dispatcher;
3153
+ /**
3154
+ * An app flow's control (P4-D: app flows are drawn). `useAppActions()` decides whether the
3155
+ * entry exists and whether it is disabled (the flow's gate); this button draws it, loads the
3156
+ * flow runtime on intent (a hover, a focus, at the latest the click) and runs the flow — its
3157
+ * host when it has a form or a review, else the send behind the confirm dialog. Busy from the
3158
+ * click until the interaction ends. Test id: `app-action:<name>`.
3159
+ */
3160
+ declare function AppFlowButton({ action, variant, size, className }: AppFlowButtonProps): ReactElement;
3087
3161
  //#endregion
3088
3162
  //#region src/checks/check-confirm-dialog.d.ts
3089
3163
  interface CheckConfirmDialogProps {
3090
3164
  readonly state: CheckGateState;
3091
- /** Base testid; the dialog and its rows suffix `:dialog`, `:proceed`, `:cancel`, `:item:<checkId>`. */
3165
+ /**
3166
+ * Base testid; the dialog and its rows suffix `:dialog`, `:proceed`,
3167
+ * `:cancel`, `:item:<checkId>`.
3168
+ */
3092
3169
  readonly testid: string;
3093
- /** Fired (before `state.cancel()`) when an anchored row — `path[0]` is a field name — is clicked. */
3170
+ /**
3171
+ * Fired (before `state.cancel()`) when an anchored row — `path[0]` is a
3172
+ * field name — is clicked.
3173
+ */
3094
3174
  readonly onAnchorFocus?: (field: string) => void;
3095
3175
  /**
3096
3176
  * Overrides the neutral `checks.dialogTitle` heading. The ACTION call sites
3097
- * pass the action's own label here (the standalone `ActionConfirmDialog`
3098
- * this component replaced used it as its title), so «Архивировать» still
3099
- * heads an archive prompt instead of a generic "please review".
3177
+ * pass the action's own label here, so «Архивировать» heads an archive
3178
+ * prompt instead of a generic "please review".
3100
3179
  */
3101
3180
  readonly title?: ReactNode;
3102
3181
  /**
@@ -3115,12 +3194,10 @@ interface CheckConfirmDialogProps {
3115
3194
  }
3116
3195
  /**
3117
3196
  * Renders `useCheckGate`'s `state` — one dialog for `action.confirm`
3118
- * headers, blocking checks and confirmable warnings alike (Task 6's
3119
- * contract). Structured after `ActionConfirmDialog`
3120
- * (`packages/ui-shadcn/src/actions/action-button.tsx`): controlled
3121
- * `<Dialog>`, same focus-restore-on-close effect, `data-testid` on the
3122
- * content and both buttons — Task 10 folds the two into one dialog, and
3123
- * staying close in shape/behaviour here is what keeps that merge cheap.
3197
+ * headers, blocking checks and confirmable warnings alike — since P4 the
3198
+ * only confirm dialog an action has (a flow's `confirm` lowers to a check):
3199
+ * a controlled `<Dialog>` with a focus-restore-on-close effect and a
3200
+ * `data-testid` on the content and both buttons.
3124
3201
  *
3125
3202
  * Blockers ('error' severity) are shown first, under `checks.blocked`, and
3126
3203
  * NEVER get a proceed button — `state.proceed()` already no-ops while
@@ -3295,10 +3372,17 @@ interface DataTableProps<TRow> {
3295
3372
  * (except an interactive element — link, button, checkbox, kebab — or while
3296
3373
  * selecting text) navigates to `rowHref(row)`. Cmd/Ctrl-click and middle-click
3297
3374
  * open it in a NEW TAB, matching native link behaviour. Client-side nav uses
3298
- * the injected `useNavigate()`. The row's own ID cell stays a real anchor for
3299
- * keyboard / copy-link, so this is a mouse-affordance layer on top.
3375
+ * `useRouteNavigate()` (the `RouterAdapter`). The row's own ID cell stays a
3376
+ * real anchor for keyboard / copy-link, so this is a mouse-affordance layer on
3377
+ * top.
3300
3378
  */
3301
3379
  rowHref?: (row: TRow) => string | undefined;
3380
+ /**
3381
+ * A row opener asked before `rowHref` on a plain click (P3-17): `true` means it handled the
3382
+ * row (a quick entry opened) and the row does not navigate. A modified click (Cmd/Ctrl,
3383
+ * middle) still opens `rowHref` in a new tab.
3384
+ */
3385
+ onRowOpen?: (row: TRow) => boolean;
3302
3386
  /**
3303
3387
  * Enable drag-to-reorder on column headers (#17). When set, headers become
3304
3388
  * draggable and dropping one onto another calls `onColumnReorder(from, to)`.
@@ -3317,7 +3401,7 @@ interface DataTableProps<TRow> {
3317
3401
  selectionColumnId?: string;
3318
3402
  className?: string;
3319
3403
  }
3320
- declare function DataTable<TRow>({ table, tableTestId, getRowTestId, getCellTestId, isLoading, loadingRowCount, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, selectionColumnId, className }: DataTableProps<TRow>): React.ReactElement;
3404
+ declare function DataTable<TRow>({ table, tableTestId, getRowTestId, getCellTestId, isLoading, loadingRowCount, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }: DataTableProps<TRow>): React.ReactElement;
3321
3405
  //#endregion
3322
3406
  //#region src/data-table/data-table-faceted-filter.d.ts
3323
3407
  /**
@@ -3769,9 +3853,7 @@ declare function ListSearchInput({ resource, label, value, onCommit, placeholder
3769
3853
  * default: `<ResourceListPage>` distinguishes a declarative
3770
3854
  * `views.list.search` (names its first field) from a resolver (generic
3771
3855
  * `list.search` label, since the framework cannot know what a resolver
3772
- * matches — see `RelationTab.search`'s note on why there is no numeric-id
3773
- * branch), and the embedded sub-resource tab always names its one
3774
- * `contains` field.
3856
+ * matches), and a `related-list` placement names its own search field.
3775
3857
  */
3776
3858
  placeholder?: string;
3777
3859
  }): import("react/jsx-runtime").JSX.Element;
@@ -3818,145 +3900,396 @@ declare function ColumnsToggle({ resource, def, allAvailable, required, visibleC
3818
3900
  onRestoreDefaults?: () => void;
3819
3901
  }): import("react/jsx-runtime").JSX.Element;
3820
3902
  //#endregion
3821
- //#region src/list-kit/cursor-load-more.d.ts
3822
- /**
3823
- * Props of {@link CursorLoadMore}.
3824
- *
3825
- * Deliberately shaped as three scalars rather than taking the controller's
3826
- * `cursor` group whole: the group's `next` is an opaque TOKEN (useful to a
3827
- * consumer that wants to log or deep-link it) while this control only ever
3828
- * needs to know whether there IS one. Spreading `cursor` in would also make
3829
- * the component unusable by a surface that paginates a nested/embedded list
3830
- * outside `useResourceList` — the workflows page does exactly that.
3831
- */
3832
- interface CursorLoadMoreProps {
3833
- /** Render the button at all. `false` renders nothing (see the docblock). */
3834
- hasMore: boolean;
3835
- /** A load-more fetch is in flight — disables the button and swaps the label. */
3836
- loading?: boolean;
3837
- /** Fetch the next page. Wire to `cursor.loadMore()`. */
3838
- onMore: () => void;
3839
- }
3840
- /**
3841
- * The "load more" footer for a **cursor (opaque-token) paginated** list —
3842
- * the counterpart of the numbered pager `<ResourceList>` renders for offset
3843
- * providers.
3844
- *
3845
- * ```tsx
3846
- * const { rows, cursor } = useResourceListContext();
3847
- * // …the table…
3848
- * {cursor && (
3849
- * <CursorLoadMore
3850
- * hasMore={cursor.next !== null}
3851
- * loading={cursor.isLoadingMore}
3852
- * onMore={cursor.loadMore}
3853
- * />
3854
- * )}
3855
- * ```
3856
- *
3857
- * ⚠️ **Renders `null` when `hasMore` is false**, so an exhausted cursor
3858
- * leaves no dead control behind — the caller does not need its own guard.
3859
- * That is also what makes "the button is gone" a usable end-of-list
3860
- * assertion in tests.
3861
- *
3862
- * Why it lives in the framework rather than in the app that first needed it
3863
- * (core flexibility program, Task B5.3): it has **zero coupling to any
3864
- * provider**. It knows nothing about Temporal, tokens, or where the next
3865
- * page comes from — only "is there more" / "is it coming" / "go get it". A
3866
- * cursor list is now a first-class declared mode
3867
- * (`views.list.pagination: 'cursor'`), so the control that drives one
3868
- * belongs beside the pager it replaces.
3869
- *
3870
- * @see ResourceListController.cursor in `@burojs/react`
3871
- */
3872
- declare function CursorLoadMore({ hasMore, loading, onMore }: CursorLoadMoreProps): React.ReactElement | null;
3903
+ //#region src/list-kit/pieces/list-body.d.ts
3904
+ declare const ListBody: () => ReactElement | null;
3873
3905
  //#endregion
3874
- //#region src/list-kit/resource-list-page.d.ts
3906
+ //#region src/list-kit/pieces/list-bulk-bar.d.ts
3907
+ declare const ListBulkBar: () => ReactElement | null;
3908
+ //#endregion
3909
+ //#region src/list-kit/pieces/list-columns.d.ts
3910
+ declare const ListColumns: () => ReactElement | null;
3911
+ //#endregion
3912
+ //#region src/list-kit/pieces/list-controls.d.ts
3875
3913
  /**
3876
- * Props of {@link ResourceListPage}.
3914
+ * The toolbar as a frame places it (`<ListControls/>`): under `checkFrame` it draws the pieces it
3915
+ * is made of, so a frame that also places one of them is counted twice.
3877
3916
  */
3878
- interface ResourceListPageProps {
3917
+ declare function ListControls(): ReactElement | null;
3918
+ //#endregion
3919
+ //#region src/list-kit/pieces/list-create.d.ts
3920
+ declare const ListCreate: () => ReactElement | null;
3921
+ //#endregion
3922
+ //#region src/list-kit/pieces/list-export.d.ts
3923
+ declare const ListExport: () => ReactElement | null;
3924
+ //#endregion
3925
+ //#region src/list-kit/pieces/list-filters.d.ts
3926
+ declare const ListFilters: () => ReactElement | null;
3927
+ //#endregion
3928
+ //#region src/list-kit/pieces/list-flows.d.ts
3929
+ declare const ListFlows: () => ReactElement | null;
3930
+ //#endregion
3931
+ //#region src/list-kit/pieces/list-live.d.ts
3932
+ declare const ListLive: () => ReactElement | null;
3933
+ //#endregion
3934
+ //#region src/list-kit/pieces/list-saved-views.d.ts
3935
+ declare const ListSavedViews: () => ReactElement | null;
3936
+ //#endregion
3937
+ //#region src/resource/resource-list.d.ts
3938
+ type RowRecord$1 = {
3939
+ id: string;
3940
+ } & Record<string, unknown>;
3941
+ interface ResourceListProps {
3942
+ resource: string;
3943
+ data?: RowRecord$1[];
3944
+ total?: number;
3945
+ page?: number;
3946
+ pageSize?: number;
3879
3947
  /**
3880
- * Registry name of the resource to list.
3881
- *
3882
- * ⚠️ **Precondition: it MUST be registered.** `useResourceList` resolves
3883
- * `routing.createHref` through `hrefFor`, which throws `unknown node
3884
- * '<resource>'` for a name the registry doesn't know. Route-level code
3885
- * should 404 before mounting this component (the playground's
3886
- * `app/(admin)/[resource]/page.tsx` does exactly that).
3948
+ * Called when the user picks a different page size. The size selector is
3949
+ * rendered ONLY when this is supplied — a list whose host does not persist
3950
+ * page size should not offer a control that silently resets on navigation.
3887
3951
  */
3888
- resource: string;
3952
+ onPageSize?: (size: number) => void;
3889
3953
  /**
3890
- * Extra `meta` merged into the underlying `useList` call — and therefore
3891
- * into the react-query cache key. This is the seam for app-level query
3892
- * scope the framework cannot know about: a sidebar product-group /
3893
- * location picker, a tenant id, anything a data provider turns into a
3894
- * server-side `where`.
3954
+ * Ask the host for the row count of the CURRENTLY described set (Task
3955
+ * B4.3). Rendered ONLY when this is supplied and `total` is unknown — the
3956
+ * same "no control the host cannot honour" gate `onPageSize` above uses.
3895
3957
  *
3896
- * ⚠️ **The caller MUST memoize this object.** It flows into the query key
3897
- * AND into the controller's own memo chain; an inline object literal
3898
- * mints a fresh key on every render, which busts that chain and can
3899
- * produce a refetch loop. The playground memoizes on
3900
- * `[scope.productGroup, scope.locationId]`:
3958
+ * This component is presentational by contract, so the trigger arrives as
3959
+ * a prop rather than being read from `useResourceListContext()`:
3960
+ * `useResourceListContext()` THROWS outside a provider
3961
+ * (`list-kit/context.tsx`), and `<ResourceList>` must stay usable
3962
+ * standalone — a `related-list` placement mounts one with a hand-rolled count and
3963
+ * no controller at all.
3901
3964
  *
3902
- * ```tsx
3903
- * const scope = useScope();
3904
- * const listMeta = useMemo(
3905
- * () => ({ scope: { pg: scope.productGroup, location_id: scope.locationId } }),
3906
- * [scope.productGroup, scope.locationId],
3907
- * );
3908
- * return <ResourceListPage resource={resource} meta={listMeta} />;
3909
- * ```
3965
+ * ⚠️ Answering moves the user, on purpose. The host writes the answer into
3966
+ * `total`, which flips `knownTotal` and with it four behaviours that are
3967
+ * dead on an unknown-total list: `pagination.showing` replaces the
3968
+ * range-only readout, the first/last jump buttons appear, `hasNextPage`
3969
+ * switches from "this page is full" to `page < totalPages`, and the
3970
+ * out-of-range clamp navigates a stale `?page=20` to the last real page.
3971
+ * That last one is the point of offering the trigger on a wrong page at
3972
+ * all.
3910
3973
  */
3911
- meta?: Record<string, unknown>;
3974
+ onRequestCount?: () => void;
3912
3975
  /**
3913
- * App-level per-cell renderer, consulted AFTER this component's own
3914
- * built-in branches (template discriminator → declared `enum` field) and
3915
- * BEFORE `<ResourceList>`'s default field-type display. Return
3916
- * `undefined` to fall through, exactly like `<ResourceList renderCell>`.
3917
- *
3918
- * The built-ins cover what the registry can describe on its own; this
3919
- * prop is for the rest — the playground uses it for a legacy hardcoded
3920
- * `status` badge and a `is_active` Yes/No badge, neither of which the
3921
- * framework can derive from a field def.
3922
- *
3923
- * ⚠️ Wins over a registered `list.cell.<resource>` slot (level 1 —
3924
- * {@link ../list-kit/list-slots.js | listSlot}). A host that mounted this
3925
- * page by hand and passed its own `renderCell` knows more than the
3926
- * registry does; the slot exists for the standard router, which mounts
3927
- * `<ResourceListPage resource={...}>` with no props at all.
3928
- *
3929
- * ⚠️ Same purity requirement as `<ResourceList renderCell>`: it runs
3930
- * inside a memoized cell, so anything depending on external reactive
3931
- * state (locale, flags) must be delegated to a component that
3932
- * subscribes to that state itself.
3976
+ * A count request is in flight. The trigger keeps its place and its width;
3977
+ * only its label is swapped for a spinner, and it stops accepting clicks.
3933
3978
  */
3934
- renderCell?: (col: string, value: unknown, row: RowRecord) => ReactNode | undefined;
3979
+ isCountingRows?: boolean;
3980
+ /** Page-size choices. Defaults to a standard admin ladder. */
3981
+ pageSizeOptions?: number[];
3982
+ onPage?: (n: number) => void;
3935
3983
  /**
3936
- * Replacement for the table's empty state, forwarded to
3937
- * `<ResourceList emptyState>` and from there to `<DataTable emptyState>`.
3984
+ * Opaque-token pagination. When supplied, the numbered pager is replaced by
3985
+ * a load-more control: a token list has no page index to jump to. Absent →
3986
+ * the offset pager renders exactly as before.
3987
+ */
3988
+ loadMore?: {
3989
+ hasMore: boolean;
3990
+ loading: boolean;
3991
+ onMore: () => void;
3992
+ };
3993
+ selectable?: boolean;
3994
+ /**
3995
+ * Accumulating selection owned by the caller. When provided the component
3996
+ * is CONTROLLED: this is the WHOLE selected set (ids that are not on the
3997
+ * current page included), the component keeps no selection state of its
3998
+ * own, and every change — including the page-scoped header checkbox — is
3999
+ * reported through `onSelectionChange` as the new whole set.
3938
4000
  *
3939
- * ⚠️ Supplying this changes the SHAPE of the empty list, not just its
3940
- * copy: the default is a standalone panel rendered INSTEAD of the table
3941
- * (dashed border, icon, heading, and a "Clear filter" / "+ Create" /
3942
- * "back to page 1" CTA), whereas a supplied node renders inside the
3943
- * table body, so the header row survives and the CTAs do not. Pass one
3944
- * when a slot wants the table chrome to stay put; omit it for the
3945
- * standard panel.
4001
+ * Omit it for the uncontrolled behaviour: the component owns the selection
4002
+ * and drops it whenever `data` changes identity.
4003
+ *
4004
+ * ⚠️ A controlled caller MUST commit every emission it receives through
4005
+ * `onSelectionChange` back into this prop: skip one and the `emittedRef`
4006
+ * bridge (below) stays armed against a base the DOM never reflected, so
4007
+ * the next toggle composes against a phantom selection instead of what's
4008
+ * actually checked.
3946
4009
  */
3947
- emptyState?: ReactNode;
3948
- }
3949
- /**
3950
- * The assembled, data-connected list page (core flexibility program, Task
3951
- * B3.1) — the piece that makes `<ResourceList>` actually useful. Given a
3952
- * resource name it renders the whole standard list surface:
3953
- *
3954
- * - page header,
3955
- * - the filter region — search box + pinned controls + the filter-builder
3956
- * popover + applied-filter badges + "Clear all" (via {@link FilterRegion}),
3957
- * - saved-views dropdown and its two dialogs (save-current / manage),
3958
- * - the columns toggle and the create button,
3959
- * - `<ResourceList>` itself (table, empty/error/loading states,
4010
+ selectedIds?: string[];
4011
+ onSelectionChange?: (ids: string[]) => void;
4012
+ className?: string;
4013
+ /** When true, show skeleton rows instead of the table. */
4014
+ isLoading?: boolean;
4015
+ /** When non-null, show an error state. */
4016
+ error?: {
4017
+ message: string;
4018
+ } | Error | null;
4019
+ /**
4020
+ * Optional retry callback. When provided AND `error` is set, the
4021
+ * error-state panel renders a "Retry" button that invokes this
4022
+ * callback. The typical wiring is `onRetry={() => query.refetch()}`
4023
+ * when the parent uses the framework's `useList` (or an equivalent
4024
+ * `refetch()` method on the data source).
4025
+ */
4026
+ onRetry?: () => void;
4027
+ /** Render an inline "+ Create" button in the empty state. */
4028
+ createHref?: string;
4029
+ /** Whether the list is currently filtered (changes empty-state copy). */
4030
+ isFiltered?: boolean;
4031
+ /** Called when the user clicks the "Clear filter" link in the filtered-empty state. */
4032
+ onClearFilter?: () => void;
4033
+ /**
4034
+ * Replacement for the "no rows" rendering, forwarded to
4035
+ * `<DataTable emptyState>`.
4036
+ *
4037
+ * ⚠️ This changes the SHAPE of the empty list, not just its copy. Left
4038
+ * undefined (the default, and what every caller did before the slot work)
4039
+ * this component early-returns a standalone panel INSTEAD of the table —
4040
+ * dashed border, icon, heading, and one of three CTAs ("Clear filter" when
4041
+ * filtered / "+ Create" when genuinely empty / "back to page 1" on an
4042
+ * out-of-range page). Supplying a node skips that panel entirely: the
4043
+ * table renders with its header row and the node fills a single body cell
4044
+ * spanning every column, so the column headers survive but none of the
4045
+ * CTAs do — the caller owns whatever recovery affordance the state needs.
4046
+ *
4047
+ * The footer/pagination is still hidden while there are no rows.
4048
+ */
4049
+ emptyState?: React.ReactNode;
4050
+ /**
4051
+ * Another drawing of the same rows in place of the table (a `related-list` with `view: 'cards'`
4052
+ * or `'timeline'`, P6 3.13). Everything around the rows stays this component's: the loading,
4053
+ * error and empty states, the numbered pager or load-more footer, the on-demand count. The
4054
+ * table's own affordances (selection, column menu, sort headers) do not exist on it.
4055
+ */
4056
+ rowsView?: {
4057
+ readonly render: (rows: RowRecord$1[]) => React.ReactNode;
4058
+ /** Drawn while the first page loads, in place of the table skeleton. */
4059
+ readonly skeleton: React.ReactNode;
4060
+ };
4061
+ /**
4062
+ * If provided, each row becomes a clickable link to this href.
4063
+ * Receives the row record; returns the navigation target.
4064
+ * Default behavior: rows are not clickable. Set to `(row) => '/{resource}/{row.id}'`
4065
+ * for typical show-page navigation.
4066
+ */
4067
+ rowHref?: (row: RowRecord$1) => string | undefined;
4068
+ /**
4069
+ * A row's quick opener (P3-17): when it returns a function, a plain click on the row or its
4070
+ * first cell calls it instead of navigating; the first cell keeps the `rowHref` anchor (marked
4071
+ * for the opener) when there is one, and is a button when there is none. `null`: the row
4072
+ * follows `rowHref`.
4073
+ */
4074
+ rowQuick?: (row: RowRecord$1) => (() => void) | null;
4075
+ /**
4076
+ * Changes whenever `rowHref` or `rowQuick` would answer differently for the same row (an
4077
+ * access answer arrived): the memoized rows and cells re-render.
4078
+ */
4079
+ rowLinkKey?: unknown;
4080
+ /**
4081
+ * Per-cell custom renderer override. Returns `undefined` to fall back to
4082
+ * the default field-type-based display. Use for badges, links, or any
4083
+ * custom visual treatment that depends on column + value + row.
4084
+ *
4085
+ * MUST be a pure function of `(col, value, row)` — the cell it renders runs
4086
+ * inside `<DataTable>`'s memoized row/cell (`MemoRow`/`MemoCell` in
4087
+ * data-table.tsx), which intentionally skips re-rendering when a row's own
4088
+ * data and selection state haven't changed. If the returned output depends
4089
+ * on EXTERNAL reactive state (locale, a feature flag, anything read outside
4090
+ * `col`/`value`/`row`), it will go stale under that memo the same way the
4091
+ * built-in `DateDisplay`/`NumberDisplay` renderers did before they were
4092
+ * fixed to self-subscribe to locale. Delegate any such state-dependent
4093
+ * text to a component that subscribes to it itself (`useLocale()` /
4094
+ * `useOptionalT()` — see `EnumDisplay`/`BooleanDisplay`/`DateDisplay` for
4095
+ * the pattern) rather than resolving it inline in this callback.
4096
+ */
4097
+ renderCell?: (col: string, value: unknown, row: RowRecord$1) => React.ReactNode | undefined;
4098
+ /** Current native sort expression. The header exposes the first sort key. */
4099
+ sort?: readonly {
4100
+ field: string;
4101
+ direction: 'asc' | 'desc';
4102
+ }[];
4103
+ /**
4104
+ * Sort request from a column header. `direction` is EXPLICIT when the header
4105
+ * menu picks «по возрастанию/убыванию/сбросить» (`'asc'`/`'desc'`/`null`); it
4106
+ * is omitted when a caller wants the legacy 3-click toggle. Consumers should
4107
+ * honour an explicit direction and fall back to toggling when it's undefined.
4108
+ */
4109
+ onSort?: (field: string, direction?: 'asc' | 'desc' | null) => void;
4110
+ /**
4111
+ * Hide a column from a column-header menu. Receives the column name; the
4112
+ * parent removes it from the visible-columns list. When omitted, the header
4113
+ * menu shows no «hide» item.
4114
+ */
4115
+ onHideColumn?: (field: string) => void;
4116
+ /**
4117
+ * Columns that may NOT be hidden — the header menu omits «hide» for these.
4118
+ * Without it every column is treated as hideable (when `onHideColumn` is set).
4119
+ */
4120
+ requiredColumns?: string[];
4121
+ /**
4122
+ * Bump to reset the persisted column layout (#17). `resetLayout.mode`:
4123
+ * - `'sizes'` — clear user-resized widths only.
4124
+ * - `'all'` — clear widths AND pinning AND order back to defaults.
4125
+ * The token is what triggers it; increment it on each reset click.
4126
+ */
4127
+ resetLayout?: {
4128
+ token: number;
4129
+ mode: 'sizes' | 'all';
4130
+ };
4131
+ /**
4132
+ * Override for the column list rendered in the table. When omitted,
4133
+ * the table falls back to `def.views.list.columns` (or all field
4134
+ * keys). When provided, the table renders EXACTLY this set in the
4135
+ * given order — useful for the user-toggleable Columns menu (see
4136
+ * `views.list.optionalColumns` / `requiredColumns`).
4137
+ */
4138
+ columns?: string[];
4139
+ /**
4140
+ * Optional callback that hands the parent the underlying TanStack
4141
+ * `Table` instance. Fires once after the table is mounted (and
4142
+ * again with `null` on unmount). Use this when the parent needs to
4143
+ * read selection state for components that live OUTSIDE the table
4144
+ * (e.g. `<BulkActionToolbar>` which calls
4145
+ * `table.getFilteredSelectedRowModel().rows`). The id-array
4146
+ * `onSelectionChange` callback remains the recommended way to
4147
+ * trigger a parent re-render on selection changes — `tableRef`
4148
+ * gives access to the rows, not a re-render signal.
4149
+ */
4150
+ tableRef?: (table: Table$1<RowRecord$1> | null) => void;
4151
+ }
4152
+ declare function ResourceList({ resource, data, total, page, pageSize, onPageSize, onRequestCount, isCountingRows, pageSizeOptions, onPage, loadMore, selectable, selectedIds, onSelectionChange, className, isLoading, error, onRetry, createHref, isFiltered, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }: ResourceListProps): React.ReactElement;
4153
+ //#endregion
4154
+ //#region src/list-kit/cursor-load-more.d.ts
4155
+ /**
4156
+ * Props of {@link CursorLoadMore}.
4157
+ *
4158
+ * Deliberately shaped as three scalars rather than taking the controller's
4159
+ * `cursor` group whole: the group's `next` is an opaque TOKEN (useful to a
4160
+ * consumer that wants to log or deep-link it) while this control only ever
4161
+ * needs to know whether there IS one. Spreading `cursor` in would also make
4162
+ * the component unusable by a surface that paginates a nested/embedded list
4163
+ * outside `useResourceList` — the workflows page does exactly that.
4164
+ */
4165
+ interface CursorLoadMoreProps {
4166
+ /** Render the button at all. `false` renders nothing (see the docblock). */
4167
+ hasMore: boolean;
4168
+ /** A load-more fetch is in flight — disables the button and swaps the label. */
4169
+ loading?: boolean;
4170
+ /** Fetch the next page. Wire to `cursor.loadMore()`. */
4171
+ onMore: () => void;
4172
+ }
4173
+ /**
4174
+ * The "load more" footer for a **cursor (opaque-token) paginated** list —
4175
+ * the counterpart of the numbered pager `<ResourceList>` renders for offset
4176
+ * providers.
4177
+ *
4178
+ * ```tsx
4179
+ * const { rows, cursor } = useResourceListContext();
4180
+ * // …the table…
4181
+ * {cursor && (
4182
+ * <CursorLoadMore
4183
+ * hasMore={cursor.next !== null}
4184
+ * loading={cursor.isLoadingMore}
4185
+ * onMore={cursor.loadMore}
4186
+ * />
4187
+ * )}
4188
+ * ```
4189
+ *
4190
+ * ⚠️ **Renders `null` when `hasMore` is false**, so an exhausted cursor
4191
+ * leaves no dead control behind — the caller does not need its own guard.
4192
+ * That is also what makes "the button is gone" a usable end-of-list
4193
+ * assertion in tests.
4194
+ *
4195
+ * Why it lives in the framework rather than in the app that first needed it
4196
+ * (core flexibility program, Task B5.3): it has **zero coupling to any
4197
+ * provider**. It knows nothing about Temporal, tokens, or where the next
4198
+ * page comes from — only "is there more" / "is it coming" / "go get it". A
4199
+ * cursor list is now a first-class declared mode
4200
+ * (`views.list.pagination: 'cursor'`), so the control that drives one
4201
+ * belongs beside the pager it replaces.
4202
+ *
4203
+ * @see ResourceListController.cursor in `@burojs/react`
4204
+ */
4205
+ declare function CursorLoadMore({ hasMore, loading, onMore }: CursorLoadMoreProps): React.ReactElement | null;
4206
+ //#endregion
4207
+ //#region src/list-kit/resource-list-page.d.ts
4208
+ /**
4209
+ * Props of {@link ResourceListPage}.
4210
+ */
4211
+ interface ResourceListPageProps {
4212
+ /**
4213
+ * Registry name of the resource to list.
4214
+ *
4215
+ * ⚠️ **Precondition: it MUST be registered.** `useResourceList` resolves
4216
+ * `routing.createHref` through `hrefFor`, which throws `unknown node
4217
+ * '<resource>'` for a name the registry doesn't know. Route-level code
4218
+ * should 404 before mounting this component (the playground's
4219
+ * `app/(admin)/[resource]/page.tsx` does exactly that).
4220
+ */
4221
+ resource: string;
4222
+ /**
4223
+ * Extra `meta` merged into the underlying `useList` call — and therefore
4224
+ * into the react-query cache key. This is the seam for app-level query
4225
+ * scope the framework cannot know about: a sidebar product-group /
4226
+ * location picker, a tenant id, anything a data provider turns into a
4227
+ * server-side `where`.
4228
+ *
4229
+ * ⚠️ **The caller MUST memoize this object.** It flows into the query key
4230
+ * AND into the controller's own memo chain; an inline object literal
4231
+ * mints a fresh key on every render, which busts that chain and can
4232
+ * produce a refetch loop. The playground memoizes on
4233
+ * `[scope.productGroup, scope.locationId]`:
4234
+ *
4235
+ * ```tsx
4236
+ * const scope = useScope();
4237
+ * const listMeta = useMemo(
4238
+ * () => ({ scope: { pg: scope.productGroup, location_id: scope.locationId } }),
4239
+ * [scope.productGroup, scope.locationId],
4240
+ * );
4241
+ * return <ResourceListPage resource={resource} meta={listMeta} />;
4242
+ * ```
4243
+ */
4244
+ meta?: Record<string, unknown>;
4245
+ /**
4246
+ * App-level per-cell renderer, consulted AFTER this component's own
4247
+ * built-in branches (typed discriminator → declared `enum` field) and
4248
+ * BEFORE `<ResourceList>`'s default field-type display. Return
4249
+ * `undefined` to fall through, exactly like `<ResourceList renderCell>`.
4250
+ *
4251
+ * The built-ins cover what the registry can describe on its own; this
4252
+ * prop is for the rest — the playground uses it for a legacy hardcoded
4253
+ * `status` badge and a `is_active` Yes/No badge, neither of which the
4254
+ * framework can derive from a field def.
4255
+ *
4256
+ * ⚠️ Wins over a registered `list.cell.<resource>` slot (level 1 —
4257
+ * {@link ../list-kit/list-slots.js | listSlot}). A host that mounted this
4258
+ * page by hand and passed its own `renderCell` knows more than the
4259
+ * registry does; the slot exists for the standard router, which mounts
4260
+ * `<ResourceListPage resource={...}>` with no props at all.
4261
+ *
4262
+ * ⚠️ Same purity requirement as `<ResourceList renderCell>`: it runs
4263
+ * inside a memoized cell, so anything depending on external reactive
4264
+ * state (locale, flags) must be delegated to a component that
4265
+ * subscribes to that state itself.
4266
+ */
4267
+ renderCell?: (col: string, value: unknown, row: RowRecord) => ReactNode | undefined;
4268
+ /**
4269
+ * Replacement for the table's empty state, forwarded to
4270
+ * `<ResourceList emptyState>` and from there to `<DataTable emptyState>`.
4271
+ *
4272
+ * ⚠️ Supplying this changes the SHAPE of the empty list, not just its
4273
+ * copy: the default is a standalone panel rendered INSTEAD of the table
4274
+ * (dashed border, icon, heading, and a "Clear filter" / "+ Create" /
4275
+ * "back to page 1" CTA), whereas a supplied node renders inside the
4276
+ * table body, so the header row survives and the CTAs do not. Pass one
4277
+ * when a slot wants the table chrome to stay put; omit it for the
4278
+ * standard panel.
4279
+ */
4280
+ emptyState?: ReactNode;
4281
+ }
4282
+ /**
4283
+ * The assembled, data-connected list page (core flexibility program, Task
4284
+ * B3.1) — the piece that makes `<ResourceList>` actually useful. Given a
4285
+ * resource name it renders the whole standard list surface:
4286
+ *
4287
+ * - page header,
4288
+ * - the filter region — search box + pinned controls + the filter-builder
4289
+ * popover + applied-filter badges + "Clear all" (via {@link FilterRegion}),
4290
+ * - saved-views dropdown and its two dialogs (save-current / manage),
4291
+ * - the columns toggle and the create button,
4292
+ * - `<ResourceList>` itself (table, empty/error/loading states,
3960
4293
  * pagination),
3961
4294
  * - the floating bulk-action bar.
3962
4295
  *
@@ -3969,6 +4302,11 @@ interface ResourceListPageProps {
3969
4302
  * — the filter region's OWN testids moved to the builder's `list-filter:*`
3970
4303
  * convention; everything else is unchanged).
3971
4304
  *
4305
+ * It renders through the built-in `related-list` block (spec 8.4, decision 63): the view is
4306
+ * `buro:related-list`'s, inside a `[data-buro-node="place"]` wrapper, and an app that registers
4307
+ * its own `buro:related-list` view (`<BuroProvider blockImplementations>`) replaces this list
4308
+ * everywhere, a development notice saying so. The body itself lives in `ResourceListSurface`.
4309
+ *
3972
4310
  * The whole subtree is wrapped in `<ResourceListProvider>`, so any nested
3973
4311
  * component can read the live controller with `useResourceListContext()`
3974
4312
  * instead of receiving a hand-threaded prop chain. That is the seam the
@@ -3982,7 +4320,7 @@ interface ResourceListPageProps {
3982
4320
  * covers:
3983
4321
  *
3984
4322
  * - **`list.cell.<resource>`** (level 1) is consulted for every cell, AFTER
3985
- * this component's own built-in branches (template discriminator →
4323
+ * this component's own built-in branches (typed discriminator →
3986
4324
  * declared `enum` field) and BEFORE `<ResourceList>`'s default field-type
3987
4325
  * display — the same position the `renderCell` prop occupies, and the
3988
4326
  * explicit prop wins when both are present. Everything else — filters,
@@ -3991,17 +4329,13 @@ interface ResourceListPageProps {
3991
4329
  * the search box, the pinned controls, the filter-builder popover and its
3992
4330
  * applied-filter badges. Saved views, the columns toggle, the create
3993
4331
  * button, the table, the footer and pagination all stay standard.
3994
- * - **`list.body.<resource>`** (level 3) replaces everything below the
3995
- * page header: the whole toolbar row, the saved-view dialogs, the
3996
- * table, the footer and the bulk bar.
3997
- *
3998
- * ⚠️ Level 3 keeps the `<PageHeader>` deliberately. It is what writes
3999
- * `document.title` (via `useDocumentTitle`, defended against Next's
4000
- * metadata re-application) and what renders the page's accessible `<h1>`
4001
- * and its breadcrumb slot — and the spec's level-3 guarantee is that
4002
- * routing, data loading, redirects and BREADCRUMBS stay framework-owned:
4003
- * "you lose only the default assembled markup". A slot that wants a
4004
- * visible heading of its own renders one; the header's `h1` is `sr-only`.
4332
+ * - A whole-body override is a list frame (`views.list.frame`, P7-34), which arranges the
4333
+ * framework's pieces and keeps the page header, the controller, the URL state, the saved-view
4334
+ * dialogs, the export watcher and the bulk bar. `list.body.<resource>` is gone (P7-37).
4335
+ *
4336
+ * ⚠️ The `<PageHeader>` stays framework-owned. It is what writes `document.title` (via
4337
+ * `useDocumentTitle`, defended against Next's metadata re-application) and what renders the
4338
+ * page's accessible `<h1>` and its breadcrumb slot; the header's `h1` is `sr-only`.
4005
4339
  *
4006
4340
  * Both slots take no props and read the controller from
4007
4341
  * `useResourceListContext()`, so each is unit-testable against a
@@ -4035,7 +4369,13 @@ declare function ResourceListPage({ resource, meta, renderCell, emptyState }: Re
4035
4369
  * no `list.filters.<resource>` slot is registered, so this component itself
4036
4370
  * never has to know about the override ladder.
4037
4371
  */
4038
- declare function FilterRegion(): React.ReactElement;
4372
+ declare function FilterRegion({ search }?: {
4373
+ /**
4374
+ * The search a placement declares (`related-list`'s `search` prop), `null` for none; the route
4375
+ * list omits it and reads the resource's `views.list.search`.
4376
+ */
4377
+ readonly search?: SearchDecl | null;
4378
+ }): React.ReactElement;
4039
4379
  //#endregion
4040
4380
  //#region src/list-kit/filter-builder/filter-builder.d.ts
4041
4381
  type Expression$2 = FilterExpression<ErasedResourceHandle>;
@@ -4279,321 +4619,140 @@ interface PinnedFilterControlsProps {
4279
4619
  * `contains`) — is left completely alone: not read for display, not
4280
4620
  * overwritten, not removed. This is what stops a pinned control from
4281
4621
  * silently eating a call-site filter it does not own, or REWRITING one
4282
- * it merely resembles — an earlier version of this rule matched on
4283
- * `field` alone, so the first edit through a pinned control would adopt
4284
- * and silently rewrite the operator of an expression it merely happened
4285
- * to share a field with (SP2 final-review finding 4); the operator check
4286
- * is what makes an unowned same-field expression fall through to
4287
- * `FilterBadges`'s "nobody claimed this" badge instead.
4288
- *
4289
- * The operator used to read/write the owned expression is always
4290
- * `spec.operators[0]` — the control renders no operator picker, so it has
4291
- * no other operator to use even if some OTHER expression on the same field
4292
- * happens to carry a different one.
4293
- */
4294
- declare function PinnedFilterControls(props: PinnedFilterControlsProps): React.ReactElement;
4295
- //#endregion
4296
- //#region src/list-kit/filter-builder/values/filter-value-control.d.ts
4297
- interface FilterValueControlProps {
4298
- readonly kind: FilterKind;
4299
- readonly op: string;
4300
- readonly value: PortableValue | undefined;
4301
- readonly onChange: (value: PortableValue | undefined) => void;
4302
- /** `data-testid` prefix; inner parts append `:option:<v>`, `:search`, `:clear`. */
4303
- readonly testId: string;
4304
- /**
4305
- * The field's own label (`FilterFieldSpec.label`), threaded through so
4306
- * this control's accessible name can say WHICH field it is a value for —
4307
- * "Value for Status", not a bare "Value" indistinguishable from every
4308
- * other row's value control and, worse, from every OTHER pinned control
4309
- * in the toolbar (SP2 final-review finding 7). Optional and additive: a
4310
- * caller with no field label in hand (or a standalone/test mount) still
4311
- * gets the plain generic label every existing test asserts on.
4312
- */
4313
- readonly fieldLabel?: string;
4314
- readonly autoFocus?: boolean;
4315
- /**
4316
- * Delay (ms) before a scalar single-value edit (`ScalarValueInput`'s
4317
- * text/number/date box) commits upward. Only that control debounces —
4318
- * enum/relation/boolean commit on selection already, so they ignore this.
4319
- * Defaults to 300 (the legacy chip UX: don't spam the URL/provider on
4320
- * every keystroke). Pass `0` for a purely local draft — the filter-builder
4321
- * panel (`FilterPanel`) does, since its draft is in-memory React state,
4322
- * not a query param, so committing every keystroke is cheap and correct;
4323
- * a pinned control (`PinnedFilterControls`), which writes straight to the
4324
- * URL/provider, keeps the default.
4325
- */
4326
- readonly debounceMs?: number;
4327
- /**
4328
- * `FilterFieldSpec.options` (`@burojs/react`), threaded straight through
4329
- * from `FilterFieldRef.options` (`@burojs/core`). When present AND
4330
- * non-empty, THIS wins over the `kind`-derived dispatch below — a picker
4331
- * over exactly these value/label pairs, reusing `EnumValueControl`'s own
4332
- * popover (`SelectListValueControl`) rather than a free-text box,
4333
- * regardless of `kind`. `kind` itself is untouched by `options`: a plain
4334
- * `string` field with `options` still categorises (and renders its list
4335
- * column) as a `string`, never as `enum` — see `FilterFieldRef.options`'s
4336
- * own doc for why that distinction is the entire point.
4337
- *
4338
- * An EMPTY array falls through to the `kind`-derived dispatch exactly as
4339
- * `undefined` does — a picker with nothing to pick from is strictly worse
4340
- * than the field's ordinary control, not a valid (if degenerate) state.
4341
- */
4342
- readonly options?: readonly {
4343
- readonly value: string;
4344
- readonly label: string;
4345
- }[];
4346
- /**
4347
- * Stretch to the width of whatever contains this control, instead of the
4348
- * intrinsic width each kind picks for itself.
4349
- *
4350
- * The builder's rows (`FilterRow`) need it: their value column is one
4351
- * shared flex track, and a control that sizes itself leaves every row's
4352
- * remove button at a different x — which is exactly what made a panel with
4353
- * more than two rows look shattered. The pinned toolbar
4354
- * (`PinnedFilterControls`) must NOT have it: those controls sit inline
4355
- * among other toolbar items with no column to fill, and stretching them
4356
- * there would be the same bug with the roles reversed. So the width comes
4357
- * from the CALLER — the only place that knows whether there is a column —
4358
- * rather than from a blanket `w-full` on the controls themselves.
4359
- */
4360
- readonly fill?: boolean;
4361
- }
4362
- /**
4363
- * The value half of a builder row.
4364
- *
4365
- * Arity comes from the OPERATOR, the input type from the FIELD — the two
4366
- * are independent, which is why `in` on a string field is a chip list while
4367
- * `contains` on the same field is a text box, and why `isNull` renders
4368
- * nothing at all rather than a disabled input.
4369
- *
4370
- * Deliberately free of TanStack: these controls replace chips that read and
4371
- * wrote through `column.getFilterValue()`, and that coupling is exactly what
4372
- * kept them out of the relation picker (SP3).
4373
- */
4374
- declare function FilterValueControl(props: FilterValueControlProps): React.ReactElement | null;
4375
- //#endregion
4376
- //#region src/resource/resource-list.d.ts
4377
- type RowRecord$1 = {
4378
- id: string;
4379
- } & Record<string, unknown>;
4380
- interface ResourceListProps {
4381
- resource: string;
4382
- data?: RowRecord$1[];
4383
- total?: number;
4384
- page?: number;
4385
- pageSize?: number;
4386
- /**
4387
- * Called when the user picks a different page size. The size selector is
4388
- * rendered ONLY when this is supplied — a list whose host does not persist
4389
- * page size should not offer a control that silently resets on navigation.
4390
- */
4391
- onPageSize?: (size: number) => void;
4392
- /**
4393
- * Ask the host for the row count of the CURRENTLY described set (Task
4394
- * B4.3). Rendered ONLY when this is supplied and `total` is unknown — the
4395
- * same "no control the host cannot honour" gate `onPageSize` above uses.
4396
- *
4397
- * This component is presentational by contract, so the trigger arrives as
4398
- * a prop rather than being read from `useResourceListContext()`:
4399
- * `useResourceListContext()` THROWS outside a provider
4400
- * (`list-kit/context.tsx`), and `<ResourceList>` must stay usable
4401
- * standalone — `SubresourceTabBody` mounts one with a hand-rolled count and
4402
- * no controller at all.
4403
- *
4404
- * ⚠️ Answering moves the user, on purpose. The host writes the answer into
4405
- * `total`, which flips `knownTotal` and with it four behaviours that are
4406
- * dead on an unknown-total list: `pagination.showing` replaces the
4407
- * range-only readout, the first/last jump buttons appear, `hasNextPage`
4408
- * switches from "this page is full" to `page < totalPages`, and the
4409
- * out-of-range clamp navigates a stale `?page=20` to the last real page.
4410
- * That last one is the point of offering the trigger on a wrong page at
4411
- * all.
4412
- */
4413
- onRequestCount?: () => void;
4414
- /**
4415
- * A count request is in flight. The trigger keeps its place and its width;
4416
- * only its label is swapped for a spinner, and it stops accepting clicks.
4417
- */
4418
- isCountingRows?: boolean;
4419
- /** Page-size choices. Defaults to a standard admin ladder. */
4420
- pageSizeOptions?: number[];
4421
- onPage?: (n: number) => void;
4422
- /**
4423
- * Opaque-token pagination. When supplied, the numbered pager is replaced by
4424
- * a load-more control: a token list has no page index to jump to. Absent →
4425
- * the offset pager renders exactly as before.
4426
- */
4427
- loadMore?: {
4428
- hasMore: boolean;
4429
- loading: boolean;
4430
- onMore: () => void;
4431
- };
4432
- selectable?: boolean;
4433
- /**
4434
- * Accumulating selection owned by the caller. When provided the component
4435
- * is CONTROLLED: this is the WHOLE selected set (ids that are not on the
4436
- * current page included), the component keeps no selection state of its
4437
- * own, and every change — including the page-scoped header checkbox — is
4438
- * reported through `onSelectionChange` as the new whole set.
4439
- *
4440
- * Omit it for the uncontrolled behaviour: the component owns the selection
4441
- * and drops it whenever `data` changes identity.
4442
- *
4443
- * ⚠️ A controlled caller MUST commit every emission it receives through
4444
- * `onSelectionChange` back into this prop: skip one and the `emittedRef`
4445
- * bridge (below) stays armed against a base the DOM never reflected, so
4446
- * the next toggle composes against a phantom selection instead of what's
4447
- * actually checked.
4448
- */
4449
- selectedIds?: string[];
4450
- onSelectionChange?: (ids: string[]) => void;
4451
- className?: string;
4452
- /** When true, show skeleton rows instead of the table. */
4453
- isLoading?: boolean;
4454
- /** When non-null, show an error state. */
4455
- error?: {
4456
- message: string;
4457
- } | Error | null;
4458
- /**
4459
- * Optional retry callback. When provided AND `error` is set, the
4460
- * error-state panel renders a "Retry" button that invokes this
4461
- * callback. The typical wiring is `onRetry={() => query.refetch()}`
4462
- * when the parent uses the framework's `useList` (or an equivalent
4463
- * `refetch()` method on the data source).
4464
- */
4465
- onRetry?: () => void;
4466
- /** Render an inline "+ Create" button in the empty state. */
4467
- createHref?: string;
4468
- /** Whether the list is currently filtered (changes empty-state copy). */
4469
- isFiltered?: boolean;
4470
- /** Called when the user clicks the "Clear filter" link in the filtered-empty state. */
4471
- onClearFilter?: () => void;
4472
- /**
4473
- * Replacement for the "no rows" rendering, forwarded to
4474
- * `<DataTable emptyState>`.
4475
- *
4476
- * ⚠️ This changes the SHAPE of the empty list, not just its copy. Left
4477
- * undefined (the default, and what every caller did before the slot work)
4478
- * this component early-returns a standalone panel INSTEAD of the table —
4479
- * dashed border, icon, heading, and one of three CTAs ("Clear filter" when
4480
- * filtered / "+ Create" when genuinely empty / "back to page 1" on an
4481
- * out-of-range page). Supplying a node skips that panel entirely: the
4482
- * table renders with its header row and the node fills a single body cell
4483
- * spanning every column, so the column headers survive but none of the
4484
- * CTAs do — the caller owns whatever recovery affordance the state needs.
4485
- *
4486
- * The footer/pagination is still hidden while there are no rows.
4487
- */
4488
- emptyState?: React.ReactNode;
4489
- /**
4490
- * If provided, each row becomes a clickable link to this href.
4491
- * Receives the row record; returns the navigation target.
4492
- * Default behavior: rows are not clickable. Set to `(row) => '/{resource}/{row.id}'`
4493
- * for typical show-page navigation.
4494
- */
4495
- rowHref?: (row: RowRecord$1) => string | undefined;
4496
- /**
4497
- * Per-cell custom renderer override. Returns `undefined` to fall back to
4498
- * the default field-type-based display. Use for badges, links, or any
4499
- * custom visual treatment that depends on column + value + row.
4500
- *
4501
- * MUST be a pure function of `(col, value, row)` — the cell it renders runs
4502
- * inside `<DataTable>`'s memoized row/cell (`MemoRow`/`MemoCell` in
4503
- * data-table.tsx), which intentionally skips re-rendering when a row's own
4504
- * data and selection state haven't changed. If the returned output depends
4505
- * on EXTERNAL reactive state (locale, a feature flag, anything read outside
4506
- * `col`/`value`/`row`), it will go stale under that memo the same way the
4507
- * built-in `DateDisplay`/`NumberDisplay` renderers did before they were
4508
- * fixed to self-subscribe to locale. Delegate any such state-dependent
4509
- * text to a component that subscribes to it itself (`useLocale()` /
4510
- * `useOptionalT()` — see `EnumDisplay`/`BooleanDisplay`/`DateDisplay` for
4511
- * the pattern) rather than resolving it inline in this callback.
4512
- */
4513
- renderCell?: (col: string, value: unknown, row: RowRecord$1) => React.ReactNode | undefined;
4514
- /** Current native sort expression. The header exposes the first sort key. */
4515
- sort?: readonly {
4516
- field: string;
4517
- direction: 'asc' | 'desc';
4518
- }[];
4519
- /**
4520
- * Sort request from a column header. `direction` is EXPLICIT when the header
4521
- * menu picks «по возрастанию/убыванию/сбросить» (`'asc'`/`'desc'`/`null`); it
4522
- * is omitted when a caller wants the legacy 3-click toggle. Consumers should
4523
- * honour an explicit direction and fall back to toggling when it's undefined.
4524
- */
4525
- onSort?: (field: string, direction?: 'asc' | 'desc' | null) => void;
4622
+ * it merely resembles — an earlier version of this rule matched on
4623
+ * `field` alone, so the first edit through a pinned control would adopt
4624
+ * and silently rewrite the operator of an expression it merely happened
4625
+ * to share a field with (SP2 final-review finding 4); the operator check
4626
+ * is what makes an unowned same-field expression fall through to
4627
+ * `FilterBadges`'s "nobody claimed this" badge instead.
4628
+ *
4629
+ * The operator used to read/write the owned expression is always
4630
+ * `spec.operators[0]` — the control renders no operator picker, so it has
4631
+ * no other operator to use even if some OTHER expression on the same field
4632
+ * happens to carry a different one.
4633
+ */
4634
+ declare function PinnedFilterControls(props: PinnedFilterControlsProps): React.ReactElement;
4635
+ //#endregion
4636
+ //#region src/list-kit/filter-builder/values/filter-value-control.d.ts
4637
+ interface FilterValueControlProps {
4638
+ readonly kind: FilterKind;
4639
+ readonly op: string;
4640
+ readonly value: PortableValue | undefined;
4641
+ readonly onChange: (value: PortableValue | undefined) => void;
4642
+ /** `data-testid` prefix; inner parts append `:option:<v>`, `:search`, `:clear`. */
4643
+ readonly testId: string;
4526
4644
  /**
4527
- * Hide a column from a column-header menu. Receives the column name; the
4528
- * parent removes it from the visible-columns list. When omitted, the header
4529
- * menu shows no «hide» item.
4645
+ * The field's own label (`FilterFieldSpec.label`), threaded through so
4646
+ * this control's accessible name can say WHICH field it is a value for —
4647
+ * "Value for Status", not a bare "Value" indistinguishable from every
4648
+ * other row's value control and, worse, from every OTHER pinned control
4649
+ * in the toolbar (SP2 final-review finding 7). Optional and additive: a
4650
+ * caller with no field label in hand (or a standalone/test mount) still
4651
+ * gets the plain generic label every existing test asserts on.
4530
4652
  */
4531
- onHideColumn?: (field: string) => void;
4653
+ readonly fieldLabel?: string;
4532
4654
  /**
4533
- * Columns that may NOT be hidden — the header menu omits «hide» for these.
4534
- * Without it every column is treated as hideable (when `onHideColumn` is set).
4655
+ * Visible hint inside a text/number box (`ScalarValueInput`). Only a caller
4656
+ * that shows the control with nothing naming its field beside it passes one:
4657
+ * the pinned toolbar (`PinnedFilterControls`) gives the field's label, so a
4658
+ * pinned text filter is not an unlabelled empty box (REF-44). A builder row
4659
+ * names its field in its own field picker and passes none.
4535
4660
  */
4536
- requiredColumns?: string[];
4661
+ readonly placeholder?: string;
4662
+ readonly autoFocus?: boolean;
4537
4663
  /**
4538
- * Bump to reset the persisted column layout (#17). `resetLayout.mode`:
4539
- * - `'sizes'` — clear user-resized widths only.
4540
- * - `'all'` — clear widths AND pinning AND order back to defaults.
4541
- * The token is what triggers it; increment it on each reset click.
4664
+ * Delay (ms) before a scalar single-value edit (`ScalarValueInput`'s
4665
+ * text/number/date box) commits upward. Only that control debounces —
4666
+ * enum/relation/boolean commit on selection already, so they ignore this.
4667
+ * Defaults to 300 (the legacy chip UX: don't spam the URL/provider on
4668
+ * every keystroke). Pass `0` for a purely local draft — the filter-builder
4669
+ * panel (`FilterPanel`) does, since its draft is in-memory React state,
4670
+ * not a query param, so committing every keystroke is cheap and correct;
4671
+ * a pinned control (`PinnedFilterControls`), which writes straight to the
4672
+ * URL/provider, keeps the default.
4542
4673
  */
4543
- resetLayout?: {
4544
- token: number;
4545
- mode: 'sizes' | 'all';
4546
- };
4674
+ readonly debounceMs?: number;
4547
4675
  /**
4548
- * Override for the column list rendered in the table. When omitted,
4549
- * the table falls back to `def.views.list.columns` (or all field
4550
- * keys). When provided, the table renders EXACTLY this set in the
4551
- * given order — useful for the user-toggleable Columns menu (see
4552
- * `views.list.optionalColumns` / `requiredColumns`).
4676
+ * `FilterFieldSpec.options` (`@burojs/react`), threaded straight through
4677
+ * from `FilterFieldRef.options` (`@burojs/core`). When present AND
4678
+ * non-empty, THIS wins over the `kind`-derived dispatch below — a picker
4679
+ * over exactly these value/label pairs, reusing `EnumValueControl`'s own
4680
+ * popover (`SelectListValueControl`) rather than a free-text box,
4681
+ * regardless of `kind`. `kind` itself is untouched by `options`: a plain
4682
+ * `string` field with `options` still categorises (and renders its list
4683
+ * column) as a `string`, never as `enum` — see `FilterFieldRef.options`'s
4684
+ * own doc for why that distinction is the entire point.
4685
+ *
4686
+ * An EMPTY array falls through to the `kind`-derived dispatch exactly as
4687
+ * `undefined` does — a picker with nothing to pick from is strictly worse
4688
+ * than the field's ordinary control, not a valid (if degenerate) state.
4553
4689
  */
4554
- columns?: string[];
4690
+ readonly options?: readonly {
4691
+ readonly value: string;
4692
+ readonly label: string;
4693
+ }[];
4555
4694
  /**
4556
- * Optional callback that hands the parent the underlying TanStack
4557
- * `Table` instance. Fires once after the table is mounted (and
4558
- * again with `null` on unmount). Use this when the parent needs to
4559
- * read selection state for components that live OUTSIDE the table
4560
- * (e.g. `<BulkActionToolbar>` which calls
4561
- * `table.getFilteredSelectedRowModel().rows`). The id-array
4562
- * `onSelectionChange` callback remains the recommended way to
4563
- * trigger a parent re-render on selection changes — `tableRef`
4564
- * gives access to the rows, not a re-render signal.
4695
+ * Stretch to the width of whatever contains this control, instead of the
4696
+ * intrinsic width each kind picks for itself.
4697
+ *
4698
+ * The builder's rows (`FilterRow`) need it: their value column is one
4699
+ * shared flex track, and a control that sizes itself leaves every row's
4700
+ * remove button at a different x — which is exactly what made a panel with
4701
+ * more than two rows look shattered. The pinned toolbar
4702
+ * (`PinnedFilterControls`) must NOT have it: those controls sit inline
4703
+ * among other toolbar items with no column to fill, and stretching them
4704
+ * there would be the same bug with the roles reversed. So the width comes
4705
+ * from the CALLER — the only place that knows whether there is a column —
4706
+ * rather than from a blanket `w-full` on the controls themselves.
4565
4707
  */
4566
- tableRef?: (table: Table$1<RowRecord$1> | null) => void;
4708
+ readonly fill?: boolean;
4567
4709
  }
4568
- declare function ResourceList({ resource, data, total, page, pageSize, onPageSize, onRequestCount, isCountingRows, pageSizeOptions, onPage, loadMore, selectable, selectedIds, onSelectionChange, className, isLoading, error, onRetry, createHref, isFiltered, onClearFilter, emptyState, rowHref, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }: ResourceListProps): React.ReactElement;
4710
+ /**
4711
+ * The value half of a builder row.
4712
+ *
4713
+ * Arity comes from the OPERATOR, the input type from the FIELD — the two
4714
+ * are independent, which is why `in` on a string field is a chip list while
4715
+ * `contains` on the same field is a text box, and why `isNull` renders
4716
+ * nothing at all rather than a disabled input.
4717
+ *
4718
+ * Deliberately free of TanStack: these controls replace chips that read and
4719
+ * wrote through `column.getFilterValue()`, and that coupling is exactly what
4720
+ * kept them out of the relation picker (SP3).
4721
+ */
4722
+ declare function FilterValueControl(props: FilterValueControlProps): React.ReactElement | null;
4569
4723
  //#endregion
4570
4724
  //#region src/list-kit/list-slots.d.ts
4571
4725
  /**
4572
- * The three override seams `<ResourceListPage>` opens on the standard list
4573
- * (core flexibility program, Task B3.2; level 1 added by SP0 Task 5) — the
4574
- * "level 1", "level 2" and "level 3" rungs of the spec's override ladder:
4726
+ * The two override seams `<ResourceListPage>` opens on the standard list (core flexibility
4727
+ * program, Task B3.2; level 1 added by SP0 Task 5) — the "level 1" and "level 2" rungs of the
4728
+ * spec's override ladder:
4575
4729
  *
4576
4730
  * ```
4577
4731
  * level 0 nothing registered → the standard assembled list
4578
4732
  * level 1 list.cell.<r> → one column's renderer; everything else stays standard
4579
4733
  * level 2 list.filters.<r> → custom filter region; table/footer stay standard
4580
- * level 3 list.body.<r> → the whole body is yours
4581
4734
  * ```
4582
4735
  *
4736
+ * ⚠️ **The old level 3, `list.body.<r>`, is gone (P7-37).** A whole-body override is a list frame
4737
+ * (`views.list.frame`, `defineFrame(id, { for: 'list' })`): it places the framework's pieces
4738
+ * (`<ListControls/>`, `<ListBody/>`, …) instead of replacing them, so it keeps the filters, the
4739
+ * saved views, the columns menu, URL state, selection and the bulk bar. A component still
4740
+ * registered under `list.body.<r>` is a development error at mount and a `checkApp` error
4741
+ * (`list.body-removed`), never a silent no-op.
4742
+ *
4583
4743
  * ⚠️ **Level 1 is reachable through the registry now, not only through a
4584
4744
  * prop.** `<ResourceList renderCell>` / `<ResourceListPage renderCell>` still
4585
4745
  * work — an explicit prop wins over the registered slot — but neither is
4586
4746
  * reachable through the standard `NavAreaPage` router, which mounts
4587
4747
  * `<ResourceListPage resource={...} />` with no props at all. Before this,
4588
4748
  * an adopter who wanted one column rendered as a badge had no way to reach
4589
- * that router and had to register `list.body.<r>` instead, trading away
4749
+ * that router and had to replace the whole list body instead, trading away
4590
4750
  * filters, the columns menu, saved views and URL state to repaint a single
4591
- * cell. `list.cell.<r>` closes that gap the same way `list.filters.<r>` and
4592
- * `list.body.<r>` already do.
4751
+ * cell. `list.cell.<r>` closes that gap the same way `list.filters.<r>` does.
4593
4752
  *
4594
4753
  * ⚠️ **`list.cell.<r>` never sees a column `<ResourceListPage>`'s OWN
4595
4754
  * built-in branches already claimed.** A column with a declared `enum`
4596
- * gets the framework's `<EnumBadge>`, and a template-discriminator column
4755
+ * gets the framework's `<EnumBadge>`, and a typed resource's discriminator column
4597
4756
  * gets its label/badge, BEFORE either the `renderCell` prop or the
4598
4757
  * `list.cell.<r>` slot is consulted — same as the pre-Task-5 behaviour for
4599
4758
  * the prop. The registered renderer's function is simply never called for
@@ -4601,20 +4760,20 @@ declare function ResourceList({ resource, data, total, page, pageSize, onPageSiz
4601
4760
  * This is the genuinely surprising half of the contract, so it is
4602
4761
  * exercised explicitly in `tests/list-cell-slot.test.tsx`.
4603
4762
  *
4604
- * ⚠️ **Polling is no longer a reason to drop to level 3.** A list that needs
4763
+ * ⚠️ **Polling is no longer a reason to write a list frame.** A list that needs
4605
4764
  * fresh data declares `views.list.refetchInterval` (`@burojs/core`); the
4606
4765
  * controller resolves it into a `live` slice and `<ResourceListPage>` renders
4607
- * the pause toggle. Every adopter surface that reached for `list.body.<r>`
4608
- * *only* to get a `refetchInterval` gave up filters, the columns menu, saved
4609
- * views and URL state to buy one option it can now declare.
4766
+ * the pause toggle. Every adopter surface that replaced the body *only* to get a
4767
+ * `refetchInterval` gave up filters, the columns menu, saved views and URL state to buy one
4768
+ * option it can now declare.
4610
4769
  *
4611
4770
  * ⚠️ **Slots are registered through the Components registry, NOT on
4612
4771
  * `ResourceConfig`.** `packages/core` declares `dependencies: {}` and
4613
4772
  * imports React nowhere, and `ResourceViews` is non-generic — an inline
4614
4773
  * `ComponentType` on the config would both drag React into core and lose
4615
- * `TRecord` inference. The registry is the existing namespaced convention
4616
- * (`show.banner.<resource>`, `tab.<key>`, `page:<name>`, `layout:<key>`),
4617
- * and it needs zero core changes.
4774
+ * `TRecord` inference. The registry's keys are dotted with the canonical
4775
+ * resource last (`list.<region>.<resource>`, `record.<action>.<resource>`,
4776
+ * `layout.<key>`), and they need zero core changes.
4618
4777
  *
4619
4778
  * @see docs/specs/core-flexibility-overview.md §3
4620
4779
  * @see docs/superpowers/plans/2026-07-26-cfb3-assembled-page-slots.md — Task B3.2
@@ -4628,10 +4787,8 @@ declare function ResourceList({ resource, data, total, page, pageSize, onPageSiz
4628
4787
  * - `'filters'` — the filter region only (search box + dynamic filter chips
4629
4788
  * + "+ Add filter"). Saved views, the columns toggle, the create button,
4630
4789
  * the table, the footer and pagination all stay standard.
4631
- * - `'body'` — everything below the page header: the whole toolbar row, the
4632
- * saved-view dialogs, the table, the footer and the bulk bar.
4633
4790
  */
4634
- type ListSlotRegion = 'filters' | 'body' | 'cell';
4791
+ type ListSlotRegion = 'filters' | 'cell';
4635
4792
  /**
4636
4793
  * A list slot component. It takes **no props**: everything it needs — rows,
4637
4794
  * query state, filters, sorting, columns, selection, saved views, hrefs —
@@ -4666,12 +4823,12 @@ type ListCellRenderer = NonNullable<ResourceListProps['renderCell']>;
4666
4823
  * <BuroProvider components={{ [listSlot('filters', 'code_code')]: CodeFilterRegion }}>
4667
4824
  * ```
4668
4825
  *
4669
- * ⚠️ The signature takes `(region, resource)` rather than the single
4670
- * `listSlot(name)` the plan sketched: the key this has to produce is
4671
- * `list.<region>.<resource>` (the `show.banner.<resource>` precedent), and
4672
- * one argument cannot carry both halves. Typing `region` as a union rather
4673
- * than a free string is the point of the helper — a typo'd slot key is
4674
- * otherwise a silent no-op, which is precisely how registry slots fail.
4826
+ * The key is `list.<region>.<resource>` with the resource canonicalised, as
4827
+ * `canonicalResourceName` does: the bare id in the default namespace (`order` and
4828
+ * `default:order` name the same slot), `namespace:id` otherwise
4829
+ * (`list.cell.crm:deals`). Typing `region` as a union rather than a free
4830
+ * string is the point of the helper — a typo'd slot key is otherwise a
4831
+ * silent no-op, which is precisely how registry slots fail.
4675
4832
  */
4676
4833
  declare function listSlot(region: ListSlotRegion, resource: string): string;
4677
4834
  /**
@@ -4686,11 +4843,11 @@ declare function listSlot(region: ListSlotRegion, resource: string): string;
4686
4843
  * call directly. A single widened return type would let `useListSlot('cell',
4687
4844
  * r)`'s result typecheck as `<Slot />`, which compiles and then explodes at
4688
4845
  * render (a component invoked as a 3-arg function, or vice versa). The
4689
- * `'filters' | 'body'` overload's contract is byte-identical to before this
4846
+ * `'filters'` overload's contract is byte-identical to before this
4690
4847
  * region existed.
4691
4848
  */
4692
4849
  declare function useListSlot(region: 'cell', resource: string): ListCellRenderer | undefined;
4693
- declare function useListSlot(region: 'filters' | 'body', resource: string): ListSlot | undefined;
4850
+ declare function useListSlot(region: 'filters', resource: string): ListSlot | undefined;
4694
4851
  //#endregion
4695
4852
  //#region src/saved-views/saved-views-dropdown.d.ts
4696
4853
  /**
@@ -4781,378 +4938,67 @@ interface SavedViewsDropdownProps {
4781
4938
  * 'fork-entry' → an entry's inline Fork button (viewId passed)
4782
4939
  * 'issues-notice' → the adapter-issues banner (no viewId)
4783
4940
  */
4784
- getTestId?: (role: 'trigger' | 'menu' | 'view-item' | 'save-current' | 'manage' | 'separator' | 'section-header' | 'rename-entry' | 'delete-entry' | 'fork-entry' | 'issues-notice', viewId?: string) => string | undefined;
4785
- /** Trigger label override. Default: "Views". */
4786
- triggerLabel?: string;
4787
- /**
4788
- * Render the trigger as a compact icon-only square button (no visible label).
4789
- * `triggerLabel` becomes the `aria-label` + tooltip. The bookmark icon (the
4790
- * saved-views affordance) stays.
4791
- */
4792
- iconOnly?: boolean;
4793
- /** className applied to the trigger button. */
4794
- triggerClassName?: string;
4795
- /** className applied to the popover content. */
4796
- popoverContentClassName?: string;
4797
- /** Popover content alignment. Default: 'start'. */
4798
- popoverAlign?: 'start' | 'center' | 'end';
4799
- }
4800
- declare function SavedViewsDropdown({ adopterViews, entries, issues, onApply, onSaveCurrent, canSaveCurrent, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork, getTestId, triggerLabel, iconOnly, triggerClassName, popoverContentClassName, popoverAlign }: SavedViewsDropdownProps): React.ReactElement;
4801
- //#endregion
4802
- //#region src/resource/list-error-state.d.ts
4803
- interface ListErrorStateProps {
4804
- /** Whatever the query threw. */
4805
- error: {
4806
- message?: string;
4807
- } | Error;
4808
- /** When provided, a Retry button is rendered and calls this. */
4809
- onRetry?: () => void;
4810
- /** Resource name, used to build the retry button's test id. */
4811
- resource?: string;
4812
- /** Overrides the default «Не удалось загрузить данные» heading. */
4813
- title?: string;
4814
- className?: string;
4815
- }
4816
- /**
4817
- * The canonical "this list failed to load" panel: icon, heading, an
4818
- * actionable sentence, and a Retry button.
4819
- *
4820
- * ⚠️ Extracted from `<ResourceList>` because it was trapped inside it. The
4821
- * `schedules` list is rendered by a level-3 body slot, which is the whole
4822
- * point of slots — but the slot then had to hand-roll its own error branch,
4823
- * and hand-rolled it worse: a bare red card printing
4824
- *
4825
- * «Не удалось загрузить расписания. Temporal GET
4826
- * /namespaces/default/schedules?maximumPageSize=10 -> 502»
4827
- *
4828
- * — a raw internal HTTP trace under a Russian sentence, with NO way to retry.
4829
- * When Temporal was briefly down, the only recovery was reloading the page.
4830
- *
4831
- * A slot author should not have to re-derive an error state the framework
4832
- * already knows how to render, so this is now shared by both. `toFriendlyError`
4833
- * keeps the technical string out of the operator's face while leaving
4834
- * adopter-written messages verbatim; the raw text stays reachable under
4835
- * «Подробности» for whoever has to file the bug.
4836
- */
4837
- declare function ListErrorState({ error, onRetry, resource, title, className }: ListErrorStateProps): React.ReactElement;
4838
- //#endregion
4839
- //#region src/resource/resource-show.d.ts
4840
- interface ResourceShowProps {
4841
- resource: string;
4842
- record: {
4843
- id: string;
4844
- } & Record<string, unknown>;
4845
- className?: string;
4846
- /**
4847
- * Slot for adopter-supplied UI rendered to the RIGHT of the resolved
4848
- * `<ActionOverflow surface="show">` — NOT an action itself, just arbitrary
4849
- * chrome the host wants next to the toolbar (a status badge, a link out to
4850
- * another system, anything that isn't dispatched through the action
4851
- * registry). Every entity already carries the built-in Edit and Delete
4852
- * (plus any registry-declared actions whose surface includes `'show'`)
4853
- * through `<ActionOverflow>`, so this slot no longer needs to (and must
4854
- * not try to) supply page-local Edit/Delete of its own — there is nothing
4855
- * left for it to fill in there. It renders directly alongside the
4856
- * registry actions with no separator between them; the two are one visual
4857
- * group, not two.
4858
- */
4859
- extraActions?: ReactNode;
4860
- /**
4861
- * When true, ResourceShow renders WITHOUT its own `<PageHeader>` (no
4862
- * breadcrumb / title / toolbar actions) — only the banner + field card,
4863
- * still wrapped in the `page-show` testid root.
4864
- *
4865
- * Used by the tabbed detail shell (`ResourceShowTabs`), which hoists a
4866
- * SINGLE header above the tab strip so item actions stay visible on every
4867
- * tab. The non-tabbed page leaves this false and renders its own header.
4868
- */
4869
- hideHeader?: boolean;
4870
- /**
4871
- * Optional breadcrumb override rendered in the page header IN PLACE OF the
4872
- * default `<Breadcrumbs resource pageLabel>`. Pass a custom `<Breadcrumbs>`
4873
- * (e.g. with `leadingSegments` + a different root `resource`) to root the
4874
- * detail page at a different list — the sub-project-5 "sticky nav" uses this
4875
- * so a document opened from a per-type list breadcrumbs back to that list +
4876
- * section instead of the generic Documents list. Undefined → default.
4877
- */
4878
- breadcrumbs?: ReactNode;
4879
- /**
4880
- * Optional per-context field map MERGED over the resource-level `def.fields`
4881
- * — same contract as `ResourceForm`'s prop of the same name (override keys
4882
- * win on collision, resource-only keys still render). Omitted → the details
4883
- * panel renders `def.fields`, or the matched document type's field set when
4884
- * this resource declares `templates`.
4885
- *
4886
- * ⚠️ Not the document-type mechanism: a templated resource resolves its own
4887
- * projection from `record[templates.discriminatorField]`, replacing rather
4888
- * than merging. Passing this prop turns that resolution OFF for this render.
4889
- */
4890
- fieldsOverride?: ResourceFieldMap;
4891
- /**
4892
- * Optional layout override — same contract as `ResourceForm`'s prop of the
4893
- * same name: when set, it REPLACES `views.show.fields ?? views.fields`
4894
- * outright for this render.
4895
- *
4896
- * Precedence: this prop → the matched template's `views.fields` → the
4897
- * resource's own.
4898
- */
4899
- fieldsLayoutOverride?: FieldLayoutSpec;
4900
- }
4901
- /**
4902
- * Resolve a record's display name using the resource's `displayField`,
4903
- * falling back to `id`.
4904
- */
4905
- declare function recordDisplayName(record: Record<string, unknown>, displayField?: string): string;
4906
- /**
4907
- * Per-resource banner slot. Rendered above the field card when the adopter
4908
- * registers a component under `show.banner.<resource>` in the global components
4909
- * map (see `mergeComponents` + `<BuroProvider components={...}>`).
4910
- *
4911
- * Resources that do NOT register an override get the default render — the
4912
- * 22 prior e2e specs verify this backward-compat path.
4913
- */
4914
- interface ResourceShowBannerProps {
4915
- resource: string;
4916
- record: {
4917
- id: string;
4918
- } & Record<string, unknown>;
4919
- }
4920
- /**
4921
- * The show-page `<PageHeader>` (breadcrumb + title + the resolved
4922
- * `<ActionOverflow surface="show">` item-action list, then any adopter
4923
- * `extraActions`).
4924
- *
4925
- * Extracted so BOTH the non-tabbed `<ResourceShow>` (which renders it inline)
4926
- * and the tabbed shell (`ResourceShowTabs`, which hoists it above the tab
4927
- * strip so it stays visible on every tab) render the identical header from one
4928
- * source of truth.
4929
- */
4930
- declare function ShowPageHeader({ resource, record, extraActions, breadcrumbs }: {
4931
- resource: string;
4932
- record: {
4933
- id: string;
4934
- } & Record<string, unknown>;
4935
- extraActions?: ReactNode;
4936
- breadcrumbs?: ReactNode;
4937
- }): React.ReactElement | null;
4938
- declare function ResourceShow({ resource, record, className, extraActions, hideHeader, fieldsOverride, fieldsLayoutOverride, breadcrumbs }: ResourceShowProps): React.ReactElement | null;
4939
- //#endregion
4940
- //#region src/resource/detail-tabs-shell.d.ts
4941
- interface DetailTabsShellProps {
4942
- resource: string;
4943
- record: {
4944
- id: string;
4945
- } & Record<string, unknown>;
4946
- /** Which detail surface this shell serves — drives `resolveTabs(view)`. */
4947
- view: TabView;
4948
- /**
4949
- * The currently active tab key. Resolved against the VISIBLE tab set (see
4950
- * `resolveTabs`): an unknown / hidden value falls back to the `details` tab
4951
- * when present, else the first visible tab. Pass `undefined` to default to
4952
- * `details`.
4953
- */
4954
- activeTab?: string;
4955
- /**
4956
- * Called when the user clicks a tab trigger. Receives the new tab key
4957
- * (`details` for the field/form panel, otherwise the tab's `key`).
4958
- * Consumers typically wire this to a URL search-param so deep-links /
4959
- * back-forward navigation work. When omitted, the shell falls back to
4960
- * internal state (uncontrolled).
4961
- */
4962
- onTabChange?: (tab: string) => void;
4963
- /**
4964
- * Optional renderer for relation-tab list rows. Forwarded to the embedded
4965
- * `<ResourceList>`'s `rowHref`. Default behaviour produces
4966
- * `/{childResource}/{row.id}` so a row click navigates to the standalone
4967
- * show page.
4968
- */
4969
- subresourceRowHref?: (childResource: string, row: {
4970
- id: string;
4971
- }) => string;
4972
- /**
4973
- * The SINGLE page header, rendered ABOVE the tab strip and therefore always
4974
- * visible regardless of the active tab. Item toolbar actions live here so
4975
- * they never vanish when a non-details tab is active. Only mounted on the
4976
- * tabbed path (the fast path uses `renderStandalone`'s own header).
4977
- */
4978
- header: ReactNode;
4979
- /**
4980
- * Renders the `details` tab body WITHOUT its own header (chromeless): the
4981
- * Show field card for `view: 'show'`, or the Edit form for `view: 'edit'`.
4982
- */
4983
- renderDetails: () => ReactNode;
4984
- /**
4985
- * Renders the standalone page (WITH its own header) used on the fast path —
4986
- * when the resource resolves to only the synthesized `details` tab. Keeps
4987
- * the no-tabs page byte-identical to the pre-tabs component.
4988
- */
4989
- renderStandalone: () => ReactNode;
4990
- }
4991
- /**
4992
- * Shared detail-page tab shell for Show AND Edit.
4993
- *
4994
- * Fast path: when `resolveTabs` yields only the synthesized `details` tab (the
4995
- * resource declares no tabs for this view and has no subresources), the shell
4996
- * is a transparent passthrough to `renderStandalone()` — no Tabs primitive in
4997
- * the tree, no visual change, and the standalone component renders its own
4998
- * header exactly as before.
4999
- *
5000
- * Tabbed path: a single hoisted `header` renders above the `TabsList` (always
5001
- * visible), the `details` tab hosts the chromeless content from
5002
- * `renderDetails()`, and component / relation tabs render lazily (Radix
5003
- * unmounts inactive `Tabs.Content`, so an inactive tab's `useList` / fetch
5004
- * never fires until it is activated).
5005
- */
5006
- declare function DetailTabsShell(props: DetailTabsShellProps): React.ReactElement | null;
5007
- interface SubresourceTabBodyProps {
5008
- childResource: string;
5009
- foreignKey: string;
5010
- parentId: string;
5011
- rowHref: (row: {
5012
- id: string;
5013
- } & Record<string, unknown>) => string;
5014
- /**
5015
- * Rows per page. Default `10` — the value this component hardcoded before
5016
- * the knob existed, so omitting it changes nothing.
5017
- *
5018
- * Excluded from the count key on purpose: see the `countKey` note below.
5019
- */
5020
- pageSize?: number;
5021
- /**
5022
- * Extra filters **ANDed with** the parent FK filter.
5023
- *
5024
- * ⚠️ They never REPLACE it. The FK is what makes this list a sub-resource of
5025
- * the open record; a filter array that supplanted it would silently turn a
5026
- * tab that reads as "this document's codes" into a listing of every row in
5027
- * the child table.
5028
- */
5029
- filters?: readonly FilterExpression<ErasedResourceHandle>[];
5030
- /**
5031
- * Field a free-text search box targets. **Absent → no box is rendered**, the
5032
- * same rule `views.list.search` follows on the standalone list page. A bare
5033
- * field name, NOT a `SearchDecl` — this is TAB level, one field, no
5034
- * resolver escape hatch. The term is composed via `searchToFilter` as
5035
- * `{ field: search, op: 'contains' }` and ANDed onto the filters above.
5036
- */
5037
- search?: string;
5038
- }
5039
- /**
5040
- * Mounts a paginated `<ResourceList>` of `childResource` filtered to
5041
- * `foreignKey === parentId`. Same internals as the standalone list page; the
5042
- * FK filter is non-clearable from inside the tab (the user is in the parent's
5043
- * context — clearing it would surface unrelated rows).
5044
- *
5045
- * Three optional knobs tune the surface — `pageSize`, `filters`, `search`
5046
- * (Task B6.1, closing this component's own `TODO(v2)`). Every one of them is
5047
- * absent by default, and absent means the pre-B6 behaviour to the byte: ten
5048
- * rows a page, no search box, the FK as the only filter. Adopters normally
5049
- * declare them on the tab (`RelationTab`) rather than here; they exist as
5050
- * props because this component is exported for bespoke tab layouts.
5051
- *
5052
- * `sort` is deliberately NOT among them. The embedded list renders a fixed
5053
- * column set with no sort affordance, so a declaration site with no reader
5054
- * would be exactly the dead `views.list.filters` key the program is deleting.
5055
- *
5056
- * Exported so adopters composing bespoke tab layouts can reuse the embedded
5057
- * filtered list without re-implementing the `useList` + FK-filter wiring.
5058
- */
5059
- declare function SubresourceTabBody({ childResource, foreignKey, parentId, rowHref, pageSize, filters: extraFilters, search: searchField }: SubresourceTabBodyProps): ReactNode;
5060
- //#endregion
5061
- //#region src/resource/resource-show-tabs.d.ts
5062
- interface ResourceShowTabsProps extends ResourceShowProps {
5063
- /**
5064
- * The currently active tab key. Resolved against the VISIBLE tab set (see
5065
- * `resolveTabs`): an unknown / hidden value falls back to the `details` tab
5066
- * when present, else the first visible tab. Pass `undefined` to default to
5067
- * `details`.
5068
- */
5069
- activeTab?: string;
5070
- /**
5071
- * Called when the user clicks a tab trigger. Receives the new tab key
5072
- * (`details` for the field panel, otherwise the tab's `key` — a subresource
5073
- * name for auto-relation tabs). Consumers typically wire this to a URL
5074
- * search-param via the router so deep-links / back-forward navigation work.
5075
- * When omitted, the component falls back to internal state (uncontrolled).
5076
- */
5077
- onTabChange?: (tab: string) => void;
4941
+ getTestId?: (role: 'trigger' | 'menu' | 'view-item' | 'save-current' | 'manage' | 'separator' | 'section-header' | 'rename-entry' | 'delete-entry' | 'fork-entry' | 'issues-notice', viewId?: string) => string | undefined;
4942
+ /** Trigger label override. Default: "Views". */
4943
+ triggerLabel?: string;
5078
4944
  /**
5079
- * Optional renderer for relation-tab list rows. Forwarded to the embedded
5080
- * `<ResourceList>`'s `rowHref`. Default behaviour produces
5081
- * `/{childResource}/{row.id}` so a row click navigates to the standalone
5082
- * show page (the "shows in tab AND has standalone pages" contract).
4945
+ * Render the trigger as a compact icon-only square button (no visible label).
4946
+ * `triggerLabel` becomes the `aria-label` + tooltip. The bookmark icon (the
4947
+ * saved-views affordance) stays.
5083
4948
  */
5084
- subresourceRowHref?: (childResource: string, row: {
5085
- id: string;
5086
- }) => string;
4949
+ iconOnly?: boolean;
4950
+ /** className applied to the trigger button. */
4951
+ triggerClassName?: string;
4952
+ /** className applied to the popover content. */
4953
+ popoverContentClassName?: string;
4954
+ /** Popover content alignment. Default: 'start'. */
4955
+ popoverAlign?: 'start' | 'center' | 'end';
5087
4956
  }
5088
- /**
5089
- * Renders `<ResourceShow>` with a declarative tab strip resolved by the core
5090
- * `resolveTabs` helper — Details (the field panel) + any `component` /
5091
- * `relation` tabs declared on the resource / template, plus auto-generated
5092
- * relation tabs for every registered subresource.
5093
- *
5094
- * The page header (breadcrumb + the resolved `<ActionOverflow surface="show">`
5095
- * item-action list, then adopter `extraActions`) is hoisted ABOVE the tab strip by the
5096
- * shared `DetailTabsShell`, so item actions stay visible on every tab. The
5097
- * `details` tab therefore renders `<ResourceShow>` chromeless (`hideHeader`).
5098
- *
5099
- * Fast path: when the resource declares no `tabs`, has no subresources, and
5100
- * `resolveTabs` yields only the synthesized `details` tab, this is a
5101
- * transparent passthrough to a plain `<ResourceShow>` (own header, no Tabs
5102
- * primitive in the tree, no visual change).
5103
- */
5104
- declare function ResourceShowTabs(props: ResourceShowTabsProps): React.ReactElement | null;
4957
+ declare function SavedViewsDropdown({ adopterViews, entries, issues, onApply, onSaveCurrent, canSaveCurrent, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork, getTestId, triggerLabel, iconOnly, triggerClassName, popoverContentClassName, popoverAlign }: SavedViewsDropdownProps): React.ReactElement;
5105
4958
  //#endregion
5106
- //#region src/resource/tab-body.d.ts
5107
- /**
5108
- * Props injected into every `component` tab body. The framework passes the
5109
- * host `resource` name and the loaded `record` so the adopter's component can
5110
- * fetch / render record-scoped content without re-reading the route.
5111
- */
5112
- interface TabComponentProps {
5113
- resource: string;
5114
- record: {
5115
- id: string;
5116
- } & Record<string, unknown>;
5117
- }
5118
- interface TabBodyProps {
5119
- /**
5120
- * Resolved component for this tab's slot — looked up via
5121
- * `useComponent('tab.<key>')` by the caller. `undefined` when the slot is
5122
- * unregistered; `<TabBody>` then renders a graceful empty state instead of
5123
- * crashing the page.
5124
- */
5125
- component?: ComponentType<TabComponentProps>;
5126
- resource: string;
5127
- record: {
5128
- id: string;
5129
- } & Record<string, unknown>;
5130
- }
5131
- interface TabBodyState {
5132
- error: Error | null;
4959
+ //#region src/resource/list-error-state.d.ts
4960
+ interface ListErrorStateProps {
4961
+ /** Whatever the query threw. */
4962
+ error: {
4963
+ message?: string;
4964
+ } | Error;
4965
+ /** When provided, a Retry button is rendered and calls this. */
4966
+ onRetry?: () => void;
4967
+ /** Resource name, used to build the retry button's test id. */
4968
+ resource?: string;
4969
+ /** Overrides the default «Не удалось загрузить данные» heading. */
4970
+ title?: string;
4971
+ className?: string;
5133
4972
  }
5134
4973
  /**
5135
- * Framework wrapper for a declarative `component` tab body.
4974
+ * The canonical "this list failed to load" panel: icon, heading, an
4975
+ * actionable sentence, and a Retry button.
5136
4976
  *
5137
- * Three responsibilities, all containment:
4977
+ * ⚠️ Extracted from `<ResourceList>` because it was trapped inside it. The
4978
+ * `schedules` list is rendered by a level-3 body slot, which is the whole
4979
+ * point of slots — but the slot then had to hand-roll its own error branch,
4980
+ * and hand-rolled it worse: a bare red card printing
5138
4981
  *
5139
- * - **ErrorBoundary** (this class): a throwing tab body must NOT take down the
5140
- * whole show page. There is no other error boundary in the resource render
5141
- * tree, so this is the containment point — a crash renders a small inline
5142
- * error state and the rest of the page (header, other tabs) stays alive.
5143
- * - **Suspense**: the child may lazy-import or suspend on data; a skeleton
5144
- * shows until it resolves. Radix `Tabs.Content` unmounts inactive tabs, so a
5145
- * lazy body's `useList` / fetch never fires until its tab is activated.
5146
- * - **Empty state**: when the slot is unregistered (`component` is undefined)
5147
- * the wrapper renders a muted placeholder rather than throwing.
4982
+ * «Не удалось загрузить расписания. Temporal GET
4983
+ * /namespaces/default/schedules?maximumPageSize=10 -> 502»
4984
+ *
4985
+ * — a raw internal HTTP trace under a Russian sentence, with NO way to retry.
4986
+ * When Temporal was briefly down, the only recovery was reloading the page.
5148
4987
  *
5149
- * Deliberately minimal — no retry / reset UI in v1.
4988
+ * A slot author should not have to re-derive an error state the framework
4989
+ * already knows how to render, so this is now shared by both. `toFriendlyError`
4990
+ * keeps the technical string out of the operator's face while leaving
4991
+ * adopter-written messages verbatim; the raw text stays reachable under
4992
+ * «Подробности» for whoever has to file the bug.
5150
4993
  */
5151
- declare class TabBody extends Component$1<TabBodyProps, TabBodyState> {
5152
- state: TabBodyState;
5153
- static getDerivedStateFromError(error: unknown): TabBodyState;
5154
- render(): ReactNode;
5155
- }
4994
+ declare function ListErrorState({ error, onRetry, resource, title, className }: ListErrorStateProps): React.ReactElement;
4995
+ //#endregion
4996
+ //#region src/resource/record-display-name.d.ts
4997
+ /**
4998
+ * A record's display name: its `displayField` value when present and non-empty, else its `id`.
4999
+ * Exported for consumers building delete dialogs, breadcrumbs and relation labels.
5000
+ */
5001
+ declare function recordDisplayName(record: Record<string, unknown>, displayField?: string): string;
5156
5002
  //#endregion
5157
5003
  //#region src/resource/page-header.d.ts
5158
5004
  interface PageHeaderProps {
@@ -5160,10 +5006,10 @@ interface PageHeaderProps {
5160
5006
  resource: string;
5161
5007
  /**
5162
5008
  * Page mode. Drives default title:
5163
- * - 'list' → label.plural
5164
- * - 'show' → label.single + ': ' + recordName
5165
- * - 'edit' → 'Edit ' + label.single + ': ' + recordName
5166
- * - 'create' → 'Create ' + label.single
5009
+ * - 'list' → labelPlural
5010
+ * - 'show' → label + ': ' + recordName
5011
+ * - 'edit' → 'Edit ' + label + ': ' + recordName
5012
+ * - 'create' → 'Create ' + label
5167
5013
  */
5168
5014
  mode: 'list' | 'show' | 'edit' | 'create';
5169
5015
  /** Display name of the current record (for show/edit). */
@@ -5173,7 +5019,7 @@ interface PageHeaderProps {
5173
5019
  * (or `/{resource}` fallback) for show/edit/create.
5174
5020
  */
5175
5021
  backHref?: string;
5176
- /** Override the back-link label. Defaults to label.plural. */
5022
+ /** Override the back-link label. Defaults to labelPlural. */
5177
5023
  backLabel?: string;
5178
5024
  /** Slot for action buttons (Edit/Delete/+Create) on the right. */
5179
5025
  actions?: ReactNode;
@@ -5184,7 +5030,7 @@ interface PageHeaderProps {
5184
5030
  * the back-link is suppressed in favour of the breadcrumb (the two
5185
5031
  * are functionally redundant — a breadcrumb's leading link points
5186
5032
  * at the same URL as the back-link, with more spatial context).
5187
- * Pass `<Breadcrumbs pageLabel={recordName} />` from
5033
+ * Pass `<Breadcrumbs resource={resource} pageLabel={recordName} />` from
5188
5034
  * `@burojs/ui-shadcn` for the canonical resource-rooted layout.
5189
5035
  */
5190
5036
  breadcrumbs?: ReactNode;
@@ -5223,239 +5069,43 @@ interface PageHeaderProps {
5223
5069
  className?: string;
5224
5070
  }
5225
5071
  declare function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink, visibleTitle, labelOverride, className }: PageHeaderProps): React.ReactElement | null;
5226
- //#endregion
5227
- //#region src/resource/resource-edit.d.ts
5228
- interface ResourceEditProps {
5072
+ /**
5073
+ * The show-page `<PageHeader>`: the breadcrumb, the record's title and the resolved
5074
+ * `<ActionOverflow surface="show">` item-action list, then any `extraActions`.
5075
+ */
5076
+ declare function ShowPageHeader({ resource, record, extraActions, breadcrumbs }: {
5229
5077
  resource: string;
5230
5078
  record: {
5231
5079
  id: string;
5232
5080
  } & Record<string, unknown>;
5233
- onSave: (values: Record<string, unknown>) => Promise<void>;
5234
- onCancel?: () => void;
5235
- className?: string;
5236
- /** Override the back-link href (defaults to the show page for this record). */
5237
- backHref?: string;
5238
- /**
5239
- * Optional per-template field map. Forwarded to ResourceForm; keys override
5240
- * the resource-level `def.fields` on collision. Used by the document-
5241
- * templates v1 wiring on the playground edit page.
5242
- */
5243
- fieldsOverride?: ResourceFieldMap;
5244
- /**
5245
- * Optional per-template layout override forwarded to ResourceForm. See
5246
- * `ResourceFormProps.fieldsLayoutOverride` — pass the matched template's
5247
- * `views?.fields` alongside `fieldsOverride`.
5248
- */
5249
- fieldsLayoutOverride?: FieldLayoutSpec;
5250
- /**
5251
- * Optional derived-value map forwarded to ResourceForm. See
5252
- * `ResourceFormProps.derive`. On edit, a target that already holds a saved
5253
- * value is never auto-overwritten.
5254
- */
5255
- derive?: Record<string, (values: Record<string, unknown>) => unknown>;
5256
- /**
5257
- * When true, ResourceEdit renders WITHOUT its own `<PageHeader>` — only the
5258
- * form, still wrapped in the `page-edit` testid root.
5259
- *
5260
- * Used by the tabbed detail shell (`ResourceEditTabs`), which hoists a
5261
- * SINGLE header above the tab strip so edit-surface item actions stay
5262
- * visible on every tab. The non-tabbed page leaves this false.
5263
- */
5264
- hideHeader?: boolean;
5265
- }
5081
+ extraActions?: ReactNode;
5082
+ breadcrumbs?: ReactNode;
5083
+ }): React.ReactElement | null;
5266
5084
  /**
5267
- * The edit-page `<PageHeader>` (back-link + title + the resolved
5268
- * `<ActionOverflow surface="edit">` item-action list).
5269
- *
5270
- * Extracted so BOTH the non-tabbed `<ResourceEdit>` (which renders it inline)
5271
- * and the tabbed shell (`ResourceEditTabs`, which hoists it above the tab
5272
- * strip) render the identical header from one source of truth.
5085
+ * The edit-page `<PageHeader>`: the back-link, the record's title and the resolved
5086
+ * `<ActionOverflow surface="edit">` item-action list.
5273
5087
  */
5274
- declare function EditPageHeader({ resource, record, backHref }: {
5088
+ declare function EditPageHeader({ resource, record, backHref, breadcrumbs }: {
5275
5089
  resource: string;
5276
5090
  record: {
5277
5091
  id: string;
5278
5092
  } & Record<string, unknown>;
5279
5093
  backHref?: string;
5094
+ breadcrumbs?: ReactNode;
5280
5095
  }): React.ReactElement;
5281
- declare function ResourceEdit({ resource, record, onSave, onCancel, className, backHref, fieldsOverride, fieldsLayoutOverride, derive, hideHeader }: ResourceEditProps): React.ReactElement;
5282
- //#endregion
5283
- //#region src/resource/resource-edit-tabs.d.ts
5284
- interface ResourceEditTabsProps extends ResourceEditProps {
5285
- /**
5286
- * The currently active tab key. Resolved against the VISIBLE tab set (see
5287
- * `resolveTabs`): an unknown / hidden value falls back to the `details` tab
5288
- * (the form) when present, else the first visible tab. Pass `undefined` to
5289
- * default to `details`.
5290
- */
5291
- activeTab?: string;
5292
- /**
5293
- * Called when the user clicks a tab trigger. Receives the new tab key
5294
- * (`details` for the form panel, otherwise the tab's `key`). Consumers
5295
- * typically wire this to a URL search-param so deep-links / back-forward
5296
- * navigation work. When omitted, falls back to internal state.
5297
- */
5298
- onTabChange?: (tab: string) => void;
5299
- /**
5300
- * Optional renderer for relation-tab list rows. Forwarded to the embedded
5301
- * `<ResourceList>`'s `rowHref`. Default behaviour produces
5302
- * `/{childResource}/{row.id}`.
5303
- */
5304
- subresourceRowHref?: (childResource: string, row: {
5305
- id: string;
5306
- }) => string;
5307
- }
5308
- /**
5309
- * Renders `<ResourceEdit>` with a declarative tab strip resolved by the core
5310
- * `resolveTabs` helper for the `'edit'` surface — the `details` tab hosts the
5311
- * FORM, alongside any `component` / `relation` tabs declared on the resource /
5312
- * template (and auto-generated relation tabs for every registered
5313
- * subresource).
5314
- *
5315
- * The page header (back-link + the resolved `<ActionOverflow surface="edit">`
5316
- * item-action list) is hoisted ABOVE the tab strip by the shared `DetailTabsShell`, so
5317
- * edit-surface item actions stay visible on every tab. The `details` tab
5318
- * therefore renders `<ResourceEdit>` chromeless (`hideHeader`).
5319
- *
5320
- * Fast path: when the resource declares no edit tabs and has no subresources,
5321
- * this is a transparent passthrough to a plain `<ResourceEdit>` (own header,
5322
- * no Tabs primitive in the tree).
5323
- */
5324
- declare function ResourceEditTabs(props: ResourceEditTabsProps): React.ReactElement | null;
5325
- //#endregion
5326
- //#region src/resource/resource-create.d.ts
5327
- interface ResourceCreateProps {
5328
- resource: string;
5329
- defaults?: Record<string, unknown>;
5330
- onCreate: (values: Record<string, unknown>) => Promise<void>;
5331
- onCancel?: () => void;
5332
- className?: string;
5333
- /** Override the back-link href (defaults to the resource list page). */
5334
- backHref?: string;
5335
- /**
5336
- * Optional per-template field map. Forwarded to ResourceForm; keys override
5337
- * the resource-level `def.fields` on collision. Used by the document-
5338
- * templates v1 wiring on the playground create page.
5339
- */
5340
- fieldsOverride?: ResourceFieldMap;
5341
- /**
5342
- * Optional per-template layout override forwarded to ResourceForm. See
5343
- * `ResourceFormProps.fieldsLayoutOverride` — pass the matched template's
5344
- * `views?.fields` alongside `fieldsOverride`.
5345
- */
5346
- fieldsLayoutOverride?: FieldLayoutSpec;
5347
- /**
5348
- * Optional derived-value map forwarded to ResourceForm. See
5349
- * `ResourceFormProps.derive` — powers e.g. the preprint auto-name.
5350
- */
5351
- derive?: Record<string, (values: Record<string, unknown>) => unknown>;
5352
- /**
5353
- * Names what is being created, when the page knows something more specific
5354
- * than the resource label. A document create launched as `?type=order_code`
5355
- * should read «Создать: Заказ кодов», not the generic «Создать: Документ» —
5356
- * the user picked a type one screen ago and the title should confirm it.
5357
- * Omitted → the resource label is used, exactly as before.
5358
- */
5359
- typeLabel?: string;
5360
- }
5361
- declare function ResourceCreate({ resource, defaults, onCreate, onCancel, className, backHref, fieldsOverride, fieldsLayoutOverride, derive, typeLabel }: ResourceCreateProps): React.ReactElement;
5362
- //#endregion
5363
- //#region src/resource/document-type-picker.d.ts
5364
- /** Text a host may substitute for a type's frozen declaration literals. */
5365
- interface DocumentTypeText {
5366
- label?: string;
5367
- description?: string;
5368
- }
5369
- interface DocumentTypePickerProps {
5370
- /** The templated resource whose types are on offer. */
5371
- resource: string;
5372
- /**
5373
- * Called with the chosen type. Each card is a `<button>` when this is the
5374
- * only handler given.
5375
- */
5376
- onSelect?: (template: DocumentTemplate) => void;
5377
- /**
5378
- * Per-type href. Given this, each card is an anchor rendered through the
5379
- * installed `LinkComponent`, so it keeps SPA routing, middle-click and
5380
- * "open in new tab" — which a button cannot. Both may be passed: the
5381
- * anchor navigates and `onSelect` still fires.
5382
- */
5383
- hrefFor?: (template: DocumentTemplate) => string;
5384
- /**
5385
- * Substitute the declared `label` / `description` for one type — the seam
5386
- * for translated text. A resource declaration's literals are frozen at boot,
5387
- * so this is where a host hands back what it resolved from its i18n bundle.
5388
- * Anything omitted falls back to the declaration.
5389
- */
5390
- localize?: (template: DocumentTemplate) => DocumentTypeText;
5391
- /** Rendered when no type is on offer. Defaults to nothing at all. */
5392
- emptyState?: React$1.ReactNode;
5393
- className?: string;
5394
- }
5395
- /**
5396
- * The create-page type picker: one card per document type a user may be
5397
- * offered right now.
5398
- *
5399
- * **This is where four of `DocumentTemplate`'s keys are read.** `visible`
5400
- * decides which types appear (through `useVisibleTemplates`, so the set
5401
- * narrows the moment scope moves — a type declared
5402
- * `visible: (ctx) => ctx.scope?.market !== 'EU'` leaves the offer when the
5403
- * market becomes EU, with no code here that knows what a market is). `icon`
5404
- * and `color` render the chip. `description` renders under the title. Until
5405
- * SP6 task 3 the framework shipped a `DocumentTemplate` type describing this
5406
- * component's card and no component: every adopter declaring types had to
5407
- * write the picker themselves, and the three presentational keys did nothing
5408
- * whatsoever in anyone's app.
5409
- *
5410
- * **It offers, it does not navigate and it does not authorize.** The host
5411
- * decides what choosing a type means — usually pushing `?type=<type>` at its
5412
- * own create route — because the URL contract belongs to the application's
5413
- * router, not to a library component. And `visible` gates the OFFER only: a
5414
- * hand-typed URL naming a hidden type still renders that type's form, exactly
5415
- * as `ResourceConfig.visible` gates navigation rather than routes. Where the
5416
- * boundary has to hold, declare a capability rule.
5417
- */
5418
- declare function DocumentTypePicker({ resource, onSelect, hrefFor, localize, emptyState, className }: DocumentTypePickerProps): React$1.ReactElement | null;
5419
- //#endregion
5420
- //#region src/resource/unsupported-document-type.d.ts
5421
- interface UnsupportedDocumentTypeProps {
5422
- /** The templated resource whose declaration refused this value. */
5423
- resource: string;
5424
- /** The unrecognised discriminator value, or `null` when the record had none. */
5425
- typeValue: string | null;
5426
- className?: string;
5427
- }
5428
- /**
5429
- * What `ResourceConfig.templates.fallback: 'unsupported'` renders.
5430
- *
5431
- * **This panel is a refusal, not an error.** The resource has declared that a
5432
- * record whose document type it does not know must be named rather than
5433
- * guessed at: rendering the host resource's whole field map instead would show
5434
- * a person the union of every type's columns and let them believe that is what
5435
- * the record is. A resource that would rather show that union simply omits
5436
- * `fallback`, which is the default and changes nothing.
5437
- *
5438
- * The strings are the framework's own `create.unsupportedType` /
5439
- * `create.unsupportedTypeHint`, in both shipped locales. They were added for
5440
- * the one application that hand-wrote this panel, and until now that
5441
- * application was their only reader — the same "the wiring works and its only
5442
- * caller leaves" shape this component exists to close. The `create.` namespace
5443
- * is now a misnomer (the show surface renders them too) and is kept anyway:
5444
- * renaming would silently drop any adopter bundle that already shadows those
5445
- * keys.
5446
- *
5447
- * `typeValue === null` — the record carries no discriminator at all — still
5448
- * lands here, with the hint naming an empty type. See
5449
- * `resolveTemplateProjection` for why the two are one outcome.
5450
- */
5451
- declare function UnsupportedDocumentType({ resource, typeValue, className }: UnsupportedDocumentTypeProps): React$1.ReactElement;
5452
5096
  //#endregion
5453
5097
  //#region src/resource/create-button.d.ts
5454
5098
  interface CreateButtonProps {
5455
5099
  /** Resource this creates. Required — the button gates itself. */
5456
5100
  resource: string;
5457
- /** Target create route, e.g. `resourceCreateHref(def, resource)`. */
5101
+ /** Target create route, e.g. `resourceCreateHref(def, resource)`. Ignored with `onOpen`. */
5458
5102
  href: string;
5103
+ /**
5104
+ * Opens the create somewhere other than a route — a resource without a create page opens its
5105
+ * `quick.create` (P3-19). The control is then a button, drawn when the entry gate answers
5106
+ * `open` (inert with its reason when `disabled`, absent otherwise, and while access loads).
5107
+ */
5108
+ onOpen?: () => void;
5459
5109
  /** `data-testid` for the button. */
5460
5110
  testid?: string;
5461
5111
  size?: ButtonProps['size'];
@@ -5476,119 +5126,14 @@ interface CreateButtonProps {
5476
5126
  * nothing for `absent`/`hidden`, a focusable `aria-disabled` control with a
5477
5127
  * toast reason for `disabled`, and the normal link for `available`.
5478
5128
  */
5479
- declare function CreateButton({ resource, href, testid, size, variant, label, className }: CreateButtonProps): React.ReactElement | null;
5480
- //#endregion
5481
- //#region src/resource/resource-form.d.ts
5482
- interface ResourceFormProps {
5483
- resource: string;
5484
- view: 'edit' | 'create';
5485
- recordId?: string;
5486
- initialValues: Record<string, unknown>;
5487
- onSubmit: (values: Record<string, unknown>) => Promise<void>;
5488
- onCancel?: () => void;
5489
- className?: string;
5490
- submitLabel?: string;
5491
- /**
5492
- * Optional per-context field map MERGED over the resource-level
5493
- * `def.fields`. Keys present here win over keys of the same name on the
5494
- * resource; keys only on the resource still render. It means "specialize or
5495
- * add these keys for this render".
5496
- *
5497
- * ⚠️ **This is not how a document type projects its field set.** A templated
5498
- * resource resolves that itself — the form reads
5499
- * `initialValues[templates.discriminatorField]`, matches the template and
5500
- * renders the template's OWN field map (a replacement, not a merge). Passing
5501
- * `fieldsOverride` turns that resolution OFF for this render, because an
5502
- * explicit map outranks an inferred one. Adopters wiring a template by hand
5503
- * (as the since-deleted `apps/playground` did before the resolution was
5504
- * promoted) therefore
5505
- * keep exactly the behaviour they had.
5506
- */
5507
- fieldsOverride?: ResourceFieldMap;
5508
- /**
5509
- * Optional layout override for the shared `views.fields` declaration — the
5510
- * same idea as `fieldsOverride`, one tier up: when set, it REPLACES
5511
- * `views.form.fields ?? views.fields` outright for this render.
5512
- *
5513
- * Precedence: this prop → the matched template's `views.fields` → the
5514
- * resource's own. A template's declared order wins over the resource's
5515
- * because merging two orders has no meaning anyone could predict.
5516
- *
5517
- * Ignored when `fieldsFilter` is set — a wizard step's hand-picked order
5518
- * already wins outright over any shared layout declaration.
5519
- */
5520
- fieldsLayoutOverride?: FieldLayoutSpec;
5521
- /**
5522
- * Optional whitelist of field names to render. When set, only the listed
5523
- * fields are visible inside the form (in array order); every other field
5524
- * declared on the resource is hidden. Used by `<ResourceWizard>` to
5525
- * project a slice of the resource's field set into a single step.
5526
- *
5527
- * Differs from `fieldsOverride` (which adds / replaces fields). Permission
5528
- * (`visible`) gates and the create-view "system field" rule still apply
5529
- * on top of this filter — a field listed here that fails `visible(ctx)`
5530
- * still does not render.
5531
- *
5532
- * Empty array (`[]`) renders zero inputs (the canonical
5533
- * "summary / confirm" wizard step shape). Field names not present on
5534
- * the resource def are silently ignored at render time.
5535
- */
5536
- fieldsFilter?: string[];
5537
- /**
5538
- * Optional override of the form-level cross-field validator. When
5539
- * provided, this validator runs in place of the resource's own
5540
- * `def.validate`. Used by `<ResourceWizard>` to attach step-level
5541
- * validation that wraps the form's normal lifecycle. Adopters who
5542
- * just want the resource-level validate should leave this undefined.
5543
- */
5544
- validateOverride?: ResourceValidator;
5545
- /**
5546
- * Optional override of the submit button's label. When omitted the
5547
- * form falls back to `submitLabel` (existing prop) and then to the
5548
- * default ('Save' / 'Create' depending on view).
5549
- */
5550
- hideCancelButton?: boolean;
5551
- /**
5552
- * @internal
5553
- * Optional callback that mirrors in-progress field values to a parent.
5554
- * The form invokes this AFTER each setFieldValue, but deferred to a
5555
- * microtask via queueMicrotask() so the parent's setState (if any) does
5556
- * not interleave with the form's own state updates.
5557
- *
5558
- * Used by ResourceWizard to mirror typed-but-not-yet-Next-ed values
5559
- * across step boundaries. Adopters generally should not need this; if
5560
- * they do, they MUST tolerate the microtask deferral and any throw
5561
- * inside the callback (errors are caught + console.error'd, but the
5562
- * form continues).
5563
- *
5564
- * Stable reference is NOT required — ResourceForm reads via the
5565
- * latest render's prop, not a memoized closure.
5566
- */
5567
- onValuesChange?: (values: Record<string, unknown>) => void;
5568
- /**
5569
- * Optional derived-value map: `targetField → (values) => computed`. Each
5570
- * target is auto-filled from the other live form values UNTIL the user edits
5571
- * it directly — or it already carried a value on mount (an edit form with a
5572
- * saved value is never auto-overwritten). A nullish/empty computed value
5573
- * never overwrites. This is the framework mechanism behind the preprint
5574
- * auto-name («<gtin> x <quantity> (<name>)»), replacing a bespoke per-form
5575
- * effect — same spirit as the `visible(ctx, values)` predicates, but writing
5576
- * a value instead of gating render.
5577
- *
5578
- * ⚠️ A templated resource resolves this itself: the matched document type's
5579
- * own `derive` map applies with no prop passed at all. This prop OVERRIDES
5580
- * that map wholesale for the render — it does not merge with it.
5581
- */
5582
- derive?: Record<string, (values: Record<string, unknown>) => unknown>;
5583
- }
5584
- declare function ResourceForm(props: ResourceFormProps): React.ReactElement | null;
5129
+ declare function CreateButton({ resource, href, testid, size, variant, label, className, onOpen }: CreateButtonProps): React.ReactElement | null;
5585
5130
  //#endregion
5586
5131
  //#region src/resource/form-context.d.ts
5587
5132
  /**
5588
5133
  * Minimal form-control handle exposed to field renderers so a field can update
5589
5134
  * SIBLING fields (a field renderer otherwise only sees its own value). Used for
5590
5135
  * reactive prefills — e.g. picking a product on a GTIN field also fills the
5591
- * «Название товара» / «Тип кода» fields (#2). `<ResourceForm>` provides it.
5136
+ * «Название товара» / «Тип кода» fields (#2). `<RecordScreen>` provides it.
5592
5137
  */
5593
5138
  interface FormControl {
5594
5139
  /** Merge a patch of `{ fieldName: value }` into the form's live values. */
@@ -5597,74 +5142,6 @@ interface FormControl {
5597
5142
  /** Read the surrounding form's control handle, or `null` outside a form. */
5598
5143
  declare function useFormControl(): FormControl | null;
5599
5144
  //#endregion
5600
- //#region src/resource/resource-wizard.d.ts
5601
- interface ResourceWizardProps<TValues = Record<string, unknown>> {
5602
- /** Wizard definition produced by `defineResourceWizard`. */
5603
- def: ResourceWizardDef<TValues>;
5604
- /** Optional initial form values (seeded into step 1). */
5605
- initialValues?: Partial<TValues>;
5606
- /**
5607
- * Called once when the user clicks Submit on the LAST step AND every
5608
- * step's validators (per-field + step-level + form-level) pass. Receives
5609
- * the full accumulated values across every step.
5610
- */
5611
- onSubmit: (values: TValues) => Promise<void>;
5612
- /**
5613
- * Called when the user clicks the Cancel button on any step. Adopters
5614
- * typically wire this to `router.back()` or `router.push(listUrl)`.
5615
- * Values are discarded — there is no draft persistence in v0.2.
5616
- */
5617
- onCancel?: () => void;
5618
- /**
5619
- * Optional view label (defaults to 'create'). Forwarded to the
5620
- * underlying `<ResourceForm>` instance for testid + system-field
5621
- * gating semantics.
5622
- */
5623
- view?: 'create' | 'edit';
5624
- /** Optional record id when editing an existing record. */
5625
- recordId?: string;
5626
- /**
5627
- * Per-template field map override (forwarded to ResourceForm). Used
5628
- * when the wizard renders a templated resource and the adopter wants
5629
- * the matched template's field set on each step.
5630
- */
5631
- fieldsOverride?: ResourceFieldMap;
5632
- className?: string;
5633
- }
5634
- /**
5635
- * Multi-step form built on top of `<ResourceForm>`. Each step renders
5636
- * one `<ResourceForm>` configured with a slice of the resource's
5637
- * fields (`fieldsFilter`) plus an optional step-level validator
5638
- * (`validateOverride`). The wizard owns the step index, the
5639
- * accumulated values across steps, and the Next / Back / Submit /
5640
- * Cancel button rail.
5641
- *
5642
- * Behavior:
5643
- * - Step 0 renders on mount.
5644
- * - Next: runs the step's validators (per-field + step.validate);
5645
- * if all pass, advances to step + 1.
5646
- * - Back: decrements the step (no validation) and preserves values.
5647
- * - Submit (last step): runs validators; on pass calls
5648
- * `onSubmit(values)` with the FULL accumulated values map.
5649
- * - Cancel: calls `onCancel?.()`.
5650
- *
5651
- * The progress indicator above the body shows every step with the
5652
- * current step highlighted. Completed steps (anything left of the
5653
- * current step) are clickable — clicking jumps back without
5654
- * re-validation. Future steps are disabled until reached.
5655
- *
5656
- * Edge cases:
5657
- * - Empty `fields[]` step → renders no inputs (summary / confirm
5658
- * pattern). Submit on a single-step wizard skips Next/Back entirely.
5659
- * - Step values persist across Back navigation — the wizard owns the
5660
- * values map directly; ResourceForm receives a fresh `initialValues`
5661
- * prop each render keyed off step id so its internal state resets
5662
- * to the wizard's snapshot.
5663
- * - Hidden / conditional fields per step are honored by ResourceForm
5664
- * (B3) via the values context the wizard threads through.
5665
- */
5666
- declare function ResourceWizard<TValues = Record<string, unknown>>(props: ResourceWizardProps<TValues>): React.ReactElement | null;
5667
- //#endregion
5668
5145
  //#region src/ui-presentations/dialog-host.d.ts
5669
5146
  /**
5670
5147
  * Concrete `dialog` host for `BuroProvider`'s `presentationHosts` prop:
@@ -5679,6 +5156,24 @@ declare namespace DialogPresentationHost {
5679
5156
  title: "string";
5680
5157
  description: "string";
5681
5158
  wrapper: "boolean";
5159
+ ownTitle: "boolean";
5160
+ }>;
5161
+ }
5162
+ //#endregion
5163
+ //#region src/ui-presentations/popover-host.d.ts
5164
+ /**
5165
+ * Concrete `popover` host for `BuroProvider`'s `presentationHosts` prop (spec 4.1, 4.12): a
5166
+ * non-modal Radix popover positioned against the request's `anchor.element`. Focus stays on the
5167
+ * anchor (nothing is focused on open, nothing is restored on close); the content is a
5168
+ * `role="dialog"` named by the presented screen. It closes through the guarded `dismiss()` on
5169
+ * Escape, on a pointer down outside it, when focus moves elsewhere, when the pointer has been off
5170
+ * both the anchor and the popover for {@link POPOVER_LEAVE_GRACE_MS}, and when the anchor leaves
5171
+ * the document.
5172
+ */
5173
+ declare function PopoverPresentationHost({ component: Component, props, options, anchor }: UIPresentationHostProps): ReactElement | null;
5174
+ declare namespace PopoverPresentationHost {
5175
+ var optionsSchema: Readonly<{
5176
+ title: "string";
5682
5177
  }>;
5683
5178
  }
5684
5179
  //#endregion
@@ -5690,6 +5185,7 @@ declare namespace SheetPresentationHost {
5690
5185
  title: "string";
5691
5186
  description: "string";
5692
5187
  wrapper: "boolean";
5188
+ ownTitle: "boolean";
5693
5189
  }>;
5694
5190
  }
5695
5191
  //#endregion
@@ -5702,12 +5198,18 @@ interface PresentationShellProps {
5702
5198
  readonly dismiss: () => void;
5703
5199
  readonly wrapper: boolean;
5704
5200
  readonly copy: PresentationCopy;
5201
+ /**
5202
+ * The presented component renders the overlay's title itself, as the Radix `Title` (a quick
5203
+ * entry's record title, plan ruling 2): the shell renders no title and no description, and
5204
+ * focus moves to the element marked `data-buro-overlay-heading` on open.
5205
+ */
5206
+ readonly ownTitle?: boolean;
5705
5207
  readonly children: ReactNode;
5706
5208
  }
5707
5209
  /** Shared Radix lifecycle shell for a centered temporary presentation. */
5708
- declare function DialogPresentationShell({ dismiss, wrapper, copy, children }: PresentationShellProps): import("react/jsx-runtime").JSX.Element;
5210
+ declare function DialogPresentationShell({ dismiss, wrapper, copy, ownTitle, children }: PresentationShellProps): import("react/jsx-runtime").JSX.Element;
5709
5211
  /** Shared Radix lifecycle shell for a right-hand temporary presentation. */
5710
- declare function SheetPresentationShell({ dismiss, wrapper, copy, children }: PresentationShellProps): import("react/jsx-runtime").JSX.Element;
5212
+ declare function SheetPresentationShell({ dismiss, wrapper, copy, ownTitle, children }: PresentationShellProps): import("react/jsx-runtime").JSX.Element;
5711
5213
  //#endregion
5712
5214
  //#region src/ui-presentations/copy.d.ts
5713
5215
  /**
@@ -5723,9 +5225,14 @@ declare function wrapperEnabled(options: Readonly<Record<string, unknown>>): boo
5723
5225
  //#endregion
5724
5226
  //#region src/ui-presentations/index.d.ts
5725
5227
  /**
5726
- * Concrete dialog/sheet hosts for `BuroProvider`'s `presentationHosts`
5727
- * prop, keyed by `UIPresentationDescriptor['host']`. Pass this straight
5728
- * through: `<BuroProvider presentationHosts={shadcnPresentationHosts} .../>`.
5228
+ * Concrete dialog/sheet/popover hosts for `BuroProvider`'s `presentationHosts`
5229
+ * prop, keyed by `UIPresentationDescriptor['host']`, and the kit's overlay
5230
+ * screens (`screens.quick`: a record's quick entry; `screens.peek`: the hover
5231
+ * preview of a relation display's record; `screens.flow`: a flow's host,
5232
+ * loaded on first use). Pass this straight through:
5233
+ * `<BuroProvider presentationHosts={shadcnPresentationHosts} .../>`; an app
5234
+ * with hosts of its own keeps quick entries and flows by spreading
5235
+ * `shadcnPresentationHosts.screens` into them.
5729
5236
  */
5730
5237
  declare const shadcnPresentationHosts: UIPresentationHosts;
5731
5238
  //#endregion
@@ -5747,6 +5254,8 @@ type IconComponent = ComponentType<SVGProps<SVGSVGElement>>;
5747
5254
  * ```
5748
5255
  */
5749
5256
  declare function registerIcons(map: Record<string, IconComponent>): void;
5257
+ /** Whether `name` is a registered icon (built-in or via {@link registerIcons}). */
5258
+ declare function hasIcon(name: string): boolean;
5750
5259
  interface IconProps {
5751
5260
  /** Semantic registry key (e.g. `'Hash'`). See REGISTRY above. */
5752
5261
  name?: string;
@@ -6048,7 +5557,7 @@ type FormItemProps = React$1.HTMLAttributes<HTMLDivElement>;
6048
5557
  declare const FormItem: React$1.ForwardRefExoticComponent<FormItemProps & React$1.RefAttributes<HTMLDivElement>>;
6049
5558
  type FormLabelProps = React$1.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>;
6050
5559
  declare const FormLabel: React$1.ForwardRefExoticComponent<Omit<LabelPrimitive.LabelProps & React$1.RefAttributes<HTMLLabelElement>, "ref"> & React$1.RefAttributes<HTMLLabelElement>>;
6051
- type FormControlProps = React$1.ComponentPropsWithoutRef<typeof Slot>;
5560
+ type FormControlProps = React$1.ComponentPropsWithoutRef<typeof Slot$1>;
6052
5561
  type FormDescriptionProps = React$1.HTMLAttributes<HTMLParagraphElement>;
6053
5562
  declare const FormDescription: React$1.ForwardRefExoticComponent<FormDescriptionProps & React$1.RefAttributes<HTMLParagraphElement>>;
6054
5563
  type FormMessageProps = React$1.HTMLAttributes<HTMLParagraphElement>;
@@ -6163,7 +5672,7 @@ declare const Label: React$1.ForwardRefExoticComponent<Omit<LabelPrimitive.Label
6163
5672
  //#region src/primitives/tabs.d.ts
6164
5673
  /**
6165
5674
  * Thin shadcn-ui-style wrapper around `@radix-ui/react-tabs`. Exposed as
6166
- * a primitive so feature components (e.g. ResourceShowTabs) can compose
5675
+ * a primitive so feature components (e.g. the screen tree's tabs node) can compose
6167
5676
  * the standard accessible Tabs pattern without re-implementing radix
6168
5677
  * boilerplate.
6169
5678
  *
@@ -6238,18 +5747,20 @@ type SidebarContextValue = {
6238
5747
  toggleSidebar: () => void;
6239
5748
  };
6240
5749
  declare function useSidebar(): SidebarContextValue;
5750
+ /** The sidebar context, or `null` outside a `SidebarProvider`. Never throws. */
5751
+ declare function useOptionalSidebar(): SidebarContextValue | null;
6241
5752
  interface SidebarProviderProps extends React$1.ComponentPropsWithoutRef<'div'> {
6242
5753
  defaultOpen?: boolean;
6243
5754
  open?: boolean;
6244
5755
  onOpenChange?: (open: boolean) => void;
6245
5756
  }
6246
5757
  declare const SidebarProvider: React$1.ForwardRefExoticComponent<SidebarProviderProps & React$1.RefAttributes<HTMLDivElement>>;
6247
- interface SidebarProps$1 extends React$1.ComponentPropsWithoutRef<'div'> {
5758
+ interface SidebarProps extends React$1.ComponentPropsWithoutRef<'div'> {
6248
5759
  side?: 'left' | 'right';
6249
5760
  variant?: 'sidebar' | 'floating' | 'inset';
6250
5761
  collapsible?: 'offcanvas' | 'icon' | 'none';
6251
5762
  }
6252
- declare const Sidebar$1: React$1.ForwardRefExoticComponent<SidebarProps$1 & React$1.RefAttributes<HTMLDivElement>>;
5763
+ declare const Sidebar: React$1.ForwardRefExoticComponent<SidebarProps & React$1.RefAttributes<HTMLDivElement>>;
6253
5764
  declare const SidebarTrigger: React$1.ForwardRefExoticComponent<Omit<ButtonProps & React$1.RefAttributes<HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
6254
5765
  declare const SidebarRail: React$1.ForwardRefExoticComponent<Omit<React$1.DetailedHTMLProps<React$1.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
6255
5766
  declare const SidebarInset: React$1.ForwardRefExoticComponent<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLElement>, HTMLElement>, "ref"> & React$1.RefAttributes<HTMLElement>>;
@@ -6270,7 +5781,7 @@ declare const SidebarMenu: React$1.ForwardRefExoticComponent<Omit<React$1.Detail
6270
5781
  declare const SidebarMenuItem: React$1.ForwardRefExoticComponent<Omit<React$1.DetailedHTMLProps<React$1.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "ref"> & React$1.RefAttributes<HTMLLIElement>>;
6271
5782
  declare const sidebarMenuButtonVariants: (props?: ({
6272
5783
  variant?: "default" | "outline" | null | undefined;
6273
- size?: "default" | "sm" | "lg" | null | undefined;
5784
+ size?: "sm" | "lg" | "default" | null | undefined;
6274
5785
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6275
5786
  interface SidebarMenuButtonProps extends React$1.ComponentPropsWithoutRef<'button'>, VariantProps<typeof sidebarMenuButtonVariants> {
6276
5787
  asChild?: boolean;
@@ -6466,9 +5977,248 @@ declare const typography: {
6466
5977
  };
6467
5978
  type TypographyKey = keyof typeof typography;
6468
5979
  //#endregion
5980
+ //#region src/shell/shell-context.d.ts
5981
+ interface ShellBrand {
5982
+ readonly name: string;
5983
+ readonly subtitle?: string;
5984
+ readonly logo?: ReactNode;
5985
+ /** Where the brand links to. Default `'/'`. */
5986
+ readonly homePath?: string;
5987
+ }
5988
+ interface ShellUser {
5989
+ readonly name: string;
5990
+ readonly email?: string;
5991
+ readonly avatarUrl?: string;
5992
+ readonly initials: string;
5993
+ }
5994
+ /** Maps the auth provider's identity to what the shell shows. `initials` is derived from `name`. */
5995
+ type ShellUserMapper = (identity: unknown) => Omit<ShellUser, 'initials'>;
5996
+ interface ShellContextValue {
5997
+ readonly brand?: ShellBrand;
5998
+ readonly mapUser: ShellUserMapper;
5999
+ /** Where sign-out and "Sign in" go. Default `'/login'`. */
6000
+ readonly loginPath: string;
6001
+ /** Set only when a command palette is mounted. */
6002
+ readonly openPalette?: () => void;
6003
+ /** The mounted palette's open state; `AppShell` sets it unless `palette={false}`. Read it with {@link usePaletteState}. */
6004
+ readonly palette?: PaletteState;
6005
+ }
6006
+ interface PaletteState {
6007
+ readonly open: boolean;
6008
+ readonly setOpen: (open: boolean) => void;
6009
+ }
6010
+ /** `name`, else `email`, else `id` for the name; `email`; `avatarUrl` else `avatar`. */
6011
+ declare function defaultUserMapper(identity: unknown): Omit<ShellUser, 'initials'>;
6012
+ type ShellProviderProps = Partial<ShellContextValue> & {
6013
+ readonly children: ReactNode;
6014
+ };
6015
+ declare function ShellProvider({ brand, mapUser, loginPath, openPalette, palette, children }: ShellProviderProps): ReactElement;
6016
+ /** Outside a `ShellProvider`: no brand, the default mapper, `/login`. */
6017
+ declare function useShell(): ShellContextValue;
6018
+ /**
6019
+ * The palette's `open` and `setOpen`, for a custom `AppShell palette={<MyPalette/>}`, which
6020
+ * receives no props. Throws outside an `AppShell` (or a `ShellProvider` given `palette`).
6021
+ */
6022
+ declare function usePaletteState(): PaletteState;
6023
+ //#endregion
6024
+ //#region src/shell/use-current-user.d.ts
6025
+ interface CurrentUser {
6026
+ readonly user: ShellUser | null;
6027
+ /** The identity is being fetched. `false` for a query that is idle (session not ready) or absent. */
6028
+ readonly isLoading: boolean;
6029
+ /** `false` when the app has no `authProvider`: there is no identity to show and no logout to run. */
6030
+ readonly available: boolean;
6031
+ /**
6032
+ * Logs out, then goes to the shell's `loginPath`; stays put on `{ success: false }`. Returns the
6033
+ * result. Rejects when the auth provider's `logout` throws instead of returning a result.
6034
+ */
6035
+ readonly signOut: () => Promise<AuthResult>;
6036
+ }
6037
+ /**
6038
+ * The signed-in user as the shell shows it. Never throws: without an `authProvider` it reports
6039
+ * `available: false` and no user.
6040
+ */
6041
+ declare function useCurrentUser(): CurrentUser;
6042
+ //#endregion
6043
+ //#region src/shell/sidebar-brand.d.ts
6044
+ /** The brand tile, name and subtitle at the top of the sidebar; reads `AppShell brand` / `ShellProvider brand`. */
6045
+ declare function SidebarBrand(): ReactElement | null;
6046
+ //#endregion
6047
+ //#region src/shell/brand-mark.d.ts
6048
+ /**
6049
+ * The `ShellProvider` brand as a standalone mark — `SidebarBrand` without the sidebar button.
6050
+ * `AuthPage` shows it when given no `brand`. Renders nothing without a brand.
6051
+ */
6052
+ declare function BrandMark(): ReactElement | null;
6053
+ //#endregion
6054
+ //#region src/shell/use-nav-icon.d.ts
6055
+ /**
6056
+ * Resolve a nav icon NAME to a node: a Components-registry slot of that key
6057
+ * first, then the `registerIcons` registry. An unknown name yields `undefined`
6058
+ * (no placeholder circle) and, outside production, warns once per name.
6059
+ */
6060
+ declare function resolveNavIcon(name: string | undefined, components: (key: string) => unknown): ReactNode | undefined;
6061
+ /** `resolveNavIcon` bound to the Components registry in context. */
6062
+ declare function useNavIcon(): (name: string | undefined) => ReactNode | undefined;
6063
+ //#endregion
6064
+ //#region src/shell/app-shell.d.ts
6065
+ /** Every slot follows {@link Slot}: omit for the default, `false` for nothing, an element to replace. */
6066
+ interface AppShellProps {
6067
+ /** Read by `SidebarBrand`, `AuthPage` and the document title. */
6068
+ brand: ShellBrand;
6069
+ /** Maps the auth provider's identity to what the user menu shows. Default: `defaultUserMapper`. */
6070
+ user?: ShellUserMapper;
6071
+ /** Where sign-out and "Sign in" go. Default `'/login'`. */
6072
+ loginPath?: string;
6073
+ /** Default `<AppSidebar />`. `false` gives a header-only layout with no sidebar trigger. */
6074
+ sidebar?: Slot;
6075
+ /** Default `<AppHeader />`. */
6076
+ header?: Slot;
6077
+ /**
6078
+ * Default `<DefaultCommandPalette />`. `false` removes the palette, the ⌘K / Ctrl+K shortcut
6079
+ * and the header's search chip. A custom element receives no props: it reads `open` and
6080
+ * `setOpen` from `usePaletteState()`.
6081
+ */
6082
+ palette?: Slot;
6083
+ /** Passed to `AppLayout`, typically `<DocPanel />`. */
6084
+ docPanel?: ReactNode;
6085
+ children: ReactNode;
6086
+ }
6087
+ /**
6088
+ * `AppLayout` with the default sidebar, header and command palette, and the brand and user
6089
+ * context they read. Renders inside `BuroProvider`, a `RouterAdapter` and a
6090
+ * `LinkComponentProvider`; it does not boot the runtime, own routes or guard auth.
6091
+ */
6092
+ declare function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, children }: AppShellProps): ReactElement;
6093
+ //#endregion
6094
+ //#region src/shell/default-command-palette.d.ts
6095
+ interface DefaultCommandPaletteProps {
6096
+ open: boolean;
6097
+ onOpenChange: (open: boolean) => void;
6098
+ /** Appended to the Actions group after theme, language and sign out. */
6099
+ actions?: ActionItem[];
6100
+ /** Replaces every group. The dialog, input, empty state and footer stay. */
6101
+ children?: ReactNode;
6102
+ }
6103
+ /**
6104
+ * The palette `AppShell` mounts by default: Docs, Recent, Records, Navigation, Create and
6105
+ * Actions, in that order. Records needs a data runtime and is left out without one. Actions are the theme choices, the languages (only with more than
6106
+ * one locale), "Sign out" (only when someone is signed in) and then `actions`.
6107
+ */
6108
+ declare function DefaultCommandPalette({ open, onOpenChange, actions, children }: DefaultCommandPaletteProps): ReactElement;
6109
+ //#endregion
6110
+ //#region src/shell/scope-switcher.d.ts
6111
+ interface ScopeOption {
6112
+ value: string;
6113
+ label: string;
6114
+ description?: string;
6115
+ icon?: ReactNode;
6116
+ }
6117
+ interface ScopeSwitcherProps {
6118
+ options: readonly ScopeOption[];
6119
+ value: string | null;
6120
+ /** A rejection is swallowed here and leaves the value unchanged; the caller reports the error. */
6121
+ onChange: (value: string) => void | Promise<void>;
6122
+ /** Dropdown heading, e.g. "Tenant". */
6123
+ label?: string;
6124
+ /** Rows rendered after a separator, e.g. an "Administration" item. */
6125
+ extra?: ReactNode;
6126
+ /** Shown when `value` is null. */
6127
+ placeholder?: string;
6128
+ }
6129
+ /** A sidebar-header picker for the current tenant, workspace or other scope. */
6130
+ declare function ScopeSwitcher({ options, value, onChange, label, extra, placeholder }: ScopeSwitcherProps): ReactElement;
6131
+ //#endregion
6132
+ //#region src/shell/user-menu.d.ts
6133
+ interface UserMenuProps {
6134
+ /** `'sidebar'`: the sidebar-footer row (avatar, name, email). `'avatar'`: a round avatar button for a header. Default `'sidebar'`. */
6135
+ variant?: 'sidebar' | 'avatar';
6136
+ /**
6137
+ * The menu's parts, in the order given. Omit to get the default set: Profile, Theme, Language
6138
+ * (only with more than one locale) and Sign out. Passed parts always render. Signed out, the
6139
+ * menu shows the passed parts except `Profile` and `SignOut`, then "Sign in"; the default set
6140
+ * signed out is Theme, Language and "Sign in".
6141
+ */
6142
+ children?: ReactNode;
6143
+ }
6144
+ declare function Profile(): ReactElement;
6145
+ declare function Theme(): ReactElement;
6146
+ declare function Language(): ReactElement | null;
6147
+ declare function Separator$1(): ReactElement;
6148
+ type ItemProps = {
6149
+ id: string;
6150
+ label: ReactNode;
6151
+ icon?: ReactNode;
6152
+ } & ({
6153
+ href: string;
6154
+ onSelect?: never;
6155
+ } | {
6156
+ onSelect: () => void;
6157
+ href?: never;
6158
+ });
6159
+ declare function Item(props: ItemProps): ReactElement;
6160
+ declare function SignOut(): ReactElement;
6161
+ declare function UserMenuRoot({ variant, children }: UserMenuProps): ReactElement | null;
6162
+ /**
6163
+ * The user's menu. Fills each part you do not pass with its default; passed children replace
6164
+ * the default set. `UserMenu.Profile`, `.Theme`, `.Language`, `.Separator`, `.Item` and
6165
+ * `.SignOut` are the parts. Signed out it is still a menu — the parts that need no identity,
6166
+ * then "Sign in" (`user-menu:sign-in`) — so theme and language stay reachable. Renders nothing
6167
+ * without an auth provider.
6168
+ */
6169
+ declare const UserMenu: typeof UserMenuRoot & {
6170
+ Profile: typeof Profile;
6171
+ Theme: typeof Theme;
6172
+ Language: typeof Language;
6173
+ Separator: typeof Separator$1;
6174
+ Item: typeof Item;
6175
+ SignOut: typeof SignOut;
6176
+ };
6177
+ //#endregion
6178
+ //#region src/shell/use-language-switch.d.ts
6179
+ interface LanguageSwitchApi {
6180
+ /** The active locale code. */
6181
+ readonly locale: string;
6182
+ /** Every locale the app can switch to; just the active one when the i18n facade does not list them. */
6183
+ readonly locales: readonly string[];
6184
+ /** Switch the live locale (no reload) and persist the choice under the provider's `languageStorageKey`. */
6185
+ readonly change: (code: string) => void;
6186
+ }
6187
+ /**
6188
+ * The headless model behind every language control: the active locale, the
6189
+ * locales on offer, and a `change` that switches live and remembers the pick.
6190
+ * `<LanguageSwitch>`, `<LanguageSubmenu>` and the palette actions are views
6191
+ * over it; an app's own control can be one too.
6192
+ */
6193
+ declare function useLanguageSwitch(): LanguageSwitchApi;
6194
+ //#endregion
6195
+ //#region src/shell/theme-controls.d.ts
6196
+ /** Radio submenu over {@link THEME_CHOICES}, bound to the stored choice (not the resolved theme). */
6197
+ declare function ThemeSubmenu(): ReactElement;
6198
+ /** Icon button cycling light, dark, system. The icon shows the current choice. */
6199
+ declare function ThemeToggle(): ReactElement;
6200
+ /** One command-palette action per theme choice. */
6201
+ declare function useThemePaletteActions(): ActionItem[];
6202
+ //#endregion
6203
+ //#region src/shell/language-submenu.d.ts
6204
+ /**
6205
+ * A locale's name in its own language ("English", "Русский", "Français"),
6206
+ * capitalised. Falls back to the bare code when `Intl.DisplayNames` does not
6207
+ * know it (or the code is not a valid BCP-47 tag).
6208
+ */
6209
+ declare function localeLabel(code: string): string;
6210
+ /**
6211
+ * Radio submenu over the app's locales. Renders nothing only when there is no
6212
+ * i18n facade; with one locale it still renders that one choice (hiding a
6213
+ * single-locale row is the default menu's decision, not this part's).
6214
+ */
6215
+ declare function LanguageSubmenu(): ReactElement | null;
6216
+ /** One command-palette action per locale; empty unless the app has more than one. */
6217
+ declare function useLanguagePaletteActions(): ActionItem[];
6218
+ //#endregion
6469
6219
  //#region src/index.d.ts
6470
6220
  declare const PACKAGE_NAME: "@burojs/ui-shadcn";
6471
6221
  declare const PACKAGE_VERSION: "0.0.0";
6472
6222
  //#endregion
6473
- export { ActionButton, type ActionButtonProps, ActionConfirmDialog, type ActionConfirmDialogProps, type ActionItem, ActionOverflow, type ActionOverflowProps, type ActionSurface, AlertDialog, AlertDialogAction, AlertDialogActionProps, AlertDialogCancel, AlertDialogCancelProps, AlertDialogContent, AlertDialogContentProps, AlertDialogDescription, AlertDialogDescriptionProps, AlertDialogFooter, AlertDialogFooterProps, AlertDialogHeader, AlertDialogHeaderProps, AlertDialogOverlay, AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, AlertDialogTitleProps, AlertDialogTrigger, AppHeader, type AppHeaderProps, AppLayout, type AppLayoutProps, AppSidebar, type AppSidebarProps, AuthPage, type AuthPageProps, Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, BooleanDisplay, BooleanInput, Breadcrumbs, type BreadcrumbsProps, type BuildNavItemsArgs, BulkActionToolbar, type BulkActionToolbarProps, Button, ButtonProps, Card, CardContent, CardContentProps, CardDescription, CardDescriptionProps, CardFooter, CardFooterProps, CardHeader, CardHeaderProps, CardProps, CardTitle, CardTitleProps, CheckConfirmDialog, type CheckConfirmDialogProps, Checkbox, CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnsToggle, Command, CommandDialog, CommandDialogProps, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandLoadingProps, CommandPalette, CommandPaletteActionsGroup, type CommandPaletteActionsGroupProps, type CommandPaletteActionsTestIdRole, CommandPaletteCreateGroup, type CommandPaletteCreateGroupProps, type CommandPaletteCreateTestIdRole, CommandPaletteDocsGroup, type CommandPaletteDocsGroupProps, CommandPaletteNavigationGroups, type CommandPaletteNavigationGroupsProps, type CommandPaletteNavigationTestIdRole, type CommandPaletteProps, CommandPaletteRecentGroup, type CommandPaletteRecentGroupProps, type CommandPaletteRecentTestIdRole, CommandPaletteRecordsGroup, type CommandPaletteRecordsGroupProps, type CommandPaletteRecordsTestIdRole, type CommandPaletteTestIdRole, CommandPaletteTrigger, type CommandPaletteTriggerProps, CommandSeparator, ContextPicker, type ContextPickerProps, CreateButton, type CreateButtonProps, CursorLoadMore, type CursorLoadMoreProps, DEFAULT_LANGUAGE, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, type DataTableDateRangeFilterProps, DataTableEnumFilter, type DataTableEnumFilterProps, DataTableFacetedFilter, type DataTableFacetedFilterProps, DataTableNumRangeFilter, type DataTableNumRangeFilterProps, DataTableOperatorFilter, type DataTableOperatorFilterProps, type DataTableProps, DataTableRowActions, type DataTableRowActionsProps, DataTableTextFilter, type DataTableTextFilterProps, DataTableToolbar, type DataTableToolbarProps, DataTableViewOptions, type DataTableViewOptionsProps, DateDisplay, DateInput, DeclarativeDisplay, type DeclarativeDisplayProps, DeclarativeForm, type DeclarativeFormProps, type DeclarativeFormSlots, DetailTabsShell, type DetailTabsShellProps, Dialog, DialogClose, DialogContent, DialogContentProps, DialogDescription, DialogDescriptionProps, type DialogFilterState, DialogFooter, DialogFooterProps, DialogHeader, DialogHeaderProps, DialogOverlayProps, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTitleProps, DialogTrigger, DisabledAction, type DisabledActionProps, DispatcherProvider, type DispatcherProviderProps, DocBrowser, type DocBrowserProps, DocInfo, type DocInfoProps, DocPanel, type DocPanelProps, DocTrailHelp, type DocTrailHelpProps, DocumentTitleProvider, DocumentTypePicker, type DocumentTypePickerProps, type DocumentTypeText, type DraftRemovalSignal, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, type EnumBadgeProps, EnumDisplay, EnumInput, EnumPicker, type EnumPickerProps, type EnumPickerRenderAs, EnumDisplay$1 as EnumValueDisplay, type EnumDisplayProps as EnumValueDisplayProps, type EnumDisplayVariant as EnumValueDisplayVariant, ExportButton, type ExportButtonProps, ExportDialog, type ExportDialogProps, type FacetedFilterOption, Field, FieldDisplay, type FieldDisplayProps, type FieldProps, FileDisplay, FileField, type FileFieldProps, FileInput, type FileInputProps, FilterBadges, type FilterBadgesProps, FilterBuilder, type FilterBuilderProps, type FilterOperator, FilterPanel, type FilterPanelProps, FilterRegion, FilterValueControl, type FilterValueControlProps, Form, FormControl, FormControlProps, FormDescription, FormDescriptionProps, FormField, FormItem, FormItemProps, FormLabel, FormLabelProps, FormMessage, FormMessageProps, Icon, IconComponent, IconProps, Input, InputProps, Label, LabelProps, type LanguageCode, LanguageSwitch, type LanguageSwitchProps, Sidebar as LegacySidebar, type SidebarProps as LegacySidebarProps, type LinkComponent, type LinkComponentProps, LinkComponentProvider, type LinkComponentProviderProps, type ListCellRenderer, ListErrorState, type ListErrorStateProps, ListSearchInput, type ListSlot, type ListSlotRegion, type LoginCredentials, LoginForm, type LoginFormProps, NavAreaLayout, type NavAreaLayoutProps, NavAreaPage, NavChrome, NavChromeBoundaryProvider, type NavChromeProps, type NavItem, type NavLayout, type NavLayoutProps, NavMain, type NavMainProps, NavUser, type NavUserProps, type NavigateFn, NavigationGuardProvider, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, PageHeader, type PageHeaderProps, PinnedFilterControls, type PinnedFilterControlsProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverContentProps, PopoverTrigger, type PresentationCopy, Progress, ProgressProps, RelationDisplay, RelationField, RelationPicker, RelationPickerDialog, type RelationPickerDialogProps, RelationPickerInput, type RelationPickerProps, type ResolvedTheme, ResourceCreate, type ResourceCreateProps, ResourceEdit, type ResourceEditProps, ResourceEditTabs, type ResourceEditTabsProps, ResourceForm, type ResourceFormProps, ResourceList, ResourceListPage, type ResourceListPageProps, type ResourceListProps, ResourcePageRouter, type ResourcePageRouterProps, ResourceShow, type ResourceShowBannerProps, type ResourceShowProps, ResourceShowTabs, type ResourceShowTabsProps, ResourceWizard, type ResourceWizardProps, type RowActionDescriptor, SavedViewsDropdown, type SavedViewsDropdownProps, SecondarySidebarLayout, Select, SelectContent, SelectContentProps, SelectGroup, SelectItem, SelectItemProps, SelectLabel, SelectLabelProps, SelectScrollDownButton, SelectScrollDownButtonProps, SelectScrollUpButton, SelectScrollUpButtonProps, SelectSeparator, SelectSeparatorProps, SelectTrigger, SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetContentProps, SheetDescription, SheetDescriptionProps, SheetFooter, SheetFooterProps, SheetHeader, SheetHeaderProps, SheetOverlayProps, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTitleProps, SheetTrigger, ShowPageHeader, Sidebar$1 as Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubButtonProps, SidebarMenuSubItem, SidebarProps$1 as SidebarProps, SidebarProvider, SidebarProviderProps, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type SpacingKey, SubresourceTabBody, THEME_STORAGE_KEY, TabBody, type TabBodyProps, type TabComponentProps, Table, TableBody, TableBodyProps, TableCaption, TableCaptionProps, TableCell, TableCellProps, TableHead, TableHeadProps, TableHeader, TableHeaderProps, TableProps, TableRow, TableRowProps, Tabs, TabsContent, TabsContentProps, TabsLayout, TabsList, TabsListProps, TabsTrigger, TabsTriggerProps, TaskRunPanel, TaskRunPanelProps, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, TextareaProps, type ThemeChoice, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TypographyKey, UnsupportedDocumentType, type UnsupportedDocumentTypeProps, type UseBuroDataTableOptions, type UseDialogFilterStateOptions, type UseThemeResult, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, createNotificationProvider, defaultNavLayouts, downloadArtifact, formatOperatorLabel, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLanguage, loadLocale, modeForSurface, pageSlot, recordDisplayName, registerIcons, relationLinkClassName, resolveUpdater, saveLanguage, saveLocale, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tabSlot, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentPath, useDialogFilterState, useDispatcher, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavChromeBoundary, useNavVisible, useNavigate, useNavigationBlocker, usePageComponent, usePresentationCopy, useSidebar, useTheme, wrapperEnabled };
6223
+ export { ActionButton, type ActionButtonProps, type ActionItem, ActionOverflow, type ActionOverflowProps, type ActionSurface, AlertDialog, AlertDialogAction, AlertDialogActionProps, AlertDialogCancel, AlertDialogCancelProps, AlertDialogContent, AlertDialogContentProps, AlertDialogDescription, AlertDialogDescriptionProps, AlertDialogFooter, AlertDialogFooterProps, AlertDialogHeader, AlertDialogHeaderProps, AlertDialogOverlay, AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, AlertDialogTitleProps, AlertDialogTrigger, AppActions, AppFlowButton, type AppFlowButtonProps, AppHeader, type AppHeaderProps, AppLayout, type AppLayoutProps, AppShell, type AppShellProps, AppSidebar, type AppSidebarProps, AuthPage, type AuthPageProps, Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, BooleanDisplay, BooleanInput, BrandMark, Breadcrumbs, type BreadcrumbsProps, type BuildNavItemsArgs, BulkActionToolbar, type BulkActionToolbarProps, Button, ButtonProps, Card, CardContent, CardContentProps, CardDescription, CardDescriptionProps, CardFooter, CardFooterProps, CardHeader, CardHeaderProps, CardProps, CardTitle, CardTitleProps, CheckConfirmDialog, type CheckConfirmDialogProps, Checkbox, CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnsToggle, Command, CommandDialog, CommandDialogProps, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandLoadingProps, CommandPalette, CommandPaletteActionsGroup, type CommandPaletteActionsGroupProps, type CommandPaletteActionsTestIdRole, CommandPaletteCreateGroup, type CommandPaletteCreateGroupProps, type CommandPaletteCreateTestIdRole, CommandPaletteDocsGroup, type CommandPaletteDocsGroupProps, CommandPaletteNavigationGroups, type CommandPaletteNavigationGroupsProps, type CommandPaletteNavigationTestIdRole, type CommandPaletteProps, CommandPaletteRecentGroup, type CommandPaletteRecentGroupProps, type CommandPaletteRecentTestIdRole, CommandPaletteRecordsGroup, type CommandPaletteRecordsGroupProps, type CommandPaletteRecordsTestIdRole, type CommandPaletteTestIdRole, CommandPaletteTrigger, type CommandPaletteTriggerProps, CommandSeparator, ContextPicker, type ContextPickerProps, CopyId, CreateButton, type CreateButtonProps, CursorLoadMore, type CursorLoadMoreProps, DEFAULT_LANGUAGE, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, type DataTableDateRangeFilterProps, DataTableEnumFilter, type DataTableEnumFilterProps, DataTableFacetedFilter, type DataTableFacetedFilterProps, DataTableNumRangeFilter, type DataTableNumRangeFilterProps, DataTableOperatorFilter, type DataTableOperatorFilterProps, type DataTableProps, DataTableRowActions, type DataTableRowActionsProps, DataTableTextFilter, type DataTableTextFilterProps, DataTableToolbar, type DataTableToolbarProps, DataTableViewOptions, type DataTableViewOptionsProps, DateDisplay, DateInput, DeclarativeDisplay, type DeclarativeDisplayProps, DeclarativeForm, type DeclarativeFormProps, type DeclarativeFormSlots, DefaultCommandPalette, type DefaultCommandPaletteProps, Dialog, DialogClose, DialogContent, DialogContentProps, DialogDescription, DialogDescriptionProps, type DialogFilterState, DialogFooter, DialogFooterProps, DialogHeader, DialogHeaderProps, DialogOverlayProps, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTitleProps, DialogTrigger, DisabledAction, type DisabledActionProps, DocBrowser, type DocBrowserProps, DocInfo, type DocInfoProps, DocPanel, type DocPanelProps, DocTrailHelp, type DocTrailHelpProps, DocumentTitleProvider, type DraftRemovalSignal, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, type EnumBadgeProps, EnumDisplay, EnumInput, EnumPicker, type EnumPickerProps, type EnumPickerRenderAs, EnumDisplay$1 as EnumValueDisplay, type EnumDisplayProps as EnumValueDisplayProps, type EnumDisplayVariant as EnumValueDisplayVariant, ExportButton, type ExportButtonProps, ExportDialog, type ExportDialogProps, type FacetedFilterOption, Field, FieldDisplay, type FieldDisplayProps, type FieldProps, FileDisplay, FileField, type FileFieldProps, FileInput, type FileInputProps, FilterBadges, type FilterBadgesProps, FilterBuilder, type FilterBuilderProps, type FilterOperator, FilterPanel, type FilterPanelProps, FilterRegion, FilterValueControl, type FilterValueControlProps, FlowActionButton, type FlowActionButtonProps, Form, FormActions, FormControl, FormControlProps, FormDescription, FormDescriptionProps, FormField, FormItem, FormItemProps, FormLabel, FormLabelProps, FormMessage, FormMessageProps, HeaderBreadcrumbs, HelpButton, type HelpButtonProps, Icon, IconComponent, IconProps, Input, InputProps, Label, LabelProps, type LanguageCode, LanguageSubmenu, LanguageSwitch, type LanguageSwitchApi, type LanguageSwitchProps, type LinkComponent, type LinkComponentProps, LinkComponentProvider, type LinkComponentProviderProps, ListBody, ListBulkBar, type ListCellRenderer, ListColumns, ListControls, ListCreate, ListErrorState, type ListErrorStateProps, ListExport, ListFilters, ListFlows, ListLive, ListSavedViews, ListSearchInput, type ListSlot, type ListSlotRegion, type LoginCredentials, LoginForm, type LoginFormProps, NavAreaLayout, type NavAreaLayoutProps, NavAreaPage, NavChrome, NavChromeBoundaryProvider, type NavChromeProps, type NavItem, type NavLayout, type NavLayoutProps, NavMain, type NavMainProps, type NavTreeEntry, type NavTreeLayout, type NavTreeSection, NavigationGuardDialog, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, type PageBreadcrumbsProps, PageHeader, type PageHeaderProps, type PaletteState, PinnedFilterControls, type PinnedFilterControlsProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverContentProps, PopoverPresentationHost, PopoverTrigger, type PresentationCopy, Progress, ProgressProps, RecordActions, RecordBreadcrumbs, RecordPage, type RecordPageProps, RecordScreen, type RecordScreenProps, RecordTitle, RefreshButton, type RefreshButtonProps, RelationDisplay, RelationField, RelationManyDisplay, RelationPicker, LazyRelationPickerDialog as RelationPickerDialog, type RelationPickerDialogProps, RelationPickerInput, type RelationPickerProps, type ResolvedTheme, type ResourceBreadcrumbsProps, ResourceList, ResourceListPage, type ResourceListPageProps, type ResourceListProps, ResourcePageRouter, type ResourcePageRouterProps, type RowActionDescriptor, SavedViewsDropdown, type SavedViewsDropdownProps, type ScopeOption, ScopeSwitcher, type ScopeSwitcherProps, SecondarySidebarLayout, Select, SelectContent, SelectContentProps, SelectGroup, SelectItem, SelectItemProps, SelectLabel, SelectLabelProps, SelectScrollDownButton, SelectScrollDownButtonProps, SelectScrollUpButton, SelectScrollUpButtonProps, SelectSeparator, SelectSeparatorProps, SelectTrigger, SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetContentProps, SheetDescription, SheetDescriptionProps, SheetFooter, SheetFooterProps, SheetHeader, SheetHeaderProps, SheetOverlayProps, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTitleProps, SheetTrigger, type ShellBrand, type ShellContextValue, ShellProvider, type ShellProviderProps, type ShellUser, type ShellUserMapper, ShowPageHeader, Sidebar, SidebarBrand, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubButtonProps, SidebarMenuSubItem, SidebarProps, SidebarProvider, SidebarProviderProps, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type Slot, type SpacingKey, THEME_CHOICES, THEME_STORAGE_KEY, Table, TableBody, TableBodyProps, TableCaption, TableCaptionProps, TableCell, TableCellProps, TableHead, TableHeadProps, TableHeader, TableHeaderProps, TableProps, TableRow, TableRowProps, Tabs, TabsContent, TabsContentProps, TabsLayout, TabsList, TabsListProps, TabsTrigger, TabsTriggerProps, TaskRunPanel, TaskRunPanelProps, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, TextareaProps, type ThemeChoice, ThemeSubmenu, ThemeToggle, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TypographyKey, type UseBuroDataTableOptions, type UseDialogFilterStateOptions, type UseNavTreeOptions, type UseThemeResult, UserMenu, type UserMenuProps, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, defaultNavLayouts, defaultUserMapper, downloadArtifact, formatOperatorLabel, hasIcon, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLanguage, loadLocale, localeLabel, modeForSurface, recordDisplayName, registerIcons, relationLinkClassName, resolveNavIcon, resolveUpdater, saveLanguage, saveLocale, shadcnBlockImplementations, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentUser, useDialogFilterState, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLanguagePaletteActions, useLanguageSwitch, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavChromeBoundary, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useShell, useSidebar, useTheme, useThemePaletteActions, wrapperEnabled };
6474
6224
  //# sourceMappingURL=index.d.ts.map