@burojs/ui-shadcn 0.5.0 → 0.6.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 (133) hide show
  1. package/dist/candidates-gate-Bb70LPAy.js +135 -0
  2. package/dist/candidates-gate-Bb70LPAy.js.map +1 -0
  3. package/dist/candidates-gate-D33jXLHO.cjs +153 -0
  4. package/dist/candidates-gate-D33jXLHO.cjs.map +1 -0
  5. package/dist/candidates-relation-picker-B116Bmqi.js +31 -0
  6. package/dist/candidates-relation-picker-B116Bmqi.js.map +1 -0
  7. package/dist/candidates-relation-picker-Cy0rTxvI.cjs +31 -0
  8. package/dist/candidates-relation-picker-Cy0rTxvI.cjs.map +1 -0
  9. package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog--gPFnEeg.cjs} +27 -14
  10. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +1 -0
  11. package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-bRT8lt0d.js} +28 -15
  12. package/dist/check-confirm-dialog-bRT8lt0d.js.map +1 -0
  13. package/dist/{filter-builder-DOnaXNfP.js → filter-builder-Bhn-dhZV.js} +30 -14
  14. package/dist/filter-builder-Bhn-dhZV.js.map +1 -0
  15. package/dist/{filter-builder-B3mZFL-K.cjs → filter-builder-nXqtM_h4.cjs} +39 -11
  16. package/dist/filter-builder-nXqtM_h4.cjs.map +1 -0
  17. package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-D3avVH0I.cjs} +3 -3
  18. package/dist/{flow-runtime-B6zOHYqI.cjs.map → flow-runtime-D3avVH0I.cjs.map} +1 -1
  19. package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-s6lkRPKF.js} +3 -3
  20. package/dist/{flow-runtime-D8h99FSg.js.map → flow-runtime-s6lkRPKF.js.map} +1 -1
  21. package/dist/{flow-screen-CmbARSqg.js → flow-screen-CCvgDI9G.js} +11 -8
  22. package/dist/flow-screen-CCvgDI9G.js.map +1 -0
  23. package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Ci_Sik4Y.cjs} +10 -7
  24. package/dist/flow-screen-Ci_Sik4Y.cjs.map +1 -0
  25. package/dist/index.cjs +577 -56
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +181 -103
  28. package/dist/index.d.ts +181 -103
  29. package/dist/index.js +567 -45
  30. package/dist/index.js.map +1 -1
  31. package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-B6FI0ctQ.js} +3 -3
  32. package/dist/{inline-flow-host-C3H2UpSA.js.map → inline-flow-host-B6FI0ctQ.js.map} +1 -1
  33. package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-DV1et3c6.cjs} +3 -3
  34. package/dist/{inline-flow-host-B7U1OJUe.cjs.map → inline-flow-host-DV1et3c6.cjs.map} +1 -1
  35. package/dist/label-66W1qbke.js.map +1 -1
  36. package/dist/label-w6JYf2cF.cjs.map +1 -1
  37. package/dist/markdown/index.d.cts +2 -3
  38. package/dist/markdown/index.d.ts +2 -3
  39. package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-hdnULWqd.cjs} +6 -6
  40. package/dist/{page-flow-route-CQ7HsbBm.cjs.map → page-flow-route-hdnULWqd.cjs.map} +1 -1
  41. package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-i3ZfJ1sd.js} +6 -6
  42. package/dist/{page-flow-route-CyXSvXSE.js.map → page-flow-route-i3ZfJ1sd.js.map} +1 -1
  43. package/dist/{place-node-SIp4XYQH.cjs → place-node-C5SmIAYU.cjs} +441 -135
  44. package/dist/place-node-C5SmIAYU.cjs.map +1 -0
  45. package/dist/{place-node--GdsP688.js → place-node-NraNYKPx.js} +420 -138
  46. package/dist/place-node-NraNYKPx.js.map +1 -0
  47. package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BWZ9GfOG.js} +3 -3
  48. package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BWZ9GfOG.js.map} +1 -1
  49. package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CFq3meUn.cjs} +3 -3
  50. package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CFq3meUn.cjs.map} +1 -1
  51. package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-CCzl71aA.cjs} +2 -2
  52. package/dist/{related-list-timeline-BYJKEbmA.cjs.map → related-list-timeline-CCzl71aA.cjs.map} +1 -1
  53. package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-yrPsjA3F.js} +2 -2
  54. package/dist/{related-list-timeline-DEdfd-rr.js.map → related-list-timeline-yrPsjA3F.js.map} +1 -1
  55. package/dist/{related-rows-zh5f_4QR.cjs → related-rows-ea43qhhy.cjs} +10 -11
  56. package/dist/related-rows-ea43qhhy.cjs.map +1 -0
  57. package/dist/{related-rows-B4levHcR.js → related-rows-o0_t-Yce.js} +7 -8
  58. package/dist/related-rows-o0_t-Yce.js.map +1 -0
  59. package/dist/{relation-create-BOaUe12G.cjs → relation-create-IA40t1hi.cjs} +10 -8
  60. package/dist/relation-create-IA40t1hi.cjs.map +1 -0
  61. package/dist/{relation-create-Bg-eEJLs.js → relation-create-h6F9B4C4.js} +10 -8
  62. package/dist/relation-create-h6F9B4C4.js.map +1 -0
  63. package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-C8zFEjwd.cjs} +14 -12
  64. package/dist/relation-many-picker-C8zFEjwd.cjs.map +1 -0
  65. package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-bvkjkv-k.js} +13 -11
  66. package/dist/relation-many-picker-bvkjkv-k.js.map +1 -0
  67. package/dist/{relation-picker-BAY77ivO.js → relation-picker-Z0ZJJ5uS.js} +68 -37
  68. package/dist/relation-picker-Z0ZJJ5uS.js.map +1 -0
  69. package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-0IzkjmiA.cjs} +17 -13
  70. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +1 -0
  71. package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-B6nIiJBf.js} +18 -14
  72. package/dist/relation-picker-dialog-B6nIiJBf.js.map +1 -0
  73. package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-gag0qSR5.cjs} +74 -37
  74. package/dist/relation-picker-gag0qSR5.cjs.map +1 -0
  75. package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-3RUf9d1j.js} +44 -38
  76. package/dist/resource-page-router-3RUf9d1j.js.map +1 -0
  77. package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-PXfMW9nh.cjs} +42 -36
  78. package/dist/resource-page-router-PXfMW9nh.cjs.map +1 -0
  79. package/dist/{search-access-CURsWn-U.js → search-access-CPPmfuvy.js} +4 -19
  80. package/dist/search-access-CPPmfuvy.js.map +1 -0
  81. package/dist/{search-access-B7G5oR8n.cjs → search-access-Dugvttlg.cjs} +3 -24
  82. package/dist/search-access-Dugvttlg.cjs.map +1 -0
  83. package/dist/{toaster-CfCRLgR-.js → toaster-C02XbB9r.js} +30 -59
  84. package/dist/toaster-C02XbB9r.js.map +1 -0
  85. package/dist/{toaster-C7CiYA4Z.cjs → toaster-Cr51X7bz.cjs} +30 -83
  86. package/dist/toaster-Cr51X7bz.cjs.map +1 -0
  87. package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-C--oMAZG.js} +2 -2
  88. package/dist/{use-dialog-filter-state-BDcgS5Oz.js.map → use-dialog-filter-state-C--oMAZG.js.map} +1 -1
  89. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-Dhe8PcB5.cjs} +2 -2
  90. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs.map → use-dialog-filter-state-Dhe8PcB5.cjs.map} +1 -1
  91. package/dist/use-theme-DFlpXils.cjs.map +1 -1
  92. package/dist/use-theme-dnE3Od3I.js.map +1 -1
  93. package/package.json +9 -9
  94. package/dist/candidates-gate-CX2PBu6l.js +0 -63
  95. package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
  96. package/dist/candidates-gate-DiFI52r7.cjs +0 -70
  97. package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
  98. package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
  99. package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
  100. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
  101. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
  102. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
  103. package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
  104. package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
  105. package/dist/filter-builder-DOnaXNfP.js.map +0 -1
  106. package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
  107. package/dist/flow-screen-CmbARSqg.js.map +0 -1
  108. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
  109. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
  110. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
  111. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
  112. package/dist/peek-filter-CnqHwTMU.cjs +0 -51
  113. package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
  114. package/dist/peek-filter-ErLfO153.js +0 -28
  115. package/dist/peek-filter-ErLfO153.js.map +0 -1
  116. package/dist/place-node--GdsP688.js.map +0 -1
  117. package/dist/place-node-SIp4XYQH.cjs.map +0 -1
  118. package/dist/related-rows-B4levHcR.js.map +0 -1
  119. package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
  120. package/dist/relation-create-BOaUe12G.cjs.map +0 -1
  121. package/dist/relation-create-Bg-eEJLs.js.map +0 -1
  122. package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
  123. package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
  124. package/dist/relation-picker-BAY77ivO.js.map +0 -1
  125. package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
  126. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
  127. package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
  128. package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
  129. package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
  130. package/dist/search-access-B7G5oR8n.cjs.map +0 -1
  131. package/dist/search-access-CURsWn-U.js.map +0 -1
  132. package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
  133. package/dist/toaster-CfCRLgR-.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-access-CPPmfuvy.js","names":["Command","CommandPrimitive"],"sources":["../src/primitives/command.tsx","../src/fields/claimed-quick-link.tsx","../src/resource/record-display-name.ts","../src/resource/use-row-title.ts","../src/quick/peek-context.ts","../src/quick/use-peek-intent.tsx","../src/fields/relation-display.tsx","../src/fields/search-access.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport { IconSearch } from '@tabler/icons-react';\nimport { cn } from '../lib/cn.js';\nimport {\n Dialog,\n DialogContent,\n DialogTitle,\n DialogDescription,\n type DialogContentProps,\n} from './dialog.js';\n\n/**\n * Shadcn-canonical Command primitive built on `cmdk`. Provides\n * keyboard-navigable, ARIA-correct combobox/listbox semantics: arrow\n * keys to navigate items, Enter to select, type-ahead search via the\n * embedded `<CommandInput>`. Use this for any searchable list inside a\n * Popover (typeaheads, command palettes, relation pickers).\n *\n * Filter behavior: by default cmdk filters items client-side based on\n * the input text. For server-side filtering (typical for relation\n * pickers that hit `useList` with a `contains` operator), pass\n * `shouldFilter={false}` to the root `<Command>` and drive the option\n * list yourself from the server-side results.\n */\nconst Command = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn(\n 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',\n className,\n )}\n {...props}\n />\n));\nCommand.displayName = CommandPrimitive.displayName;\n\nconst CommandInput = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <IconSearch className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" aria-hidden=\"true\" />\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled',\n className,\n )}\n {...props}\n />\n </div>\n));\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n));\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n <CommandPrimitive.Empty\n ref={ref}\n className=\"py-6 text-center text-sm text-muted-foreground\"\n {...props}\n />\n));\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n 'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 h-px bg-border', className)}\n {...props}\n />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n \"group relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nexport type CommandLoadingProps = React.ComponentPropsWithoutRef<typeof CommandPrimitive.Loading>;\n\nconst CommandLoading = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Loading>,\n CommandLoadingProps\n>(({ ...props }, ref) => <CommandPrimitive.Loading ref={ref} {...props} />);\nCommandLoading.displayName = CommandPrimitive.Loading.displayName;\n\n/**\n * Props for `<CommandDialog>`. Mirrors Radix Dialog's `Root` props\n * (`open`, `onOpenChange`, `defaultOpen`, `modal`) plus optional\n * accessibility labels and dialog-content className overrides.\n *\n * The shadcn-canonical pattern wraps a Radix `<Dialog>` around the\n * `<Command>` root so cmdk's keyboard navigation (arrow keys, Enter,\n * type-ahead) operates on the items rendered as children. We render\n * a visually-hidden `<DialogTitle>` + `<DialogDescription>` for the\n * a11y tree (Radix requires a labelled dialog or it warns at runtime).\n */\nexport interface CommandDialogProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n modal?: boolean;\n /** Accessible title (visually hidden). Defaults to \"Command Palette\". */\n title?: string;\n /** Accessible description (visually hidden). Defaults to \"Search and run actions.\" */\n description?: string;\n /** Forwarded to the `<DialogContent>` className. */\n className?: string;\n /** Forwarded to the inner `<Command>` className. */\n commandClassName?: string;\n /** The Command groups + items + input. */\n children?: React.ReactNode;\n /** Optional dialog-content overrides (e.g. style for animations). */\n contentProps?: Omit<DialogContentProps, 'children' | 'className'>;\n}\n\n/**\n * Modal command palette. Combines Radix Dialog with cmdk's `<Command>`\n * root so adopters can compose `<CommandInput>`, `<CommandList>`,\n * `<CommandGroup>`, `<CommandItem>` as children and get focus-trapped\n * keyboard navigation out of the box.\n *\n * Usage:\n * ```tsx\n * <CommandDialog open={open} onOpenChange={setOpen}>\n * <CommandInput placeholder=\"Search…\" />\n * <CommandList>\n * <CommandEmpty>No matches.</CommandEmpty>\n * <CommandGroup heading=\"Resources\">\n * <CommandItem onSelect={...}>Documents</CommandItem>\n * </CommandGroup>\n * </CommandList>\n * </CommandDialog>\n * ```\n */\nfunction CommandDialog({\n open,\n onOpenChange,\n defaultOpen,\n modal,\n title = 'Command Palette',\n description = 'Search and run actions.',\n className,\n commandClassName,\n children,\n contentProps,\n}: CommandDialogProps) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={defaultOpen} modal={modal}>\n <DialogContent\n {...contentProps}\n // Width: cap to 480px regardless of breakpoint (the shadcn default\n // `DialogContent` uses `max-w-lg` ~512px). `sm:max-w-[480px]`\n // re-asserts at the `sm` breakpoint so any built-in `sm:max-w-lg`\n // override loses to ours.\n className={cn('overflow-hidden p-0 shadow-lg max-w-[480px] sm:max-w-[480px]', className)}\n // Backdrop blur for the command palette specifically: softens\n // the underlying chrome so the palette feels like a focused\n // overlay rather than a stacked panel. Other dialogs keep the\n // shadcn-default solid backdrop.\n overlayClassName=\"backdrop-blur-sm\"\n >\n {/* Visually-hidden labels: Radix Dialog needs them for a11y, but\n the palette UI itself supplies its own search affordance. */}\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <DialogDescription className=\"sr-only\">{description}</DialogDescription>\n <Command\n className={cn(\n '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5',\n commandClassName,\n )}\n >\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\nCommandDialog.displayName = 'CommandDialog';\n\nexport {\n Command,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandSeparator,\n CommandLoading,\n CommandDialog,\n};\n","'use client';\nimport { QUICK_ANCHOR_ATTRIBUTE, quickAnchorRef } from '@burojs/react';\nimport type * as React from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\n\n/** A plain left click: the main button, no modifier. Anything else follows the `href`. */\nexport function isPlainLeftClick(event: React.MouseEvent): boolean {\n return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;\n}\n\n/** Claims the anchor an app's link component rendered (it may not forward a ref). */\nfunction claimAnchor(wrapper: HTMLSpanElement | null): void {\n const anchor = wrapper?.querySelector('a') ?? null;\n quickAnchorRef(anchor);\n}\n\n/**\n * A quick opener's anchor (spec 4.5): the app's link component to the record's page, marked with\n * `data-buro-quick` and claimed for its opener, so the navigation guard leaves its click to it. A\n * plain left click calls `preventDefault()` and `onOpen()`; a modified click follows the `href`\n * (a new tab, guarded as any link).\n */\nexport function ClaimedQuickLink({\n href,\n testid,\n className,\n onOpen,\n children,\n}: {\n readonly href: string;\n readonly testid?: string;\n readonly className?: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const RouterLink = useLinkComponent();\n return (\n <span ref={claimAnchor} className=\"contents\">\n <RouterLink\n href={href}\n {...(testid === undefined ? {} : { 'data-testid': testid })}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n {...(className === undefined ? {} : { className })}\n onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {\n event.stopPropagation();\n if (!isPlainLeftClick(event) || event.currentTarget.target === '_blank') return;\n event.preventDefault();\n onOpen();\n }}\n >\n {children}\n </RouterLink>\n </span>\n );\n}\n","/**\n * A record's display name: its `displayField` value when present and non-empty, else its `id`.\n * Exported for consumers building delete dialogs, breadcrumbs and relation labels.\n */\nexport function recordDisplayName(record: Record<string, unknown>, displayField?: string): string {\n if (displayField && record[displayField] != null && record[displayField] !== '') {\n return String(record[displayField]);\n }\n return String(record.id ?? '');\n}\n","import { useListAccess } from '@burojs/react';\nimport { useCallback } from 'react';\nimport { recordDisplayName } from './record-display-name.js';\n\n/**\n * The display name of a row of `target` as a list surface may show it (P5 spec 3.6, decision\n * P5-27): the row's `displayField` value only while that field is visible in any-record reach,\n * else the id. While the decision is pending the id is shown as well, never the value. Picker\n * options, relation displays, related labels and palette results read their titles through this.\n */\nexport function useRowTitle(\n target: string,\n displayField: string | undefined,\n): (row: Record<string, unknown>) => string {\n const access = useListAccess(target);\n const visible =\n displayField !== undefined && access.status === 'ready' && access.fieldVisible(displayField);\n return useCallback(\n (row) => recordDisplayName(row, visible ? displayField : undefined),\n [visible, displayField],\n );\n}\n","'use client';\nimport { createContext } from 'react';\n\n/**\n * @internal What the popover host gives the screen inside it: the anchor element it is positioned\n * against (the relation display the peek was opened from), and a setter for the popover's\n * accessible name once the screen knows the record's title.\n */\nexport interface PeekHostValue {\n readonly anchor: HTMLElement | null;\n setLabel(label: string): void;\n}\n\n/** @internal Provided by `PopoverPresentationHost`; `null` outside a popover. */\nexport const PeekHostContext = createContext<PeekHostValue | null>(null);\n\n/**\n * @internal True inside a peek (provided by `PeekScreen`): a relation display there attaches no\n * hover intent, so a peek never opens a second peek (spec 4.6).\n */\nexport const PeekContext = createContext(false);\n","'use client';\nimport {\n PEEK_COMPONENT,\n type PeekScreenProps,\n useCurrentUIActivation,\n useEntryGate,\n useQuickOpener,\n useUIPresentationRuntime,\n} from '@burojs/react';\nimport {\n type FocusEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { PeekContext } from './peek-context.js';\n\n/** The hover (and keyboard-focus) delay before a peek opens (spec 4.6). */\nexport const PEEK_DELAY_MS = 500;\n/** Focus this soon after a Tab or arrow keydown is keyboard modality. */\nconst KEYBOARD_FOCUS_WINDOW_MS = 100;\n\nconst NAVIGATION_KEYS = new Set(['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);\n\n/** When the last Tab or arrow keydown happened; a pointer down forgets it. */\nlet lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\nconst listening = new WeakSet<Document>();\n\nfunction listenForModality(doc: Document): void {\n if (listening.has(doc)) return;\n listening.add(doc);\n doc.addEventListener(\n 'keydown',\n (event) => {\n if (NAVIGATION_KEYS.has(event.key)) lastNavigationKeyAt = Date.now();\n },\n true,\n );\n const forget = () => {\n lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\n };\n doc.addEventListener('pointerdown', forget, true);\n doc.addEventListener('mousedown', forget, true);\n}\n\n/** Whether a focus now came from the keyboard (a Tab or arrow keydown just before it). */\nfunction keyboardModality(): boolean {\n return Date.now() - lastNavigationKeyAt <= KEYBOARD_FOCUS_WINDOW_MS;\n}\n\n/** Elements about to be focused by code: their next focus opens no peek. */\nconst focusedByCode = new WeakSet<Element>();\n\n/**\n * @internal Focus `element` by code (the peek's anchor, when its title opens `quick.show`): the\n * focus opens no peek, whatever key was pressed last.\n */\nexport function focusWithoutPeek(element: HTMLElement): void {\n focusedByCode.add(element);\n element.focus();\n focusedByCode.delete(element);\n}\n\n/** What opens a record from where an anchor sits: its display's own quick opener. */\nexport type PeekAnchorOpener = (target: string, id: string) => void;\n\n/**\n * Each anchor's opener, taken where the display renders: inside a quick sheet it pushes into that\n * sheet, inside a dialog it closes the dialog first. The peek renders beside the overlays, outside\n * the sheet's context, so its title opens through this instead of its own opener.\n */\nconst anchorOpeners = new WeakMap<Element, PeekAnchorOpener>();\n\n/** @internal The opener registered for `anchor` by its peek intent, if any. */\nexport function peekAnchorOpener(anchor: Element | null): PeekAnchorOpener | undefined {\n return anchor === null ? undefined : anchorOpeners.get(anchor);\n}\n\n/** What a peek previews: the display's target and the key it holds (an id, or a `by` key). */\nexport interface PeekTarget {\n readonly to: string;\n readonly id?: string;\n readonly by?: { readonly field: string; readonly key: string };\n}\n\n/** The anchor inside the wrapper: the opener (`data-buro-quick`), else the first element. */\nfunction anchorIn(wrapper: HTMLElement | null): HTMLElement | null {\n if (wrapper === null) return null;\n const marked = wrapper.querySelector<HTMLElement>('[data-buro-quick]');\n if (marked !== null) return marked;\n const first = wrapper.firstElementChild;\n return first instanceof HTMLElement ? first : null;\n}\n\nconst noSubscription = () => () => undefined;\n\n/**\n * A relation display's hover intent (spec 4.6): a `mouse` or `pen` pointer resting on the anchor,\n * or keyboard-modality focus on it (a Tab or arrow keydown just before, never focus set by code),\n * for {@link PEEK_DELAY_MS} asks the entry gate for the target's peek; only `open` presents it,\n * after dismissing any other open peek. Leaving or blurring before then cancels. Nothing is read\n * before the peek opens. Inside a peek it renders its children only.\n */\nexport function PeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const inside = useContext(PeekContext);\n if (inside) return <>{children}</>;\n return <ActivePeekIntent target={target}>{children}</ActivePeekIntent>;\n}\n\nfunction ActivePeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const gate = useEntryGate();\n const runtime = useUIPresentationRuntime();\n const activation = useCurrentUIActivation();\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const wrapper = useRef<HTMLSpanElement | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The latest of each, read when the timer fires (the access answer may have changed since).\n const latest = useRef({ gate, target, activation });\n latest.current = { gate, target, activation };\n\n const attach = useCallback((element: HTMLSpanElement | null) => {\n wrapper.current = element;\n if (element !== null && element.ownerDocument !== undefined)\n listenForModality(element.ownerDocument);\n setAnchor(anchorIn(element));\n }, []);\n\n const expanded = useSyncExternalStore(\n anchor === null ? noSubscription : runtime.subscribe,\n () =>\n anchor !== null &&\n runtime\n .getSnapshot()\n .some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === anchor),\n () => false,\n );\n const peekable = gate(target.to, 'peek').state === 'open';\n\n // The anchor is the app's link (it may not forward props): its popup state is set on it.\n useEffect(() => {\n if (anchor === null) return;\n if (!peekable) {\n anchor.removeAttribute('aria-haspopup');\n anchor.removeAttribute('aria-expanded');\n return;\n }\n anchor.setAttribute('aria-haspopup', 'dialog');\n anchor.setAttribute('aria-expanded', expanded ? 'true' : 'false');\n }, [anchor, peekable, expanded]);\n\n // The opener of where this display sits (a sheet pushes, a dialog closes first), for every\n // display with a peek: an eager one, a lazy `by` one and one under undecided access alike.\n const quickOpen = useQuickOpener();\n const onOpen = useCallback<PeekAnchorOpener>((to, id) => void quickOpen(to, id), [quickOpen]);\n useEffect(() => {\n if (anchor === null) return;\n anchorOpeners.set(anchor, onOpen);\n return () => {\n if (anchorOpeners.get(anchor) === onOpen) anchorOpeners.delete(anchor);\n };\n }, [anchor, onOpen]);\n\n const cancel = useCallback(() => {\n if (timer.current === null) return;\n clearTimeout(timer.current);\n timer.current = null;\n }, []);\n useEffect(() => cancel, [cancel]);\n\n const fire = useCallback(() => {\n timer.current = null;\n const { gate: ask, target: what, activation: from } = latest.current;\n const element = anchorIn(wrapper.current);\n if (element === null || !element.isConnected) return;\n // Only `open` presents: undecided access reads nothing (spec 4.14), a refusal opens nothing.\n const answer = ask(what.to, 'peek');\n if (answer.state !== 'open') return;\n const snapshot = runtime.getSnapshot();\n if (snapshot.some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === element))\n return;\n // One peek at a time.\n for (const descriptor of snapshot)\n if (descriptor.component === PEEK_COMPONENT) runtime.dismiss(descriptor.id);\n const props: PeekScreenProps = {\n target: answer.target,\n ...(what.id === undefined ? {} : { id: what.id }),\n ...(what.by === undefined ? {} : { by: what.by }),\n };\n runtime.present({\n component: PEEK_COMPONENT,\n host: 'popover',\n anchor: { element },\n props: props as unknown as Readonly<Record<string, unknown>>,\n activation: from,\n });\n }, [runtime]);\n\n const schedule = useCallback(() => {\n if (timer.current !== null) return;\n timer.current = setTimeout(fire, PEEK_DELAY_MS);\n }, [fire]);\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: a `display: contents` wrapper that only observes the anchor inside it; the anchor is the interactive element\n <span\n ref={attach}\n className=\"contents\"\n onPointerEnter={(event: PointerEvent) => {\n if (event.pointerType === 'mouse' || event.pointerType === 'pen') schedule();\n }}\n onPointerLeave={cancel}\n onFocus={(event: FocusEvent) => {\n if (focusedByCode.has(event.target)) return;\n if (keyboardModality()) schedule();\n }}\n onBlur={cancel}\n // A click is always quick.show (spec 4.6): it cancels a pending peek.\n onPointerDown={cancel}\n onClickCapture={cancel}\n >\n {children}\n </span>\n );\n}\n","'use client';\nimport type * as React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n hasRecordPage,\n relationAmbiguousError,\n resourceKey,\n relationOpenOf,\n type RelationOpen,\n resourceShowHref,\n type ResourceDef,\n type ResourceRefInput,\n} from '@burojs/core';\nimport {\n QUICK_ANCHOR_ATTRIBUTE,\n useApp,\n useEntryGate,\n useList,\n useOptionalT,\n useQuickOpener,\n useRelatedRecord,\n} from '@burojs/react';\nimport { useRowTitle } from '../resource/use-row-title.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { PeekIntent, type PeekTarget } from '../quick/use-peek-intent.js';\nimport { ClaimedQuickLink } from './claimed-quick-link.js';\nimport type { FieldRendererProps } from './registry.js';\n\ntype App = ReturnType<typeof useApp>;\n\n/**\n * Shared link styling for in-table relation/row links. Persistent muted\n * dotted underline at idle that snaps to a solid foreground underline on\n * hover. Color stays at `text-foreground` so the link blends with body\n * text — affordance is carried by the underline alone, avoiding the\n * \"every link in brand color\" noise common in admin tables.\n *\n * Used by `<RelationDisplay>` and the first-cell row-anchor link in\n * `<ResourceList>`.\n *\n * `underline-offset-4` is intentional (vs the more common `-2`) to\n * clear the descenders of non-Latin glyphs seen in our\n * playground's Locations column.\n */\nexport const relationLinkClassName =\n 'rounded text-foreground underline decoration-muted-foreground/40 decoration-dotted underline-offset-4 transition-all hover:decoration-foreground hover:decoration-solid focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring';\n\n/**\n * Render a foreign-key value as a clickable link to the related\n * entity's show page.\n *\n * Resolution order:\n * 1. Read `field.meta.to` to get the target resource name (set by\n * `f.relation({ to: '<name>' })`).\n * 2. Look up the target resource's `displayField` from the app\n * registry — used to resolve the link text.\n * 3. Fetch the target record via `useRelatedRecord(to, id)`.\n * 4. Render `<a href=\"/<to>/<id>\">{displayName}</a>`.\n *\n * Fallbacks (matches the fail-soft policy used by `<EnumBadge>`):\n * - Value null/undefined/empty → em-dash placeholder, no fetch.\n * - Target resource not in registry → raw value as muted plain text.\n * Do NOT fire the fetch — the data provider would 404.\n * - Loading → small skeleton placeholder.\n * - Fetch errored → raw value muted, with the error message as title.\n * - Target row not found (no error, no record) → raw value muted with\n * `title=\"<resource>#<id> not found\"`.\n */\nexport function RelationDisplay({ value, field }: FieldRendererProps): React.ReactElement {\n const app = useApp();\n const meta = field?.meta as\n | { to?: ResourceRefInput; by?: unknown; resolve?: unknown }\n | undefined;\n const by = typeof meta?.by === 'string' ? meta.by : undefined;\n const lazy = meta?.resolve === 'lazy';\n // A target this surface cannot name — a builder field's thunk `to` that no app registration\n // lowered (a declarative form or display), or a malformed ref — renders like no target, as the\n // picker does, instead of throwing during render.\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const id = value as string | number | null | undefined;\n const idStr = String(value ?? '');\n const baseId = `relation-display:${to ?? 'unknown'}:${idStr}`;\n // What a click on the title opens: the overlay (default), the page, or nothing (`ui.open`).\n const open = relationOpenOf({ ui: (field as { readonly ui?: unknown } | undefined)?.ui });\n\n // Empty / null FK — render em-dash, do not fetch.\n // NB: do NOT short-circuit on `value === 0`; the relation schema is\n // `string | number`, and a target row with PK 0 (rare but valid) would\n // otherwise be silently treated as missing.\n if (value == null || value === '') {\n return (\n <span data-testid={`${baseId}:empty`} className=\"text-muted-foreground\">\n —\n </span>\n );\n }\n\n // Target resource not declared on the field. Without a target we\n // cannot build a link — degrade to plain text.\n if (!to) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] no meta.to declared on field — rendering raw value '${idStr}'`,\n );\n }\n return <span data-testid={`relation-display:unknown:${idStr}:no-target`}>{idStr}</span>;\n }\n\n // `lazy` (spec 4.3): nothing registers, nothing is read — the key itself is shown.\n if (lazy) return <LazyRelationDisplay to={to} by={by} idStr={idStr} app={app} open={open} />;\n\n return (\n <GatedRelationDisplay\n to={to}\n by={by}\n id={id as string | number}\n idStr={idStr}\n app={app}\n open={open}\n />\n );\n}\n\n/**\n * Public, adopter-facing entry point for rendering a foreign-key value.\n * Thin convenience wrapper over `<RelationDisplay>`: adopters write\n * `<RelationField to=\"reference_locations\" value={id} />` instead of\n * constructing the field-registry `{ name, meta }` shape by hand.\n * `RelationDisplay` itself stays the field-kind renderer registered for\n * `f.relation(...)` — its `{ value, field }` signature is dictated by the\n * `FieldRendererProps` contract and is unchanged.\n *\n * `RelationDisplay` reads the target resource from `field.meta.to` (see\n * `RelationDisplay` above), so this wrapper just shapes that `meta` object;\n * `field.name` is set to `''` since standalone usage has no owning field.\n */\nexport function RelationField({\n to,\n value,\n}: {\n /** Target resource name (matches `f.relation({ to: '...' })`). */\n to: string;\n /** FK value to resolve, or null/undefined/'' when empty. */\n value: unknown;\n}): React.ReactElement {\n return <RelationDisplay value={value} field={{ name: '', meta: { to } }} />;\n}\n\n/** What a key's peek previews: the row by id, or by the `by` field the key holds. */\nfunction peekTarget(to: string, by: string | undefined, key: string): PeekTarget {\n return by === undefined ? { to, id: key } : { to, by: { field: by, key } };\n}\n\n/** The definition whose record pages a link to `def`'s record lands on (`routesTo.show`). */\nfunction showPageDef(app: App, def: ResourceDef): ResourceDef | undefined {\n const delegate = def.routesTo?.show;\n if (delegate === undefined) return def;\n try {\n return app.registry.get(resourceKey(delegate as ResourceRefInput));\n } catch {\n return undefined;\n }\n}\n\n/** Whether `def`'s record has a show page to link to, following `routesTo.show`. */\nexport function linksToPage(app: App, def: ResourceDef): boolean {\n const target = showPageDef(app, def);\n return target !== undefined && hasRecordPage(target, 'show');\n}\n\n/**\n * A relation display's opener (spec 4.5): the title as an anchor to the page (when there is one)\n * or a button (when there is none), carrying `data-buro-quick` and claimed for the opener, whose\n * plain left click opens `quick.show` instead of navigating. A modified click follows the `href`.\n */\nfunction QuickOpener({\n href,\n testid,\n onOpen,\n children,\n}: {\n readonly href: string | null;\n readonly testid: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n if (href === null)\n return (\n <button\n type=\"button\"\n data-testid={testid}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onOpen();\n }}\n >\n {children}\n </button>\n );\n return (\n <ClaimedQuickLink href={href} testid={testid} className={relationLinkClassName} onOpen={onOpen}>\n {children}\n </ClaimedQuickLink>\n );\n}\n\n/**\n * A lazy relation's key (spec 4.3, 4.5): nothing is read until it is clicked. An id key is the\n * opener of the target's `quick.show`; a `by` key is a button whose click reads the one row the\n * key names (`where <by> eq <key>`, two rows) and opens it — no row renders the not-available\n * text, two rows the muted key with the development error, and neither opens anything.\n */\nfunction LazyRelationDisplay({\n to,\n by,\n idStr,\n app,\n open: openMode,\n}: Omit<BodyProps, 'id'>): React.ReactElement {\n const RouterLink = useLinkComponent();\n const gate = useEntryGate();\n const open = useQuickOpener();\n const targetDef = app.registry.get(to);\n const baseId = `relation-display:${to}:${idStr}`;\n if (!targetDef || openMode === 'none') return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const answer = gate(to, 'show');\n // Undecided: the key as text, nothing read; its peek asks the gate again when it would open.\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <span data-testid={`${baseId}:lazy`}>{idStr}</span>\n </PeekIntent>\n );\n // Denied: the key as text, nothing to open (no read, no anchor).\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const page = linksToPage(app, targetDef);\n // `open: 'page'`: the key is a plain page link whatever the gate says about the overlay.\n if (openMode === 'page') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (answer.state !== 'open') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (by !== undefined)\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <LazyByOpener to={to} by={by} idStr={idStr} target={answer.target} />\n </PeekIntent>\n );\n const openKey = () => void open(to, idStr);\n return (\n <PeekIntent target={peekTarget(to, undefined, idStr)}>\n <QuickOpener\n href={page ? resourceShowHref(app.registry, targetDef, to, idStr) : null}\n testid={`${baseId}:lazy`}\n onOpen={openKey}\n >\n {idStr}\n </QuickOpener>\n </PeekIntent>\n );\n}\n\n/** A lazy `by` key's button: one `where <by> eq <key>` read on click, then the row it names. */\nfunction LazyByOpener({\n to,\n by,\n idStr,\n target,\n}: {\n readonly to: string;\n readonly by: string;\n readonly idStr: string;\n readonly target: string;\n}): React.ReactElement {\n const app = useApp();\n const t = useOptionalT();\n const open = useQuickOpener();\n const [asked, setAsked] = useState(false);\n const read = useList(to, {\n ...(asked\n ? {\n filter: { field: by, op: 'eq', value: idStr } as never,\n pagination: { kind: 'offset', page: 1, pageSize: 2 } as never,\n }\n : {}),\n // A key names one record: never narrowed by a scope. The quick peek reads this same\n // descriptor (`peek-screen.tsx`), so both say `'none'`.\n scope: 'none',\n enabled: asked,\n retry: false,\n });\n const rows = (read.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)\n ?.data;\n const baseId = `relation-display:${to}:${idStr}`;\n const idField = (app.registry.get(target)?.data as { readonly idField?: unknown } | undefined)\n ?.idField;\n const rowId =\n rows?.length === 1\n ? rows[0]?.[typeof idField === 'string' && idField.length > 0 ? idField : 'id']\n : undefined;\n // Only a click opens: a click before the row is read leaves a request here, the effect below\n // answers it once; a refetch that changes the rows later opens nothing.\n const requested = useRef(false);\n useEffect(() => {\n if (!asked || rows === undefined || !requested.current) return;\n if (rows.length > 1) {\n requested.current = false;\n if (process.env.NODE_ENV !== 'production')\n console.error(`[burojs] ${relationAmbiguousError(to, by, idStr).message}`);\n return;\n }\n requested.current = false;\n if (rows.length === 1 && rowId !== undefined && rowId !== null) open(to, String(rowId));\n }, [asked, rows, rowId, open, to, by, idStr]);\n if (asked && rows !== undefined && rows.length === 0)\n return (\n <span data-testid={`${baseId}:unavailable`} className=\"text-muted-foreground\">\n {t('relation.unavailable', { defaultValue: 'Not available' })}\n </span>\n );\n if (asked && rows !== undefined && rows.length > 1)\n return (\n <span data-testid={`${baseId}:ambiguous`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n return (\n <button\n type=\"button\"\n data-testid={`${baseId}:lazy`}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n // A key already read opens its row at once; otherwise the read answers this click.\n if (asked && rowId !== undefined && rowId !== null) open(to, String(rowId));\n else {\n requested.current = true;\n setAsked(true);\n }\n }}\n >\n {idStr}\n </button>\n );\n}\n\ninterface BodyProps {\n to: string;\n /** The target field the key holds (`meta.by`); the target's id field when absent. */\n by?: string;\n id: string | number;\n idStr: string;\n app: App;\n /** What a click on the title opens (`ui.open`). */\n open: RelationOpen;\n}\n\n/**\n * The entry gate decides before anything is read (spec 4.4, 4.14): while it is undecided (the\n * permission map loads) or when `show` is denied, the key renders as plain text and the display\n * registers no read; otherwise the body reads the title and renders the opener — or, when the\n * target has no quick show (`absent`), the page link of before, or text when there is no page.\n */\nfunction GatedRelationDisplay(props: BodyProps): React.ReactElement {\n const gate = useEntryGate();\n const target = props.to;\n const { idStr } = props;\n const answer = gate(target, 'show');\n // Undecided: plain text and no read; its peek (hover) asks the gate again when it would open,\n // and opens nothing until access is decided (spec 4.14).\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(target, props.by, idStr)}>\n <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>\n </PeekIntent>\n );\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>;\n return (\n <RelationDisplayBody\n {...props}\n quickTarget={answer.state === 'open' && props.open === 'quick' ? answer.target : null}\n />\n );\n}\n\n/**\n * Split body so the hook is only called when we know `to` is set and\n * the value is non-falsy. React's rules of hooks require unconditional\n * hook calls per render — separating the body keeps the early-return\n * branches above hook-free.\n */\nfunction RelationDisplayBody({\n to,\n by,\n id,\n idStr,\n app,\n open: openMode,\n quickTarget,\n}: BodyProps & { readonly quickTarget: string | null }): React.ReactElement {\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const baseId = `relation-display:${to}:${idStr}`;\n const RouterLink = useLinkComponent();\n const open = useQuickOpener();\n const { record, isLoading, error, ambiguous } = useRelatedRecord(\n to,\n id,\n by === undefined ? undefined : { by },\n );\n\n // Target resource not in registry — degrade to plain text. No link\n // can be built without a basePath. Same fail-soft as `<EnumBadge>`.\n if (!targetDef) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] target resource '${to}' not registered — rendering raw value '${idStr}'`,\n );\n }\n return (\n <span data-testid={`${baseId}:unregistered`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n }\n\n if (isLoading) {\n return (\n <span\n data-testid={`relation-display-loading:${to}`}\n className=\"inline-block h-5 w-20 animate-pulse rounded bg-muted\"\n aria-busy=\"true\"\n />\n );\n }\n\n // A key two rows hold: the raw key, muted — never either row (spec 4.2, fail closed).\n if (ambiguous) {\n return (\n <span\n data-testid={`${baseId}:ambiguous`}\n className=\"text-muted-foreground\"\n title={`${to}: more than one row has ${by ?? 'id'} ${idStr}`}\n >\n {idStr}\n </span>\n );\n }\n\n if (error) {\n return (\n <span data-testid={`${baseId}:error`} className=\"text-muted-foreground\" title={error.message}>\n {idStr}\n </span>\n );\n }\n\n if (!record) {\n return (\n <span\n data-testid={`${baseId}:not-found`}\n className=\"text-muted-foreground\"\n title={`${to} #${idStr} not found`}\n >\n {idStr}\n </span>\n );\n }\n\n const displayName = rowTitle(record);\n // The link names the resolved row by its id field (spec 4.2), never by the key: a `by` key is\n // not an id, and the target's id field need not be `id`.\n const idField = (targetDef.data as { readonly idField?: unknown } | undefined)?.idField;\n const rowId = record[typeof idField === 'string' && idField.length > 0 ? idField : 'id'];\n if (rowId === null || rowId === undefined || rowId === '')\n return (\n <span data-testid={`${baseId}:no-id`} className=\"text-muted-foreground\">\n {displayName}\n </span>\n );\n const page = linksToPage(app, targetDef);\n const href = page ? resourceShowHref(app.registry, targetDef, to, String(rowId)) : null;\n // `open: 'none'`: the title is text, no anchor and no button.\n if (openMode === 'none') return <span data-testid={baseId}>{displayName}</span>;\n if (quickTarget !== null)\n return (\n <RowOpener to={to} rowId={String(rowId)} href={href} testid={baseId} open={open}>\n {displayName}\n </RowOpener>\n );\n // No quick show (switched off, or no host wired): the page link, or text without a page.\n if (href === null) return <span data-testid={baseId}>{displayName}</span>;\n return (\n <RouterLink\n href={href}\n data-testid={baseId}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {displayName}\n </RouterLink>\n );\n}\n\n/**\n * A resolved row's opener: a click opens its `quick.show`, a hover its peek — by the row's id,\n * the record descriptor `quick.show` reads, so the two share one read.\n */\nfunction RowOpener({\n to,\n rowId,\n href,\n testid,\n open,\n children,\n}: {\n readonly to: string;\n readonly rowId: string;\n readonly href: string | null;\n readonly testid: string;\n readonly open: ReturnType<typeof useQuickOpener>;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const onOpen = useCallback(() => void open(to, rowId), [open, to, rowId]);\n return (\n <PeekIntent target={peekTarget(to, undefined, rowId)}>\n <QuickOpener href={href} testid={testid} onOpen={onOpen}>\n {children}\n </QuickOpener>\n </PeekIntent>\n );\n}\n","import type { ErasedResourceHandle, FilterExpression, SearchDecl } from '@burojs/core';\nimport { filterFields } from '@burojs/react';\n\n/**\n * A declared search set without the fields its user may not view (P5 spec 3.7, decision P5-17).\n * No field left means no search. A function search is kept: its output is checked by\n * {@link namesHiddenField}.\n */\nexport function narrowSearchDecl(\n declared: SearchDecl | undefined,\n visible: (field: string) => boolean,\n): SearchDecl | undefined {\n if (declared === undefined || typeof declared === 'function') return declared;\n if (!Array.isArray(declared.fields)) return declared;\n const fields = declared.fields.filter((ref) =>\n visible(typeof ref === 'string' ? ref : ref.field),\n );\n if (fields.length === declared.fields.length) return declared;\n return fields.length === 0 ? undefined : { ...declared, fields };\n}\n\n/**\n * Whether a composed filter names a field its user may not view: an author predicate (a fixed\n * filter) or a function search's output. Such a read is forbidden, never narrowed — dropping a\n * narrowing predicate would fail open.\n */\nexport function namesHiddenField(\n filter: FilterExpression<ErasedResourceHandle> | undefined,\n visible: (field: string) => boolean,\n): boolean {\n return filter !== undefined && filterFields(filter).some((field) => !visible(field));\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  const require_use_theme = require("./use-theme-DFlpXils.cjs");
2
- const require_toaster = require("./toaster-C7CiYA4Z.cjs");
2
+ const require_toaster = require("./toaster-Cr51X7bz.cjs");
3
3
  const require_dialog = require("./dialog-DRNUq9EA.cjs");
4
4
  let _burojs_core = require("@burojs/core");
5
5
  let _burojs_react = require("@burojs/react");
@@ -472,6 +472,7 @@ function LazyByOpener({ to, by, idStr, target }) {
472
472
  pageSize: 2
473
473
  }
474
474
  } : {},
475
+ scope: "none",
475
476
  enabled: asked,
476
477
  retry: false
477
478
  }).data?.data;
@@ -632,22 +633,6 @@ function RowOpener({ to, rowId, href, testid, open, children }) {
632
633
  })
633
634
  });
634
635
  }
635
- function RelationManyDisplay({ value, field }) {
636
- const keys = Array.isArray(value) ? value : [];
637
- if (keys.length === 0) return (0, react_jsx_runtime.jsx)("span", {
638
- "data-testid": "relation-many-display:empty",
639
- className: "text-muted-foreground",
640
- children: "—"
641
- });
642
- return (0, react_jsx_runtime.jsx)("span", {
643
- className: "inline-flex flex-wrap gap-x-2",
644
- "data-testid": "relation-many-display",
645
- children: keys.map((key, index) => (0, react_jsx_runtime.jsx)(RelationDisplay, {
646
- value: key,
647
- field
648
- }, `${String(key)}:${index}`))
649
- });
650
- }
651
636
  //#endregion
652
637
  //#region src/fields/search-access.ts
653
638
  function narrowSearchDecl(declared, visible) {
@@ -748,12 +733,6 @@ Object.defineProperty(exports, "RelationField", {
748
733
  return RelationField;
749
734
  }
750
735
  });
751
- Object.defineProperty(exports, "RelationManyDisplay", {
752
- enumerable: true,
753
- get: function() {
754
- return RelationManyDisplay;
755
- }
756
- });
757
736
  Object.defineProperty(exports, "focusWithoutPeek", {
758
737
  enumerable: true,
759
738
  get: function() {
@@ -809,4 +788,4 @@ Object.defineProperty(exports, "useRowTitle", {
809
788
  }
810
789
  });
811
790
 
812
- //# sourceMappingURL=search-access-B7G5oR8n.cjs.map
791
+ //# sourceMappingURL=search-access-Dugvttlg.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-access-Dugvttlg.cjs","names":["React","CommandPrimitive","cn","IconSearch","Dialog","DialogContent","DialogTitle","DialogDescription","useLinkComponent","QUICK_ANCHOR_ATTRIBUTE","useListAccess","useCallback","createContext","useContext","useEntryGate","useUIPresentationRuntime","useCurrentUIActivation","useState","useRef","useCallback","useSyncExternalStore","PEEK_COMPONENT","useQuickOpener","useApp","resourceKey","relationOpenOf","hasRecordPage","QUICK_ANCHOR_ATTRIBUTE","useLinkComponent","useEntryGate","useQuickOpener","resourceShowHref","useOptionalT","useState","useList","useRef","relationAmbiguousError","useRelatedRecord","useCallback","filterFields"],"sources":["../src/primitives/command.tsx","../src/fields/claimed-quick-link.tsx","../src/resource/record-display-name.ts","../src/resource/use-row-title.ts","../src/quick/peek-context.ts","../src/quick/use-peek-intent.tsx","../src/fields/relation-display.tsx","../src/fields/search-access.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport { IconSearch } from '@tabler/icons-react';\nimport { cn } from '../lib/cn.js';\nimport {\n Dialog,\n DialogContent,\n DialogTitle,\n DialogDescription,\n type DialogContentProps,\n} from './dialog.js';\n\n/**\n * Shadcn-canonical Command primitive built on `cmdk`. Provides\n * keyboard-navigable, ARIA-correct combobox/listbox semantics: arrow\n * keys to navigate items, Enter to select, type-ahead search via the\n * embedded `<CommandInput>`. Use this for any searchable list inside a\n * Popover (typeaheads, command palettes, relation pickers).\n *\n * Filter behavior: by default cmdk filters items client-side based on\n * the input text. For server-side filtering (typical for relation\n * pickers that hit `useList` with a `contains` operator), pass\n * `shouldFilter={false}` to the root `<Command>` and drive the option\n * list yourself from the server-side results.\n */\nconst Command = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn(\n 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',\n className,\n )}\n {...props}\n />\n));\nCommand.displayName = CommandPrimitive.displayName;\n\nconst CommandInput = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <IconSearch className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" aria-hidden=\"true\" />\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled',\n className,\n )}\n {...props}\n />\n </div>\n));\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n));\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n <CommandPrimitive.Empty\n ref={ref}\n className=\"py-6 text-center text-sm text-muted-foreground\"\n {...props}\n />\n));\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n 'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 h-px bg-border', className)}\n {...props}\n />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n \"group relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nexport type CommandLoadingProps = React.ComponentPropsWithoutRef<typeof CommandPrimitive.Loading>;\n\nconst CommandLoading = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Loading>,\n CommandLoadingProps\n>(({ ...props }, ref) => <CommandPrimitive.Loading ref={ref} {...props} />);\nCommandLoading.displayName = CommandPrimitive.Loading.displayName;\n\n/**\n * Props for `<CommandDialog>`. Mirrors Radix Dialog's `Root` props\n * (`open`, `onOpenChange`, `defaultOpen`, `modal`) plus optional\n * accessibility labels and dialog-content className overrides.\n *\n * The shadcn-canonical pattern wraps a Radix `<Dialog>` around the\n * `<Command>` root so cmdk's keyboard navigation (arrow keys, Enter,\n * type-ahead) operates on the items rendered as children. We render\n * a visually-hidden `<DialogTitle>` + `<DialogDescription>` for the\n * a11y tree (Radix requires a labelled dialog or it warns at runtime).\n */\nexport interface CommandDialogProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n modal?: boolean;\n /** Accessible title (visually hidden). Defaults to \"Command Palette\". */\n title?: string;\n /** Accessible description (visually hidden). Defaults to \"Search and run actions.\" */\n description?: string;\n /** Forwarded to the `<DialogContent>` className. */\n className?: string;\n /** Forwarded to the inner `<Command>` className. */\n commandClassName?: string;\n /** The Command groups + items + input. */\n children?: React.ReactNode;\n /** Optional dialog-content overrides (e.g. style for animations). */\n contentProps?: Omit<DialogContentProps, 'children' | 'className'>;\n}\n\n/**\n * Modal command palette. Combines Radix Dialog with cmdk's `<Command>`\n * root so adopters can compose `<CommandInput>`, `<CommandList>`,\n * `<CommandGroup>`, `<CommandItem>` as children and get focus-trapped\n * keyboard navigation out of the box.\n *\n * Usage:\n * ```tsx\n * <CommandDialog open={open} onOpenChange={setOpen}>\n * <CommandInput placeholder=\"Search…\" />\n * <CommandList>\n * <CommandEmpty>No matches.</CommandEmpty>\n * <CommandGroup heading=\"Resources\">\n * <CommandItem onSelect={...}>Documents</CommandItem>\n * </CommandGroup>\n * </CommandList>\n * </CommandDialog>\n * ```\n */\nfunction CommandDialog({\n open,\n onOpenChange,\n defaultOpen,\n modal,\n title = 'Command Palette',\n description = 'Search and run actions.',\n className,\n commandClassName,\n children,\n contentProps,\n}: CommandDialogProps) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={defaultOpen} modal={modal}>\n <DialogContent\n {...contentProps}\n // Width: cap to 480px regardless of breakpoint (the shadcn default\n // `DialogContent` uses `max-w-lg` ~512px). `sm:max-w-[480px]`\n // re-asserts at the `sm` breakpoint so any built-in `sm:max-w-lg`\n // override loses to ours.\n className={cn('overflow-hidden p-0 shadow-lg max-w-[480px] sm:max-w-[480px]', className)}\n // Backdrop blur for the command palette specifically: softens\n // the underlying chrome so the palette feels like a focused\n // overlay rather than a stacked panel. Other dialogs keep the\n // shadcn-default solid backdrop.\n overlayClassName=\"backdrop-blur-sm\"\n >\n {/* Visually-hidden labels: Radix Dialog needs them for a11y, but\n the palette UI itself supplies its own search affordance. */}\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <DialogDescription className=\"sr-only\">{description}</DialogDescription>\n <Command\n className={cn(\n '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5',\n commandClassName,\n )}\n >\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\nCommandDialog.displayName = 'CommandDialog';\n\nexport {\n Command,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandSeparator,\n CommandLoading,\n CommandDialog,\n};\n","'use client';\nimport { QUICK_ANCHOR_ATTRIBUTE, quickAnchorRef } from '@burojs/react';\nimport type * as React from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\n\n/** A plain left click: the main button, no modifier. Anything else follows the `href`. */\nexport function isPlainLeftClick(event: React.MouseEvent): boolean {\n return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;\n}\n\n/** Claims the anchor an app's link component rendered (it may not forward a ref). */\nfunction claimAnchor(wrapper: HTMLSpanElement | null): void {\n const anchor = wrapper?.querySelector('a') ?? null;\n quickAnchorRef(anchor);\n}\n\n/**\n * A quick opener's anchor (spec 4.5): the app's link component to the record's page, marked with\n * `data-buro-quick` and claimed for its opener, so the navigation guard leaves its click to it. A\n * plain left click calls `preventDefault()` and `onOpen()`; a modified click follows the `href`\n * (a new tab, guarded as any link).\n */\nexport function ClaimedQuickLink({\n href,\n testid,\n className,\n onOpen,\n children,\n}: {\n readonly href: string;\n readonly testid?: string;\n readonly className?: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const RouterLink = useLinkComponent();\n return (\n <span ref={claimAnchor} className=\"contents\">\n <RouterLink\n href={href}\n {...(testid === undefined ? {} : { 'data-testid': testid })}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n {...(className === undefined ? {} : { className })}\n onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {\n event.stopPropagation();\n if (!isPlainLeftClick(event) || event.currentTarget.target === '_blank') return;\n event.preventDefault();\n onOpen();\n }}\n >\n {children}\n </RouterLink>\n </span>\n );\n}\n","/**\n * A record's display name: its `displayField` value when present and non-empty, else its `id`.\n * Exported for consumers building delete dialogs, breadcrumbs and relation labels.\n */\nexport function recordDisplayName(record: Record<string, unknown>, displayField?: string): string {\n if (displayField && record[displayField] != null && record[displayField] !== '') {\n return String(record[displayField]);\n }\n return String(record.id ?? '');\n}\n","import { useListAccess } from '@burojs/react';\nimport { useCallback } from 'react';\nimport { recordDisplayName } from './record-display-name.js';\n\n/**\n * The display name of a row of `target` as a list surface may show it (P5 spec 3.6, decision\n * P5-27): the row's `displayField` value only while that field is visible in any-record reach,\n * else the id. While the decision is pending the id is shown as well, never the value. Picker\n * options, relation displays, related labels and palette results read their titles through this.\n */\nexport function useRowTitle(\n target: string,\n displayField: string | undefined,\n): (row: Record<string, unknown>) => string {\n const access = useListAccess(target);\n const visible =\n displayField !== undefined && access.status === 'ready' && access.fieldVisible(displayField);\n return useCallback(\n (row) => recordDisplayName(row, visible ? displayField : undefined),\n [visible, displayField],\n );\n}\n","'use client';\nimport { createContext } from 'react';\n\n/**\n * @internal What the popover host gives the screen inside it: the anchor element it is positioned\n * against (the relation display the peek was opened from), and a setter for the popover's\n * accessible name once the screen knows the record's title.\n */\nexport interface PeekHostValue {\n readonly anchor: HTMLElement | null;\n setLabel(label: string): void;\n}\n\n/** @internal Provided by `PopoverPresentationHost`; `null` outside a popover. */\nexport const PeekHostContext = createContext<PeekHostValue | null>(null);\n\n/**\n * @internal True inside a peek (provided by `PeekScreen`): a relation display there attaches no\n * hover intent, so a peek never opens a second peek (spec 4.6).\n */\nexport const PeekContext = createContext(false);\n","'use client';\nimport {\n PEEK_COMPONENT,\n type PeekScreenProps,\n useCurrentUIActivation,\n useEntryGate,\n useQuickOpener,\n useUIPresentationRuntime,\n} from '@burojs/react';\nimport {\n type FocusEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { PeekContext } from './peek-context.js';\n\n/** The hover (and keyboard-focus) delay before a peek opens (spec 4.6). */\nexport const PEEK_DELAY_MS = 500;\n/** Focus this soon after a Tab or arrow keydown is keyboard modality. */\nconst KEYBOARD_FOCUS_WINDOW_MS = 100;\n\nconst NAVIGATION_KEYS = new Set(['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);\n\n/** When the last Tab or arrow keydown happened; a pointer down forgets it. */\nlet lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\nconst listening = new WeakSet<Document>();\n\nfunction listenForModality(doc: Document): void {\n if (listening.has(doc)) return;\n listening.add(doc);\n doc.addEventListener(\n 'keydown',\n (event) => {\n if (NAVIGATION_KEYS.has(event.key)) lastNavigationKeyAt = Date.now();\n },\n true,\n );\n const forget = () => {\n lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\n };\n doc.addEventListener('pointerdown', forget, true);\n doc.addEventListener('mousedown', forget, true);\n}\n\n/** Whether a focus now came from the keyboard (a Tab or arrow keydown just before it). */\nfunction keyboardModality(): boolean {\n return Date.now() - lastNavigationKeyAt <= KEYBOARD_FOCUS_WINDOW_MS;\n}\n\n/** Elements about to be focused by code: their next focus opens no peek. */\nconst focusedByCode = new WeakSet<Element>();\n\n/**\n * @internal Focus `element` by code (the peek's anchor, when its title opens `quick.show`): the\n * focus opens no peek, whatever key was pressed last.\n */\nexport function focusWithoutPeek(element: HTMLElement): void {\n focusedByCode.add(element);\n element.focus();\n focusedByCode.delete(element);\n}\n\n/** What opens a record from where an anchor sits: its display's own quick opener. */\nexport type PeekAnchorOpener = (target: string, id: string) => void;\n\n/**\n * Each anchor's opener, taken where the display renders: inside a quick sheet it pushes into that\n * sheet, inside a dialog it closes the dialog first. The peek renders beside the overlays, outside\n * the sheet's context, so its title opens through this instead of its own opener.\n */\nconst anchorOpeners = new WeakMap<Element, PeekAnchorOpener>();\n\n/** @internal The opener registered for `anchor` by its peek intent, if any. */\nexport function peekAnchorOpener(anchor: Element | null): PeekAnchorOpener | undefined {\n return anchor === null ? undefined : anchorOpeners.get(anchor);\n}\n\n/** What a peek previews: the display's target and the key it holds (an id, or a `by` key). */\nexport interface PeekTarget {\n readonly to: string;\n readonly id?: string;\n readonly by?: { readonly field: string; readonly key: string };\n}\n\n/** The anchor inside the wrapper: the opener (`data-buro-quick`), else the first element. */\nfunction anchorIn(wrapper: HTMLElement | null): HTMLElement | null {\n if (wrapper === null) return null;\n const marked = wrapper.querySelector<HTMLElement>('[data-buro-quick]');\n if (marked !== null) return marked;\n const first = wrapper.firstElementChild;\n return first instanceof HTMLElement ? first : null;\n}\n\nconst noSubscription = () => () => undefined;\n\n/**\n * A relation display's hover intent (spec 4.6): a `mouse` or `pen` pointer resting on the anchor,\n * or keyboard-modality focus on it (a Tab or arrow keydown just before, never focus set by code),\n * for {@link PEEK_DELAY_MS} asks the entry gate for the target's peek; only `open` presents it,\n * after dismissing any other open peek. Leaving or blurring before then cancels. Nothing is read\n * before the peek opens. Inside a peek it renders its children only.\n */\nexport function PeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const inside = useContext(PeekContext);\n if (inside) return <>{children}</>;\n return <ActivePeekIntent target={target}>{children}</ActivePeekIntent>;\n}\n\nfunction ActivePeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const gate = useEntryGate();\n const runtime = useUIPresentationRuntime();\n const activation = useCurrentUIActivation();\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const wrapper = useRef<HTMLSpanElement | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The latest of each, read when the timer fires (the access answer may have changed since).\n const latest = useRef({ gate, target, activation });\n latest.current = { gate, target, activation };\n\n const attach = useCallback((element: HTMLSpanElement | null) => {\n wrapper.current = element;\n if (element !== null && element.ownerDocument !== undefined)\n listenForModality(element.ownerDocument);\n setAnchor(anchorIn(element));\n }, []);\n\n const expanded = useSyncExternalStore(\n anchor === null ? noSubscription : runtime.subscribe,\n () =>\n anchor !== null &&\n runtime\n .getSnapshot()\n .some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === anchor),\n () => false,\n );\n const peekable = gate(target.to, 'peek').state === 'open';\n\n // The anchor is the app's link (it may not forward props): its popup state is set on it.\n useEffect(() => {\n if (anchor === null) return;\n if (!peekable) {\n anchor.removeAttribute('aria-haspopup');\n anchor.removeAttribute('aria-expanded');\n return;\n }\n anchor.setAttribute('aria-haspopup', 'dialog');\n anchor.setAttribute('aria-expanded', expanded ? 'true' : 'false');\n }, [anchor, peekable, expanded]);\n\n // The opener of where this display sits (a sheet pushes, a dialog closes first), for every\n // display with a peek: an eager one, a lazy `by` one and one under undecided access alike.\n const quickOpen = useQuickOpener();\n const onOpen = useCallback<PeekAnchorOpener>((to, id) => void quickOpen(to, id), [quickOpen]);\n useEffect(() => {\n if (anchor === null) return;\n anchorOpeners.set(anchor, onOpen);\n return () => {\n if (anchorOpeners.get(anchor) === onOpen) anchorOpeners.delete(anchor);\n };\n }, [anchor, onOpen]);\n\n const cancel = useCallback(() => {\n if (timer.current === null) return;\n clearTimeout(timer.current);\n timer.current = null;\n }, []);\n useEffect(() => cancel, [cancel]);\n\n const fire = useCallback(() => {\n timer.current = null;\n const { gate: ask, target: what, activation: from } = latest.current;\n const element = anchorIn(wrapper.current);\n if (element === null || !element.isConnected) return;\n // Only `open` presents: undecided access reads nothing (spec 4.14), a refusal opens nothing.\n const answer = ask(what.to, 'peek');\n if (answer.state !== 'open') return;\n const snapshot = runtime.getSnapshot();\n if (snapshot.some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === element))\n return;\n // One peek at a time.\n for (const descriptor of snapshot)\n if (descriptor.component === PEEK_COMPONENT) runtime.dismiss(descriptor.id);\n const props: PeekScreenProps = {\n target: answer.target,\n ...(what.id === undefined ? {} : { id: what.id }),\n ...(what.by === undefined ? {} : { by: what.by }),\n };\n runtime.present({\n component: PEEK_COMPONENT,\n host: 'popover',\n anchor: { element },\n props: props as unknown as Readonly<Record<string, unknown>>,\n activation: from,\n });\n }, [runtime]);\n\n const schedule = useCallback(() => {\n if (timer.current !== null) return;\n timer.current = setTimeout(fire, PEEK_DELAY_MS);\n }, [fire]);\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: a `display: contents` wrapper that only observes the anchor inside it; the anchor is the interactive element\n <span\n ref={attach}\n className=\"contents\"\n onPointerEnter={(event: PointerEvent) => {\n if (event.pointerType === 'mouse' || event.pointerType === 'pen') schedule();\n }}\n onPointerLeave={cancel}\n onFocus={(event: FocusEvent) => {\n if (focusedByCode.has(event.target)) return;\n if (keyboardModality()) schedule();\n }}\n onBlur={cancel}\n // A click is always quick.show (spec 4.6): it cancels a pending peek.\n onPointerDown={cancel}\n onClickCapture={cancel}\n >\n {children}\n </span>\n );\n}\n","'use client';\nimport type * as React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n hasRecordPage,\n relationAmbiguousError,\n resourceKey,\n relationOpenOf,\n type RelationOpen,\n resourceShowHref,\n type ResourceDef,\n type ResourceRefInput,\n} from '@burojs/core';\nimport {\n QUICK_ANCHOR_ATTRIBUTE,\n useApp,\n useEntryGate,\n useList,\n useOptionalT,\n useQuickOpener,\n useRelatedRecord,\n} from '@burojs/react';\nimport { useRowTitle } from '../resource/use-row-title.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { PeekIntent, type PeekTarget } from '../quick/use-peek-intent.js';\nimport { ClaimedQuickLink } from './claimed-quick-link.js';\nimport type { FieldRendererProps } from './registry.js';\n\ntype App = ReturnType<typeof useApp>;\n\n/**\n * Shared link styling for in-table relation/row links. Persistent muted\n * dotted underline at idle that snaps to a solid foreground underline on\n * hover. Color stays at `text-foreground` so the link blends with body\n * text — affordance is carried by the underline alone, avoiding the\n * \"every link in brand color\" noise common in admin tables.\n *\n * Used by `<RelationDisplay>` and the first-cell row-anchor link in\n * `<ResourceList>`.\n *\n * `underline-offset-4` is intentional (vs the more common `-2`) to\n * clear the descenders of non-Latin glyphs seen in our\n * playground's Locations column.\n */\nexport const relationLinkClassName =\n 'rounded text-foreground underline decoration-muted-foreground/40 decoration-dotted underline-offset-4 transition-all hover:decoration-foreground hover:decoration-solid focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring';\n\n/**\n * Render a foreign-key value as a clickable link to the related\n * entity's show page.\n *\n * Resolution order:\n * 1. Read `field.meta.to` to get the target resource name (set by\n * `f.relation({ to: '<name>' })`).\n * 2. Look up the target resource's `displayField` from the app\n * registry — used to resolve the link text.\n * 3. Fetch the target record via `useRelatedRecord(to, id)`.\n * 4. Render `<a href=\"/<to>/<id>\">{displayName}</a>`.\n *\n * Fallbacks (matches the fail-soft policy used by `<EnumBadge>`):\n * - Value null/undefined/empty → em-dash placeholder, no fetch.\n * - Target resource not in registry → raw value as muted plain text.\n * Do NOT fire the fetch — the data provider would 404.\n * - Loading → small skeleton placeholder.\n * - Fetch errored → raw value muted, with the error message as title.\n * - Target row not found (no error, no record) → raw value muted with\n * `title=\"<resource>#<id> not found\"`.\n */\nexport function RelationDisplay({ value, field }: FieldRendererProps): React.ReactElement {\n const app = useApp();\n const meta = field?.meta as\n | { to?: ResourceRefInput; by?: unknown; resolve?: unknown }\n | undefined;\n const by = typeof meta?.by === 'string' ? meta.by : undefined;\n const lazy = meta?.resolve === 'lazy';\n // A target this surface cannot name — a builder field's thunk `to` that no app registration\n // lowered (a declarative form or display), or a malformed ref — renders like no target, as the\n // picker does, instead of throwing during render.\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const id = value as string | number | null | undefined;\n const idStr = String(value ?? '');\n const baseId = `relation-display:${to ?? 'unknown'}:${idStr}`;\n // What a click on the title opens: the overlay (default), the page, or nothing (`ui.open`).\n const open = relationOpenOf({ ui: (field as { readonly ui?: unknown } | undefined)?.ui });\n\n // Empty / null FK — render em-dash, do not fetch.\n // NB: do NOT short-circuit on `value === 0`; the relation schema is\n // `string | number`, and a target row with PK 0 (rare but valid) would\n // otherwise be silently treated as missing.\n if (value == null || value === '') {\n return (\n <span data-testid={`${baseId}:empty`} className=\"text-muted-foreground\">\n —\n </span>\n );\n }\n\n // Target resource not declared on the field. Without a target we\n // cannot build a link — degrade to plain text.\n if (!to) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] no meta.to declared on field — rendering raw value '${idStr}'`,\n );\n }\n return <span data-testid={`relation-display:unknown:${idStr}:no-target`}>{idStr}</span>;\n }\n\n // `lazy` (spec 4.3): nothing registers, nothing is read — the key itself is shown.\n if (lazy) return <LazyRelationDisplay to={to} by={by} idStr={idStr} app={app} open={open} />;\n\n return (\n <GatedRelationDisplay\n to={to}\n by={by}\n id={id as string | number}\n idStr={idStr}\n app={app}\n open={open}\n />\n );\n}\n\n/**\n * Public, adopter-facing entry point for rendering a foreign-key value.\n * Thin convenience wrapper over `<RelationDisplay>`: adopters write\n * `<RelationField to=\"reference_locations\" value={id} />` instead of\n * constructing the field-registry `{ name, meta }` shape by hand.\n * `RelationDisplay` itself stays the field-kind renderer registered for\n * `f.relation(...)` — its `{ value, field }` signature is dictated by the\n * `FieldRendererProps` contract and is unchanged.\n *\n * `RelationDisplay` reads the target resource from `field.meta.to` (see\n * `RelationDisplay` above), so this wrapper just shapes that `meta` object;\n * `field.name` is set to `''` since standalone usage has no owning field.\n */\nexport function RelationField({\n to,\n value,\n}: {\n /** Target resource name (matches `f.relation({ to: '...' })`). */\n to: string;\n /** FK value to resolve, or null/undefined/'' when empty. */\n value: unknown;\n}): React.ReactElement {\n return <RelationDisplay value={value} field={{ name: '', meta: { to } }} />;\n}\n\n/** What a key's peek previews: the row by id, or by the `by` field the key holds. */\nfunction peekTarget(to: string, by: string | undefined, key: string): PeekTarget {\n return by === undefined ? { to, id: key } : { to, by: { field: by, key } };\n}\n\n/** The definition whose record pages a link to `def`'s record lands on (`routesTo.show`). */\nfunction showPageDef(app: App, def: ResourceDef): ResourceDef | undefined {\n const delegate = def.routesTo?.show;\n if (delegate === undefined) return def;\n try {\n return app.registry.get(resourceKey(delegate as ResourceRefInput));\n } catch {\n return undefined;\n }\n}\n\n/** Whether `def`'s record has a show page to link to, following `routesTo.show`. */\nexport function linksToPage(app: App, def: ResourceDef): boolean {\n const target = showPageDef(app, def);\n return target !== undefined && hasRecordPage(target, 'show');\n}\n\n/**\n * A relation display's opener (spec 4.5): the title as an anchor to the page (when there is one)\n * or a button (when there is none), carrying `data-buro-quick` and claimed for the opener, whose\n * plain left click opens `quick.show` instead of navigating. A modified click follows the `href`.\n */\nfunction QuickOpener({\n href,\n testid,\n onOpen,\n children,\n}: {\n readonly href: string | null;\n readonly testid: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n if (href === null)\n return (\n <button\n type=\"button\"\n data-testid={testid}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onOpen();\n }}\n >\n {children}\n </button>\n );\n return (\n <ClaimedQuickLink href={href} testid={testid} className={relationLinkClassName} onOpen={onOpen}>\n {children}\n </ClaimedQuickLink>\n );\n}\n\n/**\n * A lazy relation's key (spec 4.3, 4.5): nothing is read until it is clicked. An id key is the\n * opener of the target's `quick.show`; a `by` key is a button whose click reads the one row the\n * key names (`where <by> eq <key>`, two rows) and opens it — no row renders the not-available\n * text, two rows the muted key with the development error, and neither opens anything.\n */\nfunction LazyRelationDisplay({\n to,\n by,\n idStr,\n app,\n open: openMode,\n}: Omit<BodyProps, 'id'>): React.ReactElement {\n const RouterLink = useLinkComponent();\n const gate = useEntryGate();\n const open = useQuickOpener();\n const targetDef = app.registry.get(to);\n const baseId = `relation-display:${to}:${idStr}`;\n if (!targetDef || openMode === 'none') return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const answer = gate(to, 'show');\n // Undecided: the key as text, nothing read; its peek asks the gate again when it would open.\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <span data-testid={`${baseId}:lazy`}>{idStr}</span>\n </PeekIntent>\n );\n // Denied: the key as text, nothing to open (no read, no anchor).\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const page = linksToPage(app, targetDef);\n // `open: 'page'`: the key is a plain page link whatever the gate says about the overlay.\n if (openMode === 'page') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (answer.state !== 'open') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (by !== undefined)\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <LazyByOpener to={to} by={by} idStr={idStr} target={answer.target} />\n </PeekIntent>\n );\n const openKey = () => void open(to, idStr);\n return (\n <PeekIntent target={peekTarget(to, undefined, idStr)}>\n <QuickOpener\n href={page ? resourceShowHref(app.registry, targetDef, to, idStr) : null}\n testid={`${baseId}:lazy`}\n onOpen={openKey}\n >\n {idStr}\n </QuickOpener>\n </PeekIntent>\n );\n}\n\n/** A lazy `by` key's button: one `where <by> eq <key>` read on click, then the row it names. */\nfunction LazyByOpener({\n to,\n by,\n idStr,\n target,\n}: {\n readonly to: string;\n readonly by: string;\n readonly idStr: string;\n readonly target: string;\n}): React.ReactElement {\n const app = useApp();\n const t = useOptionalT();\n const open = useQuickOpener();\n const [asked, setAsked] = useState(false);\n const read = useList(to, {\n ...(asked\n ? {\n filter: { field: by, op: 'eq', value: idStr } as never,\n pagination: { kind: 'offset', page: 1, pageSize: 2 } as never,\n }\n : {}),\n // A key names one record: never narrowed by a scope. The quick peek reads this same\n // descriptor (`peek-screen.tsx`), so both say `'none'`.\n scope: 'none',\n enabled: asked,\n retry: false,\n });\n const rows = (read.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)\n ?.data;\n const baseId = `relation-display:${to}:${idStr}`;\n const idField = (app.registry.get(target)?.data as { readonly idField?: unknown } | undefined)\n ?.idField;\n const rowId =\n rows?.length === 1\n ? rows[0]?.[typeof idField === 'string' && idField.length > 0 ? idField : 'id']\n : undefined;\n // Only a click opens: a click before the row is read leaves a request here, the effect below\n // answers it once; a refetch that changes the rows later opens nothing.\n const requested = useRef(false);\n useEffect(() => {\n if (!asked || rows === undefined || !requested.current) return;\n if (rows.length > 1) {\n requested.current = false;\n if (process.env.NODE_ENV !== 'production')\n console.error(`[burojs] ${relationAmbiguousError(to, by, idStr).message}`);\n return;\n }\n requested.current = false;\n if (rows.length === 1 && rowId !== undefined && rowId !== null) open(to, String(rowId));\n }, [asked, rows, rowId, open, to, by, idStr]);\n if (asked && rows !== undefined && rows.length === 0)\n return (\n <span data-testid={`${baseId}:unavailable`} className=\"text-muted-foreground\">\n {t('relation.unavailable', { defaultValue: 'Not available' })}\n </span>\n );\n if (asked && rows !== undefined && rows.length > 1)\n return (\n <span data-testid={`${baseId}:ambiguous`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n return (\n <button\n type=\"button\"\n data-testid={`${baseId}:lazy`}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n // A key already read opens its row at once; otherwise the read answers this click.\n if (asked && rowId !== undefined && rowId !== null) open(to, String(rowId));\n else {\n requested.current = true;\n setAsked(true);\n }\n }}\n >\n {idStr}\n </button>\n );\n}\n\ninterface BodyProps {\n to: string;\n /** The target field the key holds (`meta.by`); the target's id field when absent. */\n by?: string;\n id: string | number;\n idStr: string;\n app: App;\n /** What a click on the title opens (`ui.open`). */\n open: RelationOpen;\n}\n\n/**\n * The entry gate decides before anything is read (spec 4.4, 4.14): while it is undecided (the\n * permission map loads) or when `show` is denied, the key renders as plain text and the display\n * registers no read; otherwise the body reads the title and renders the opener — or, when the\n * target has no quick show (`absent`), the page link of before, or text when there is no page.\n */\nfunction GatedRelationDisplay(props: BodyProps): React.ReactElement {\n const gate = useEntryGate();\n const target = props.to;\n const { idStr } = props;\n const answer = gate(target, 'show');\n // Undecided: plain text and no read; its peek (hover) asks the gate again when it would open,\n // and opens nothing until access is decided (spec 4.14).\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(target, props.by, idStr)}>\n <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>\n </PeekIntent>\n );\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>;\n return (\n <RelationDisplayBody\n {...props}\n quickTarget={answer.state === 'open' && props.open === 'quick' ? answer.target : null}\n />\n );\n}\n\n/**\n * Split body so the hook is only called when we know `to` is set and\n * the value is non-falsy. React's rules of hooks require unconditional\n * hook calls per render — separating the body keeps the early-return\n * branches above hook-free.\n */\nfunction RelationDisplayBody({\n to,\n by,\n id,\n idStr,\n app,\n open: openMode,\n quickTarget,\n}: BodyProps & { readonly quickTarget: string | null }): React.ReactElement {\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const baseId = `relation-display:${to}:${idStr}`;\n const RouterLink = useLinkComponent();\n const open = useQuickOpener();\n const { record, isLoading, error, ambiguous } = useRelatedRecord(\n to,\n id,\n by === undefined ? undefined : { by },\n );\n\n // Target resource not in registry — degrade to plain text. No link\n // can be built without a basePath. Same fail-soft as `<EnumBadge>`.\n if (!targetDef) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] target resource '${to}' not registered — rendering raw value '${idStr}'`,\n );\n }\n return (\n <span data-testid={`${baseId}:unregistered`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n }\n\n if (isLoading) {\n return (\n <span\n data-testid={`relation-display-loading:${to}`}\n className=\"inline-block h-5 w-20 animate-pulse rounded bg-muted\"\n aria-busy=\"true\"\n />\n );\n }\n\n // A key two rows hold: the raw key, muted — never either row (spec 4.2, fail closed).\n if (ambiguous) {\n return (\n <span\n data-testid={`${baseId}:ambiguous`}\n className=\"text-muted-foreground\"\n title={`${to}: more than one row has ${by ?? 'id'} ${idStr}`}\n >\n {idStr}\n </span>\n );\n }\n\n if (error) {\n return (\n <span data-testid={`${baseId}:error`} className=\"text-muted-foreground\" title={error.message}>\n {idStr}\n </span>\n );\n }\n\n if (!record) {\n return (\n <span\n data-testid={`${baseId}:not-found`}\n className=\"text-muted-foreground\"\n title={`${to} #${idStr} not found`}\n >\n {idStr}\n </span>\n );\n }\n\n const displayName = rowTitle(record);\n // The link names the resolved row by its id field (spec 4.2), never by the key: a `by` key is\n // not an id, and the target's id field need not be `id`.\n const idField = (targetDef.data as { readonly idField?: unknown } | undefined)?.idField;\n const rowId = record[typeof idField === 'string' && idField.length > 0 ? idField : 'id'];\n if (rowId === null || rowId === undefined || rowId === '')\n return (\n <span data-testid={`${baseId}:no-id`} className=\"text-muted-foreground\">\n {displayName}\n </span>\n );\n const page = linksToPage(app, targetDef);\n const href = page ? resourceShowHref(app.registry, targetDef, to, String(rowId)) : null;\n // `open: 'none'`: the title is text, no anchor and no button.\n if (openMode === 'none') return <span data-testid={baseId}>{displayName}</span>;\n if (quickTarget !== null)\n return (\n <RowOpener to={to} rowId={String(rowId)} href={href} testid={baseId} open={open}>\n {displayName}\n </RowOpener>\n );\n // No quick show (switched off, or no host wired): the page link, or text without a page.\n if (href === null) return <span data-testid={baseId}>{displayName}</span>;\n return (\n <RouterLink\n href={href}\n data-testid={baseId}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {displayName}\n </RouterLink>\n );\n}\n\n/**\n * A resolved row's opener: a click opens its `quick.show`, a hover its peek — by the row's id,\n * the record descriptor `quick.show` reads, so the two share one read.\n */\nfunction RowOpener({\n to,\n rowId,\n href,\n testid,\n open,\n children,\n}: {\n readonly to: string;\n readonly rowId: string;\n readonly href: string | null;\n readonly testid: string;\n readonly open: ReturnType<typeof useQuickOpener>;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const onOpen = useCallback(() => void open(to, rowId), [open, to, rowId]);\n return (\n <PeekIntent target={peekTarget(to, undefined, rowId)}>\n <QuickOpener href={href} testid={testid} onOpen={onOpen}>\n {children}\n </QuickOpener>\n </PeekIntent>\n );\n}\n","import type { ErasedResourceHandle, FilterExpression, SearchDecl } from '@burojs/core';\nimport { filterFields } from '@burojs/react';\n\n/**\n * A declared search set without the fields its user may not view (P5 spec 3.7, decision P5-17).\n * No field left means no search. A function search is kept: its output is checked by\n * {@link namesHiddenField}.\n */\nexport function narrowSearchDecl(\n declared: SearchDecl | undefined,\n visible: (field: string) => boolean,\n): SearchDecl | undefined {\n if (declared === undefined || typeof declared === 'function') return declared;\n if (!Array.isArray(declared.fields)) return declared;\n const fields = declared.fields.filter((ref) =>\n visible(typeof ref === 'string' ? ref : ref.field),\n );\n if (fields.length === declared.fields.length) return declared;\n return fields.length === 0 ? undefined : { ...declared, fields };\n}\n\n/**\n * Whether a composed filter names a field its user may not view: an author predicate (a fixed\n * filter) or a function search's output. Such a read is forbidden, never narrowed — dropping a\n * narrowing predicate would fail open.\n */\nexport function namesHiddenField(\n filter: FilterExpression<ErasedResourceHandle> | undefined,\n visible: (field: string) => boolean,\n): boolean {\n return filter !== undefined && filterFields(filter).some((field) => !visible(field));\n}\n"],"mappings":";;;;;;;;;;;AA2BA,MAAM,UAAUA,MAAM,YAGnB,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,IAAA,CAACC,KAAAA,SAAD;CACO;CACL,WAAWC,kBAAAA,GACT,6FACA,SACF;CACA,GAAI;AACL,CAAA,CACF;AACD,QAAQ,cAAcD,KAAAA,QAAiB;AAEvC,MAAM,eAAeD,MAAM,YAGxB,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,KAAA,CAAC,OAAD;CAAK,WAAU;CAAkC,sBAAmB;CAApE,UAAA,EACE,GAAA,kBAAA,IAAA,CAACG,oBAAAA,YAAD;EAAY,WAAU;EAAmC,eAAY;CAAQ,CAAA,IAC7E,GAAA,kBAAA,IAAA,CAACF,KAAAA,QAAiB,OAAlB;EACO;EACL,WAAWC,kBAAAA,GACT,uLACA,SACF;EACA,GAAI;CACL,CAAA,CACE;AACN,CAAA,CAAA;AACD,aAAa,cAAcD,KAAAA,QAAiB,MAAM;AAElD,MAAM,cAAcD,MAAM,YAGvB,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,MAAlB;CACO;CACL,WAAWC,kBAAAA,GAAG,mDAAmD,SAAS;CAC1E,GAAI;AACL,CAAA,CACF;AACD,YAAY,cAAcD,KAAAA,QAAiB,KAAK;AAEhD,MAAM,eAAeD,MAAM,YAGxB,OAAO,SACR,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,OAAlB;CACO;CACL,WAAU;CACV,GAAI;AACL,CAAA,CACF;AACD,aAAa,cAAcA,KAAAA,QAAiB,MAAM;AAElD,MAAM,eAAeD,MAAM,YAGxB,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,OAAlB;CACO;CACL,WAAWC,kBAAAA,GACT,0NACA,SACF;CACA,GAAI;AACL,CAAA,CACF;AACD,aAAa,cAAcD,KAAAA,QAAiB,MAAM;AAElD,MAAM,mBAAmBD,MAAM,YAG5B,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,WAAlB;CACO;CACL,WAAWC,kBAAAA,GAAG,wBAAwB,SAAS;CAC/C,GAAI;AACL,CAAA,CACF;AACD,iBAAiB,cAAcD,KAAAA,QAAiB,UAAU;AAE1D,MAAM,cAAcD,MAAM,YAGvB,EAAE,WAAW,GAAG,SAAS,SAC1B,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,MAAlB;CACO;CACL,WAAWC,kBAAAA,GACT,mQACA,SACF;CACA,GAAI;AACL,CAAA,CACF;AACD,YAAY,cAAcD,KAAAA,QAAiB,KAAK;AAIhD,MAAM,iBAAiBD,MAAM,YAG1B,EAAE,GAAG,SAAS,SAAQ,GAAA,kBAAA,IAAA,CAACC,KAAAA,QAAiB,SAAlB;CAA+B;CAAK,GAAI;AAAQ,CAAA,CAAC;AAC1E,eAAe,cAAcA,KAAAA,QAAiB,QAAQ;AAmDtD,SAAS,cAAc,EACrB,MACA,cACA,aACA,OACA,QAAQ,mBACR,cAAc,2BACd,WACA,kBACA,UACA,gBACqB;CACrB,QACE,GAAA,kBAAA,IAAA,CAACG,eAAAA,QAAD;EAAc;EAAoB;EAA2B;EAAoB;EAC/E,WAAA,GAAA,kBAAA,KAAA,CAACC,eAAAA,eAAD;GACE,GAAI;GAKJ,WAAWH,kBAAAA,GAAG,gEAAgE,SAAS;GAKvF,kBAAiB;GAXnB,UAAA;KAeE,GAAA,kBAAA,IAAA,CAACI,eAAAA,aAAD;KAAa,WAAU;KAAW,UAAA;IAAmB,CAAA;KACrD,GAAA,kBAAA,IAAA,CAACC,eAAAA,mBAAD;KAAmB,WAAU;KAAW,UAAA;IAA+B,CAAA;KACvE,GAAA,kBAAA,IAAA,CAAC,SAAD;KACE,WAAWL,kBAAAA,GACT,+WACA,gBACF;KAEC;IACM,CAAA;GACI;;CACT,CAAA;AAEZ;AACA,cAAc,cAAc;;;AC5N5B,SAAgB,iBAAiB,OAAkC;CACjE,OAAO,MAAM,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,YAAY,CAAC,MAAM;AAC7F;AAGA,SAAS,YAAY,SAAuC;CAC1D,MAAM,SAAS,SAAS,cAAc,GAAG,KAAK;CAC9C,CAAA,GAAA,cAAA,eAAA,CAAe,MAAM;AACvB;AAQA,SAAgB,iBAAiB,EAC/B,MACA,QACA,WACA,QACA,YAOqB;CACrB,MAAM,aAAaM,gBAAAA,iBAAiB;CACpC,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,KAAK;EAAa,WAAU;EAChC,WAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;GACQ;GACN,GAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe,OAAO;IAClDC,cAAAA,yBAAyB;GAChC,GAAK,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;GAChD,UAAU,UAA+C;IACvD,MAAM,gBAAgB;IACtB,IAAI,CAAC,iBAAiB,KAAK,KAAK,MAAM,cAAc,WAAW,UAAU;IACzE,MAAM,eAAe;IACrB,OAAO;GACT;GAEC;EACS,CAAA;CACR,CAAA;AAEV;;;AClDA,SAAgB,kBAAkB,QAAiC,cAA+B;CAChG,IAAI,gBAAgB,OAAO,iBAAiB,QAAQ,OAAO,kBAAkB,IAC3E,OAAO,OAAO,OAAO,aAAa;CAEpC,OAAO,OAAO,OAAO,MAAM,EAAE;AAC/B;;;ACCA,SAAgB,YACd,QACA,cAC0C;CAC1C,MAAM,UAAA,GAASC,cAAAA,cAAAA,CAAc,MAAM;CACnC,MAAM,UACJ,iBAAiB,KAAA,KAAa,OAAO,WAAW,WAAW,OAAO,aAAa,YAAY;CAC7F,QAAA,GAAOC,MAAAA,YAAAA,EACJ,QAAQ,kBAAkB,KAAK,UAAU,eAAe,KAAA,CAAS,GAClE,CAAC,SAAS,YAAY,CACxB;AACF;;;ACPA,MAAa,mBAAA,GAAkBC,MAAAA,cAAAA,CAAoC,IAAI;AAMvE,MAAa,eAAA,GAAcA,MAAAA,cAAAA,CAAc,KAAK;ACM9C,MAAM,2BAA2B;AAEjC,MAAM,kBAAkB,IAAI,IAAI;CAAC;CAAO;CAAW;CAAa;CAAa;AAAY,CAAC;AAG1F,IAAI,sBAAsB,OAAO;AACjC,MAAM,YAAY,IAAI,QAAkB;AAExC,SAAS,kBAAkB,KAAqB;CAC9C,IAAI,UAAU,IAAI,GAAG,GAAG;CACxB,UAAU,IAAI,GAAG;CACjB,IAAI,iBACF,YACC,UAAU;EACT,IAAI,gBAAgB,IAAI,MAAM,GAAG,GAAG,sBAAsB,KAAK,IAAI;CACrE,GACA,IACF;CACA,MAAM,eAAe;EACnB,sBAAsB,OAAO;CAC/B;CACA,IAAI,iBAAiB,eAAe,QAAQ,IAAI;CAChD,IAAI,iBAAiB,aAAa,QAAQ,IAAI;AAChD;AAGA,SAAS,mBAA4B;CACnC,OAAO,KAAK,IAAI,IAAI,uBAAuB;AAC7C;AAGA,MAAM,gBAAgB,IAAI,QAAiB;AAM3C,SAAgB,iBAAiB,SAA4B;CAC3D,cAAc,IAAI,OAAO;CACzB,QAAQ,MAAM;CACd,cAAc,OAAO,OAAO;AAC9B;AAUA,MAAM,gBAAgB,IAAI,QAAmC;AAG7D,SAAgB,iBAAiB,QAAsD;CACrF,OAAO,WAAW,OAAO,KAAA,IAAY,cAAc,IAAI,MAAM;AAC/D;AAUA,SAAS,SAAS,SAAiD;CACjE,IAAI,YAAY,MAAM,OAAO;CAC7B,MAAM,SAAS,QAAQ,cAA2B,mBAAmB;CACrE,IAAI,WAAW,MAAM,OAAO;CAC5B,MAAM,QAAQ,QAAQ;CACtB,OAAO,iBAAiB,cAAc,QAAQ;AAChD;AAEA,MAAM,6BAA6B,KAAA;AASnC,SAAgB,WAAW,EACzB,QACA,YAIe;CAEf,KAAA,GADeC,MAAAA,WAAAA,CAAW,WACjB,GAAG,QAAO,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAG,SAAW,CAAA;CACjC,QAAO,GAAA,kBAAA,IAAA,CAAC,kBAAD;EAA0B;EAAS;CAA2B,CAAA;AACvE;AAEA,SAAS,iBAAiB,EACxB,QACA,YAIe;CACf,MAAM,QAAA,GAAOC,cAAAA,aAAAA,CAAa;CAC1B,MAAM,WAAA,GAAUC,cAAAA,yBAAAA,CAAyB;CACzC,MAAM,cAAA,GAAaC,cAAAA,uBAAAA,CAAuB;CAC1C,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,CAA6B,IAAI;CAC7D,MAAM,WAAA,GAAUC,MAAAA,OAAAA,CAA+B,IAAI;CACnD,MAAM,SAAA,GAAQA,MAAAA,OAAAA,CAA6C,IAAI;CAE/D,MAAM,UAAA,GAASA,MAAAA,OAAAA,CAAO;EAAE;EAAM;EAAQ;CAAW,CAAC;CAClD,OAAO,UAAU;EAAE;EAAM;EAAQ;CAAW;CAE5C,MAAM,UAAA,GAASC,MAAAA,YAAAA,EAAa,YAAoC;EAC9D,QAAQ,UAAU;EAClB,IAAI,YAAY,QAAQ,QAAQ,kBAAkB,KAAA,GAChD,kBAAkB,QAAQ,aAAa;EACzC,UAAU,SAAS,OAAO,CAAC;CAC7B,GAAG,CAAC,CAAC;CAEL,MAAM,YAAA,GAAWC,MAAAA,qBAAAA,CACf,WAAW,OAAO,iBAAiB,QAAQ,iBAEzC,WAAW,QACX,QACG,YAAY,CAAC,CACb,MAAM,MAAM,EAAE,cAAcC,cAAAA,kBAAkB,EAAE,QAAQ,YAAY,MAAM,SACzE,KACR;CACA,MAAM,WAAW,KAAK,OAAO,IAAI,MAAM,CAAC,CAAC,UAAU;CAGnD,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,WAAW,MAAM;EACrB,IAAI,CAAC,UAAU;GACb,OAAO,gBAAgB,eAAe;GACtC,OAAO,gBAAgB,eAAe;GACtC;EACF;EACA,OAAO,aAAa,iBAAiB,QAAQ;EAC7C,OAAO,aAAa,iBAAiB,WAAW,SAAS,OAAO;CAClE,GAAG;EAAC;EAAQ;EAAU;CAAQ,CAAC;CAI/B,MAAM,aAAA,GAAYC,cAAAA,eAAAA,CAAe;CACjC,MAAM,UAAA,GAASH,MAAAA,YAAAA,EAA+B,IAAI,OAAO,KAAK,UAAU,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC;CAC5F,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,WAAW,MAAM;EACrB,cAAc,IAAI,QAAQ,MAAM;EAChC,aAAa;GACX,IAAI,cAAc,IAAI,MAAM,MAAM,QAAQ,cAAc,OAAO,MAAM;EACvE;CACF,GAAG,CAAC,QAAQ,MAAM,CAAC;CAEnB,MAAM,UAAA,GAASA,MAAAA,YAAAA,OAAkB;EAC/B,IAAI,MAAM,YAAY,MAAM;EAC5B,aAAa,MAAM,OAAO;EAC1B,MAAM,UAAU;CAClB,GAAG,CAAC,CAAC;CACL,CAAA,GAAA,MAAA,UAAA,OAAgB,QAAQ,CAAC,MAAM,CAAC;CAEhC,MAAM,QAAA,GAAOA,MAAAA,YAAAA,OAAkB;EAC7B,MAAM,UAAU;EAChB,MAAM,EAAE,MAAM,KAAK,QAAQ,MAAM,YAAY,SAAS,OAAO;EAC7D,MAAM,UAAU,SAAS,QAAQ,OAAO;EACxC,IAAI,YAAY,QAAQ,CAAC,QAAQ,aAAa;EAE9C,MAAM,SAAS,IAAI,KAAK,IAAI,MAAM;EAClC,IAAI,OAAO,UAAU,QAAQ;EAC7B,MAAM,WAAW,QAAQ,YAAY;EACrC,IAAI,SAAS,MAAM,MAAM,EAAE,cAAcE,cAAAA,kBAAkB,EAAE,QAAQ,YAAY,OAAO,GACtF;EAEF,KAAK,MAAM,cAAc,UACvB,IAAI,WAAW,cAAcA,cAAAA,gBAAgB,QAAQ,QAAQ,WAAW,EAAE;EAC5E,MAAM,QAAyB;GAC7B,QAAQ,OAAO;GACf,GAAI,KAAK,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG;GAC/C,GAAI,KAAK,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG;EACjD;EACA,QAAQ,QAAQ;GACd,WAAWA,cAAAA;GACX,MAAM;GACN,QAAQ,EAAE,QAAQ;GACX;GACP,YAAY;EACd,CAAC;CACH,GAAG,CAAC,OAAO,CAAC;CAEZ,MAAM,YAAA,GAAWF,MAAAA,YAAAA,OAAkB;EACjC,IAAI,MAAM,YAAY,MAAM;EAC5B,MAAM,UAAU,WAAW,MAAA,GAAmB;CAChD,GAAG,CAAC,IAAI,CAAC;CAET,QAEE,GAAA,kBAAA,IAAA,CAAC,QAAD;EACE,KAAK;EACL,WAAU;EACV,iBAAiB,UAAwB;GACvC,IAAI,MAAM,gBAAgB,WAAW,MAAM,gBAAgB,OAAO,SAAS;EAC7E;EACA,gBAAgB;EAChB,UAAU,UAAsB;GAC9B,IAAI,cAAc,IAAI,MAAM,MAAM,GAAG;GACrC,IAAI,iBAAiB,GAAG,SAAS;EACnC;EACA,QAAQ;EAER,eAAe;EACf,gBAAgB;EAEf;CACG,CAAA;AAEV;;;ACrMA,MAAa,wBACX;AAuBF,SAAgB,gBAAgB,EAAE,OAAO,SAAiD;CACxF,MAAM,OAAA,GAAMI,cAAAA,OAAAA,CAAO;CACnB,MAAM,OAAO,OAAO;CAGpB,MAAM,KAAK,OAAO,MAAM,OAAO,WAAW,KAAK,KAAK,KAAA;CACpD,MAAM,OAAO,MAAM,YAAY;CAI/B,IAAI;CACJ,IAAI;EACF,KAAK,MAAM,OAAO,KAAA,IAAY,KAAA,KAAA,GAAYC,aAAAA,YAAAA,CAAY,KAAK,EAAE;CAC/D,QAAQ;EACN,KAAK,KAAA;CACP;CACA,MAAM,KAAK;CACX,MAAM,QAAQ,OAAO,SAAS,EAAE;CAChC,MAAM,SAAS,oBAAoB,MAAM,UAAU,GAAG;CAEtD,MAAM,QAAA,GAAOC,aAAAA,eAAAA,CAAe,EAAE,IAAK,OAAiD,GAAG,CAAC;CAMxF,IAAI,SAAS,QAAQ,UAAU,IAC7B,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAS,WAAU;EAAwB,UAAA;CAElE,CAAA;CAMV,IAAI,CAAC,IAAI;EACP,IAAI,QAAQ,IAAI,aAAa,cAE3B,QAAQ,KACN,yEAAyE,MAAM,EACjF;EAEF,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,4BAA4B,MAAM;GAAc,UAAA;EAAY,CAAA;CACxF;CAGA,IAAI,MAAM,QAAO,GAAA,kBAAA,IAAA,CAAC,qBAAD;EAAyB;EAAQ;EAAW;EAAY;EAAW;CAAO,CAAA;CAE3F,QACE,GAAA,kBAAA,IAAA,CAAC,sBAAD;EACM;EACA;EACA;EACG;EACF;EACC;CACP,CAAA;AAEL;AAeA,SAAgB,cAAc,EAC5B,IACA,SAMqB;CACrB,QAAO,GAAA,kBAAA,IAAA,CAAC,iBAAD;EAAwB;EAAO,OAAO;GAAE,MAAM;GAAI,MAAM,EAAE,GAAG;EAAE;CAAI,CAAA;AAC5E;AAGA,SAAS,WAAW,IAAY,IAAwB,KAAyB;CAC/E,OAAO,OAAO,KAAA,IAAY;EAAE;EAAI,IAAI;CAAI,IAAI;EAAE;EAAI,IAAI;GAAE,OAAO;GAAI;EAAI;CAAE;AAC3E;AAGA,SAAS,YAAY,KAAU,KAA2C;CACxE,MAAM,WAAW,IAAI,UAAU;CAC/B,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,IAAI;EACF,OAAO,IAAI,SAAS,KAAA,GAAID,aAAAA,YAAAA,CAAY,QAA4B,CAAC;CACnE,QAAQ;EACN;CACF;AACF;AAGA,SAAgB,YAAY,KAAU,KAA2B;CAC/D,MAAM,SAAS,YAAY,KAAK,GAAG;CACnC,OAAO,WAAW,KAAA,MAAA,GAAaE,aAAAA,cAAAA,CAAc,QAAQ,MAAM;AAC7D;AAOA,SAAS,YAAY,EACnB,MACA,QACA,QACA,YAMqB;CACrB,IAAI,SAAS,MACX,QACE,GAAA,kBAAA,IAAA,CAAC,UAAD;EACE,MAAK;EACL,eAAa;GACNC,cAAAA,yBAAyB;EAChC,WAAW,GAAG,sBAAsB;EACpC,UAAU,UAAU;GAClB,MAAM,eAAe;GACrB,MAAM,gBAAgB;GACtB,OAAO;EACT;EAEC;CACK,CAAA;CAEZ,QACE,GAAA,kBAAA,IAAA,CAAC,kBAAD;EAAwB;EAAc;EAAQ,WAAW;EAA+B;EACrF;CACe,CAAA;AAEtB;AAQA,SAAS,oBAAoB,EAC3B,IACA,IACA,OACA,KACA,MAAM,YACsC;CAC5C,MAAM,aAAaC,gBAAAA,iBAAiB;CACpC,MAAM,QAAA,GAAOC,cAAAA,aAAAA,CAAa;CAC1B,MAAM,QAAA,GAAOC,cAAAA,eAAAA,CAAe;CAC5B,MAAM,YAAY,IAAI,SAAS,IAAI,EAAE;CACrC,MAAM,SAAS,oBAAoB,GAAG,GAAG;CACzC,IAAI,CAAC,aAAa,aAAa,QAAQ,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAS,UAAA;CAAY,CAAA;CAChG,MAAM,SAAS,KAAK,IAAI,MAAM;CAE9B,IAAI,OAAO,UAAU,aACnB,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EAAY,QAAQ,WAAW,IAAI,IAAI,KAAK;EAC1C,WAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,GAAG,OAAO;GAAS,UAAA;EAAY,CAAA;CACxC,CAAA;CAGhB,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAChD,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAS,UAAA;CAAY,CAAA;CAC3D,MAAM,OAAO,YAAY,KAAK,SAAS;CAEvC,IAAI,aAAa,QAAQ;EACvB,IAAI,OAAO,KAAA,KAAa,CAAC,MAAM,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,GAAG,OAAO;GAAS,UAAA;EAAY,CAAA;EACxF,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;GACE,OAAA,GAAMC,aAAAA,iBAAAA,CAAiB,IAAI,UAAU,WAAW,IAAI,KAAK;GACzD,eAAa,GAAG,OAAO;GACvB,WAAW;GACX,UAAU,MAAM,EAAE,gBAAgB;GAEjC,UAAA;EACS,CAAA;CAEhB;CACA,IAAI,OAAO,UAAU,QAAQ;EAC3B,IAAI,OAAO,KAAA,KAAa,CAAC,MAAM,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,GAAG,OAAO;GAAS,UAAA;EAAY,CAAA;EACxF,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;GACE,OAAA,GAAMA,aAAAA,iBAAAA,CAAiB,IAAI,UAAU,WAAW,IAAI,KAAK;GACzD,eAAa,GAAG,OAAO;GACvB,WAAW;GACX,UAAU,MAAM,EAAE,gBAAgB;GAEjC,UAAA;EACS,CAAA;CAEhB;CACA,IAAI,OAAO,KAAA,GACT,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EAAY,QAAQ,WAAW,IAAI,IAAI,KAAK;EAC1C,WAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;GAAkB;GAAQ;GAAW;GAAO,QAAQ,OAAO;EAAS,CAAA;CAC1D,CAAA;CAEhB,MAAM,gBAAgB,KAAK,KAAK,IAAI,KAAK;CACzC,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EAAY,QAAQ,WAAW,IAAI,KAAA,GAAW,KAAK;EACjD,WAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;GACE,MAAM,QAAA,GAAOA,aAAAA,iBAAAA,CAAiB,IAAI,UAAU,WAAW,IAAI,KAAK,IAAI;GACpE,QAAQ,GAAG,OAAO;GAClB,QAAQ;GAEP,UAAA;EACU,CAAA;CACH,CAAA;AAEhB;AAGA,SAAS,aAAa,EACpB,IACA,IACA,OACA,UAMqB;CACrB,MAAM,OAAA,GAAMR,cAAAA,OAAAA,CAAO;CACnB,MAAM,KAAA,GAAIS,cAAAA,aAAAA,CAAa;CACvB,MAAM,QAAA,GAAOF,cAAAA,eAAAA,CAAe;CAC5B,MAAM,CAAC,OAAO,aAAA,GAAYG,MAAAA,SAAAA,CAAS,KAAK;CAcxC,MAAM,QAAA,GAbOC,cAAAA,QAAAA,CAAQ,IAAI;EACvB,GAAI,QACA;GACE,QAAQ;IAAE,OAAO;IAAI,IAAI;IAAM,OAAO;GAAM;GAC5C,YAAY;IAAE,MAAM;IAAU,MAAM;IAAG,UAAU;GAAE;EACrD,IACA,CAAC;EAGL,OAAO;EACP,SAAS;EACT,OAAO;CACT,CACiB,CAAC,CAAC,MACf;CACJ,MAAM,SAAS,oBAAoB,GAAG,GAAG;CACzC,MAAM,WAAW,IAAI,SAAS,IAAI,MAAM,CAAC,EAAE,KAAA,EACvC;CACJ,MAAM,QACJ,MAAM,WAAW,IACb,KAAK,EAAE,GAAG,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU,QACxE,KAAA;CAGN,MAAM,aAAA,GAAYC,MAAAA,OAAAA,CAAO,KAAK;CAC9B,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,SAAS,SAAS,KAAA,KAAa,CAAC,UAAU,SAAS;EACxD,IAAI,KAAK,SAAS,GAAG;GACnB,UAAU,UAAU;GACpB,IAAI,QAAQ,IAAI,aAAa,cAC3B,QAAQ,MAAM,aAAA,GAAYC,aAAAA,uBAAAA,CAAuB,IAAI,IAAI,KAAK,CAAC,CAAC,SAAS;GAC3E;EACF;EACA,UAAU,UAAU;EACpB,IAAI,KAAK,WAAW,KAAK,UAAU,KAAA,KAAa,UAAU,MAAM,KAAK,IAAI,OAAO,KAAK,CAAC;CACxF,GAAG;EAAC;EAAO;EAAM;EAAO;EAAM;EAAI;EAAI;CAAK,CAAC;CAC5C,IAAI,SAAS,SAAS,KAAA,KAAa,KAAK,WAAW,GACjD,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAe,WAAU;EACnD,UAAA,EAAE,wBAAwB,EAAE,cAAc,gBAAgB,CAAC;CACxD,CAAA;CAEV,IAAI,SAAS,SAAS,KAAA,KAAa,KAAK,SAAS,GAC/C,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAa,WAAU;EACjD,UAAA;CACG,CAAA;CAEV,QACE,GAAA,kBAAA,IAAA,CAAC,UAAD;EACE,MAAK;EACL,eAAa,GAAG,OAAO;GAChBT,cAAAA,yBAAyB;EAChC,WAAW,GAAG,sBAAsB;EACpC,UAAU,UAAU;GAClB,MAAM,eAAe;GACrB,MAAM,gBAAgB;GAEtB,IAAI,SAAS,UAAU,KAAA,KAAa,UAAU,MAAM,KAAK,IAAI,OAAO,KAAK,CAAC;QACrE;IACH,UAAU,UAAU;IACpB,SAAS,IAAI;GACf;EACF;EAEC,UAAA;CACK,CAAA;AAEZ;AAmBA,SAAS,qBAAqB,OAAsC;CAClE,MAAM,QAAA,GAAOE,cAAAA,aAAAA,CAAa;CAC1B,MAAM,SAAS,MAAM;CACrB,MAAM,EAAE,UAAU;CAClB,MAAM,SAAS,KAAK,QAAQ,MAAM;CAGlC,IAAI,OAAO,UAAU,aACnB,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EAAY,QAAQ,WAAW,QAAQ,MAAM,IAAI,KAAK;EACpD,WAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,oBAAoB,OAAO,GAAG,MAAM;GAAS,UAAA;EAAY,CAAA;CAClE,CAAA;CAEhB,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAChD,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,oBAAoB,OAAO,GAAG,MAAM;EAAS,UAAA;CAAY,CAAA;CACrF,QACE,GAAA,kBAAA,IAAA,CAAC,qBAAD;EACE,GAAI;EACJ,aAAa,OAAO,UAAU,UAAU,MAAM,SAAS,UAAU,OAAO,SAAS;CAClF,CAAA;AAEL;AAQA,SAAS,oBAAoB,EAC3B,IACA,IACA,IACA,OACA,KACA,MAAM,UACN,eAC0E;CAC1E,MAAM,YAAY,IAAI,SAAS,IAAI,EAAE;CACrC,MAAM,WAAW,YAAY,IAAI,WAAW,YAAY;CACxD,MAAM,SAAS,oBAAoB,GAAG,GAAG;CACzC,MAAM,aAAaD,gBAAAA,iBAAiB;CACpC,MAAM,QAAA,GAAOE,cAAAA,eAAAA,CAAe;CAC5B,MAAM,EAAE,QAAQ,WAAW,OAAO,eAAA,GAAcO,cAAAA,iBAAAA,CAC9C,IACA,IACA,OAAO,KAAA,IAAY,KAAA,IAAY,EAAE,GAAG,CACtC;CAIA,IAAI,CAAC,WAAW;EACd,IAAI,QAAQ,IAAI,aAAa,cAE3B,QAAQ,KACN,sCAAsC,GAAG,0CAA0C,MAAM,EAC3F;EAEF,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,eAAa,GAAG,OAAO;GAAgB,WAAU;GACpD,UAAA;EACG,CAAA;CAEV;CAEA,IAAI,WACF,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EACE,eAAa,4BAA4B;EACzC,WAAU;EACV,aAAU;CACX,CAAA;CAKL,IAAI,WACF,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EACE,eAAa,GAAG,OAAO;EACvB,WAAU;EACV,OAAO,GAAG,GAAG,0BAA0B,MAAM,KAAK,GAAG;EAEpD,UAAA;CACG,CAAA;CAIV,IAAI,OACF,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAS,WAAU;EAAwB,OAAO,MAAM;EAClF,UAAA;CACG,CAAA;CAIV,IAAI,CAAC,QACH,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EACE,eAAa,GAAG,OAAO;EACvB,WAAU;EACV,OAAO,GAAG,GAAG,IAAI,MAAM;EAEtB,UAAA;CACG,CAAA;CAIV,MAAM,cAAc,SAAS,MAAM;CAGnC,MAAM,UAAW,UAAU,MAAqD;CAChF,MAAM,QAAQ,OAAO,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU;CACnF,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IACrD,QACE,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa,GAAG,OAAO;EAAS,WAAU;EAC7C,UAAA;CACG,CAAA;CAGV,MAAM,OADO,YAAY,KAAK,SACd,KAAA,GAAIN,aAAAA,iBAAAA,CAAiB,IAAI,UAAU,WAAW,IAAI,OAAO,KAAK,CAAC,IAAI;CAEnF,IAAI,aAAa,QAAQ,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa;EAAS,UAAA;CAAkB,CAAA;CAC9E,IAAI,gBAAgB,MAClB,QACE,GAAA,kBAAA,IAAA,CAAC,WAAD;EAAe;EAAI,OAAO,OAAO,KAAK;EAAS;EAAM,QAAQ;EAAc;EACxE,UAAA;CACQ,CAAA;CAGf,IAAI,SAAS,MAAM,QAAO,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,eAAa;EAAS,UAAA;CAAkB,CAAA;CACxE,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EACQ;EACN,eAAa;EACb,WAAW;EACX,UAAU,MAAM,EAAE,gBAAgB;EAEjC,UAAA;CACS,CAAA;AAEhB;AAMA,SAAS,UAAU,EACjB,IACA,OACA,MACA,QACA,MACA,YAQqB;CACrB,MAAM,UAAA,GAASO,MAAAA,YAAAA,OAAkB,KAAK,KAAK,IAAI,KAAK,GAAG;EAAC;EAAM;EAAI;CAAK,CAAC;CACxE,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EAAY,QAAQ,WAAW,IAAI,KAAA,GAAW,KAAK;EACjD,WAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;GAAmB;GAAc;GAAgB;GAC9C;EACU,CAAA;CACH,CAAA;AAEhB;;;AC9iBA,SAAgB,iBACd,UACA,SACwB;CACxB,IAAI,aAAa,KAAA,KAAa,OAAO,aAAa,YAAY,OAAO;CACrE,IAAI,CAAC,MAAM,QAAQ,SAAS,MAAM,GAAG,OAAO;CAC5C,MAAM,SAAS,SAAS,OAAO,QAAQ,QACrC,QAAQ,OAAO,QAAQ,WAAW,MAAM,IAAI,KAAK,CACnD;CACA,IAAI,OAAO,WAAW,SAAS,OAAO,QAAQ,OAAO;CACrD,OAAO,OAAO,WAAW,IAAI,KAAA,IAAY;EAAE,GAAG;EAAU;CAAO;AACjE;AAOA,SAAgB,iBACd,QACA,SACS;CACT,OAAO,WAAW,KAAA,MAAA,GAAaC,cAAAA,aAAAA,CAAa,MAAM,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,KAAK,CAAC;AACrF"}
@@ -1,5 +1,5 @@
1
1
  import { l as cn, n as useTheme } from "./use-theme-dnE3Od3I.js";
2
- import { DEFAULT_LANGUAGE_STORAGE_KEY, useOptionalLocale, useOptionalT, useRegisterNotificationSink } from "@burojs/react";
2
+ import { useOptionalT, useRegisterNotificationSink } from "@burojs/react";
3
3
  import * as React from "react";
4
4
  import { createContext, forwardRef, useContext, useMemo, useRef } from "react";
5
5
  import * as PopoverPrimitive from "@radix-ui/react-popover";
@@ -38,57 +38,30 @@ function useLinkComponent() {
38
38
  return useContext(LinkComponentContext);
39
39
  }
40
40
  //#endregion
41
- //#region src/layout/language-storage.ts
42
- const DEFAULT_LANGUAGE = "ru";
43
- function loadLanguage(storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
44
- if (typeof window === "undefined") return "ru";
45
- try {
46
- const saved = window.localStorage.getItem(storageKey);
47
- if (saved === "ru" || saved === "en") return saved;
48
- } catch {}
49
- return "ru";
50
- }
51
- function saveLanguage(code, storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
52
- if (typeof window === "undefined") return;
53
- try {
54
- window.localStorage.setItem(storageKey, code);
55
- } catch {}
56
- }
57
- function loadLocale(storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
58
- if (typeof window === "undefined") return void 0;
59
- try {
60
- return window.localStorage.getItem(storageKey) ?? void 0;
61
- } catch {
62
- return;
63
- }
64
- }
65
- function saveLocale(code, storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
66
- if (typeof window === "undefined") return;
67
- try {
68
- window.localStorage.setItem(storageKey, code);
69
- } catch {}
70
- }
71
- //#endregion
72
41
  //#region src/lib/friendly-error.ts
73
42
  const DEV_FACING = /^Error \(status code:|^Failed to fetch$|^NetworkError|^Load failed$|^TypeError: (Failed to fetch|NetworkError)/i;
74
43
  const NETWORK = /Failed to fetch|NetworkError|Load failed|ERR_INTERNET_DISCONNECTED/i;
75
44
  const HTTP_TRACE = /\b(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b[^\n]*->\s*(\d{3})\b/;
76
- const STRINGS = {
77
- ru: {
78
- title: "Не удалось выполнить запрос",
79
- offline: "Нет связи с сервером. Проверьте подключение и повторите.",
80
- unavailable: "Сервис временно недоступен. Повторите попытку через минуту.",
81
- generic: "Попробуйте повторить действие."
82
- },
83
- en: {
84
- title: "Request failed",
85
- offline: "Cannot reach the server. Check your connection and try again.",
86
- unavailable: "The service is temporarily unavailable. Try again in a minute.",
87
- generic: "Please try again."
88
- }
45
+ const en = {
46
+ title: "Request failed",
47
+ offline: "Cannot reach the server. Check your connection and try again.",
48
+ unavailable: "The service is temporarily unavailable. Try again in a minute.",
49
+ generic: "Please try again."
89
50
  };
90
- function toFriendlyError(message, description, locale) {
91
- const s = (locale !== void 0 ? STRINGS[locale] : void 0) ?? STRINGS.en;
51
+ function friendlyStringsFrom(t) {
52
+ const pick = (name) => {
53
+ const key = `requestError.${name}`;
54
+ const value = t(key);
55
+ return value && value !== key ? value : en[name];
56
+ };
57
+ return {
58
+ title: pick("title"),
59
+ offline: pick("offline"),
60
+ unavailable: pick("unavailable"),
61
+ generic: pick("generic")
62
+ };
63
+ }
64
+ function toFriendlyError(message, description, strings) {
92
65
  const raw = `${String(message ?? "")} ${String(description ?? "")}`;
93
66
  const isDevFacing = DEV_FACING.test(String(message ?? "").trim());
94
67
  const isNetwork = NETWORK.test(raw);
@@ -96,8 +69,8 @@ function toFriendlyError(message, description, locale) {
96
69
  if (trace) {
97
70
  const status = Number(trace[1]);
98
71
  return {
99
- title: s.title,
100
- detail: status >= 500 ? s.unavailable : s.generic,
72
+ title: strings.title,
73
+ detail: status >= 500 ? strings.unavailable : strings.generic,
101
74
  replaced: true
102
75
  };
103
76
  }
@@ -107,21 +80,20 @@ function toFriendlyError(message, description, locale) {
107
80
  replaced: false
108
81
  };
109
82
  return {
110
- title: s.title,
111
- detail: isNetwork ? s.offline : String(description ?? "") || s.generic,
83
+ title: strings.title,
84
+ detail: isNetwork ? strings.offline : String(description ?? "") || strings.generic,
112
85
  replaced: true
113
86
  };
114
87
  }
115
88
  //#endregion
116
89
  //#region src/notification/notification-provider.ts
117
90
  function createNotificationProvider(options = {}) {
118
- const resolveLocale = options.resolveLocale ?? (() => loadLocale());
119
91
  return {
120
92
  open: ({ type, message, description }) => {
121
93
  let text = message;
122
94
  let detail = description;
123
95
  if (type === "error") {
124
- const friendly = toFriendlyError(message, description, resolveLocale());
96
+ const friendly = toFriendlyError(message, description, options.resolveStrings?.() ?? en);
125
97
  text = friendly.title;
126
98
  detail = friendly.detail;
127
99
  }
@@ -137,10 +109,9 @@ function createNotificationProvider(options = {}) {
137
109
  function Toaster$1(props) {
138
110
  const t = useOptionalT();
139
111
  const { resolved: theme } = useTheme();
140
- const locale = useOptionalLocale();
141
- const localeRef = useRef(locale);
142
- localeRef.current = locale;
143
- const sink = useMemo(() => createNotificationProvider({ resolveLocale: () => localeRef.current ?? loadLocale() }), []);
112
+ const tRef = useRef(t);
113
+ tRef.current = t;
114
+ const sink = useMemo(() => createNotificationProvider({ resolveStrings: () => friendlyStringsFrom(tRef.current) }), []);
144
115
  useRegisterNotificationSink(sink);
145
116
  return jsx(Toaster, {
146
117
  containerAriaLabel: t("notifications.region", { defaultValue: "Notifications" }),
@@ -159,6 +130,6 @@ function Toaster$1(props) {
159
130
  });
160
131
  }
161
132
  //#endregion
162
- export { DEFAULT_LANGUAGE_STORAGE_KEY as a, saveLanguage as c, useLinkComponent as d, Popover as f, PopoverTrigger as g, PopoverContent as h, DEFAULT_LANGUAGE as i, saveLocale as l, PopoverClose as m, toast$1 as n, loadLanguage as o, PopoverAnchor as p, toFriendlyError as r, loadLocale as s, Toaster$1 as t, LinkComponentProvider as u };
133
+ export { LinkComponentProvider as a, PopoverAnchor as c, PopoverTrigger as d, toFriendlyError as i, PopoverClose as l, toast$1 as n, useLinkComponent as o, friendlyStringsFrom as r, Popover as s, Toaster$1 as t, PopoverContent as u };
163
134
 
164
- //# sourceMappingURL=toaster-CfCRLgR-.js.map
135
+ //# sourceMappingURL=toaster-C02XbB9r.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toaster-C02XbB9r.js","names":["Toaster","SonnerToaster"],"sources":["../src/primitives/popover.tsx","../src/layout/link-component.tsx","../src/lib/friendly-error.ts","../src/notification/notification-provider.ts","../src/notification/toaster.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '../lib/cn.js';\n\n/**\n * Thin wrapper around Radix Popover, mirroring the structure of the\n * Dialog / Select primitives in this package so the Tailwind tokens\n * stay aligned. Re-exports `Root` / `Trigger` / `Anchor` / `Close`\n * directly (no styling needed) and styles `Content` to match the\n * shadcn Popover defaults.\n */\n\nconst Popover = PopoverPrimitive.Root;\nconst PopoverTrigger = PopoverPrimitive.Trigger;\nconst PopoverAnchor = PopoverPrimitive.Anchor;\nconst PopoverClose = PopoverPrimitive.Close;\n\nexport type PopoverContentProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>;\n\nconst PopoverContent = React.forwardRef<\n React.ElementRef<typeof PopoverPrimitive.Content>,\n PopoverContentProps\n>(({ className, align = 'start', sideOffset = 4, ...props }, ref) => (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'z-50 w-72 rounded-md border bg-popover p-3 text-popover-foreground shadow-md outline-hidden',\n 'duration-[var(--duration-overlay)] ease-[var(--ease-overlay)]',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = PopoverPrimitive.Content.displayName;\n\nexport { Popover, PopoverTrigger, PopoverAnchor, PopoverClose, PopoverContent };\n","'use client';\nimport {\n type AnchorHTMLAttributes,\n type ComponentType,\n type ReactElement,\n type ReactNode,\n createContext,\n forwardRef,\n useContext,\n} from 'react';\n\n/**\n * Props every `LinkComponent` must accept. We intentionally mirror the\n * native `<a>` API surface so adopters writing a small wrapper around\n * their router's link primitive (next/link, react-router, TanStack\n * Router, etc.) can pass them straight through.\n *\n * `next/link` already accepts these names verbatim. `react-router`'s\n * `<Link>` uses `to` instead of `href` — adopters wrap once:\n *\n * const RouterLink: LinkComponent = ({ href, ...rest }) =>\n * <Link to={href} {...rest} />;\n */\nexport type LinkComponentProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & {\n href: string;\n children?: ReactNode;\n};\n\nexport type LinkComponent = ComponentType<LinkComponentProps>;\n\n/**\n * Default fallback — a plain `<a href>` anchor. Triggers a full\n * document load. Adopters that ship a SPA router (Next.js App Router,\n * react-router) install their router's `<Link>` via\n * `<LinkComponentProvider>` so internal navigation stays client-side.\n */\nconst DefaultLinkComponent: LinkComponent = forwardRef<HTMLAnchorElement, LinkComponentProps>(\n function DefaultLinkComponent({ children, ...rest }, ref) {\n return (\n <a ref={ref} {...rest}>\n {children}\n </a>\n );\n },\n);\n\nconst LinkComponentContext = createContext<LinkComponent>(DefaultLinkComponent);\n\nexport interface LinkComponentProviderProps {\n /**\n * The router-aware Link component to use throughout `@burojs/ui-shadcn` for\n * internal navigation. Pass `Link` from `next/link` for Next.js App Router;\n * wrap your router's link primitive once for react-router / TanStack Router.\n */\n linkComponent: LinkComponent;\n children: ReactNode;\n}\n\n/**\n * Install a router-aware `LinkComponent` for every framework component that\n * renders an internal navigation link (sidebar entries, action links, relation\n * cells, the list-empty Create CTA, …). Without it the framework falls back to\n * a raw `<a>` — correct, but a full document reload on every click.\n *\n * This is a link component and nothing else: the location and imperative\n * navigation are the `RouterAdapter`'s (`RouterAdapterProvider` and\n * `useRouteNavigate` from `@burojs/react`), the one router seam.\n *\n * import Link from 'next/link';\n * <LinkComponentProvider linkComponent={Link}>\n * <App />\n * </LinkComponentProvider>\n */\nexport function LinkComponentProvider({\n linkComponent,\n children,\n}: LinkComponentProviderProps): ReactElement {\n return (\n <LinkComponentContext.Provider value={linkComponent}>{children}</LinkComponentContext.Provider>\n );\n}\n\n/**\n * Read the current `LinkComponent`. Call this from any framework\n * component that needs to render an internal-navigation link, then\n * use the returned component in JSX:\n *\n * const RouterLink = useLinkComponent();\n * return <RouterLink href={href}>{label}</RouterLink>;\n *\n * Always returns a usable component — the default fallback emits a\n * raw `<a href>` so the component never has to null-check.\n */\nexport function useLinkComponent(): LinkComponent {\n return useContext(LinkComponentContext);\n}\n","/**\n * Turn a developer-facing error string into something a user can act on.\n *\n * Two surfaces need this and they are reached by different code paths — the\n * notification provider (a legacy query adapter's default query-error\n * toast) and the inline\n * error panel a list renders in place of its rows. Fixing only the first left\n * `/document_document` still printing a raw English «Failed to fetch» under a\n * Russian heading, which is how this ended up in a second acceptance round.\n * One helper, both call sites.\n *\n * Anything we do not recognise is passed through unchanged: a message written\n * by the adopter's own `onError` is almost certainly better than a generic\n * substitute.\n */\n\n/** A legacy query adapter's own untranslated wording plus the raw fetch/network failures. */\nconst DEV_FACING =\n /^Error \\(status code:|^Failed to fetch$|^NetworkError|^Load failed$|^TypeError: (Failed to fetch|NetworkError)/i;\nconst NETWORK = /Failed to fetch|NetworkError|Load failed|ERR_INTERNET_DISCONNECTED/i;\n/**\n * A machine transport trace, e.g.\n * «Temporal GET /namespaces/default/schedules?maximumPageSize=10 -> 502»\n *\n * A data provider that reports which call failed is doing the right thing —\n * that string belongs in a bug report. It does not belong in front of an\n * operator as the entire explanation of why their page is empty, which is\n * exactly what `/schedules` did when the Temporal cluster went down.\n *\n * Recognised by an HTTP verb followed by an arrow and a status code; nothing\n * an adopter would hand-write looks like this, so the pass-through contract\n * for adopter-authored messages is preserved.\n */\nconst HTTP_TRACE = /\\b(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\b[^\\n]*->\\s*(\\d{3})\\b/;\n\n/** The four sentences a recognised failure is replaced with: the bundle keys `requestError.*`. */\nexport interface FriendlyErrorStrings {\n readonly title: string;\n readonly offline: string;\n readonly unavailable: string;\n readonly generic: string;\n}\n\n/**\n * The framework's code language: used where no i18n is configured, and for any\n * key the active bundles do not carry.\n */\nexport const en: FriendlyErrorStrings = {\n title: 'Request failed',\n offline: 'Cannot reach the server. Check your connection and try again.',\n unavailable: 'The service is temporarily unavailable. Try again in a minute.',\n generic: 'Please try again.',\n};\n\n/**\n * Resolve {@link FriendlyErrorStrings} through a translate function (`useT()`,\n * `useOptionalT()`), taking the English default for each key it cannot find.\n */\nexport function friendlyStringsFrom(t: (key: string) => string): FriendlyErrorStrings {\n const pick = (name: keyof FriendlyErrorStrings): string => {\n const key = `requestError.${name}`;\n const value = t(key);\n return value && value !== key ? value : en[name];\n };\n return {\n title: pick('title'),\n offline: pick('offline'),\n unavailable: pick('unavailable'),\n generic: pick('generic'),\n };\n}\n\nexport interface FriendlyError {\n /** Short headline. Empty when the original message should be kept. */\n title: string;\n /** Explanation the user can act on. */\n detail: string;\n /** True when the input was recognised as developer-facing and replaced. */\n replaced: boolean;\n}\n\n/**\n * @param strings the sentences to substitute, already in the active language:\n * {@link friendlyStringsFrom} builds them from the app's translate function,\n * and {@link en} is the default where none is configured.\n */\nexport function toFriendlyError(\n message: unknown,\n description: unknown,\n strings: FriendlyErrorStrings,\n): FriendlyError {\n const raw = `${String(message ?? '')} ${String(description ?? '')}`;\n const isDevFacing = DEV_FACING.test(String(message ?? '').trim());\n const isNetwork = NETWORK.test(raw);\n const trace = HTTP_TRACE.exec(String(message ?? '').trim());\n\n if (trace) {\n // 5xx is the server's problem and waiting is the right advice; anything\n // else is at least not something the operator can read a URL out of.\n const status = Number(trace[1]);\n return {\n title: strings.title,\n detail: status >= 500 ? strings.unavailable : strings.generic,\n replaced: true,\n };\n }\n\n if (!isDevFacing && !isNetwork) {\n return {\n title: String(message ?? ''),\n detail: String(description ?? ''),\n replaced: false,\n };\n }\n return {\n title: strings.title,\n detail: isNetwork ? strings.offline : String(description ?? '') || strings.generic,\n replaced: true,\n };\n}\n","import type { NotificationSinkMessage } from '@burojs/react';\nimport { toast } from 'sonner';\nimport { type FriendlyErrorStrings, en, toFriendlyError } from '../lib/friendly-error.js';\n\n/** `<Toaster>`'s sink: `@burojs/react`'s `NotificationSink`, plus `close`. */\nexport interface NotificationSink {\n open(args: NotificationSinkMessage): void;\n close(key: string): void;\n}\n\n/**\n * A legacy query adapter can build its default query-error notification outside\n * our i18n bundle: the user saw «Error (status code: undefined)» / «Failed to fetch», plus it duplicated\n * the inline error state a list already renders. We can't localize a string we\n * don't own, so `toFriendlyError` recognises it and substitutes plain language.\n * The SAME helper backs the list's inline panel — see friendly-error.ts for why\n * that matters.\n */\nexport interface NotificationProviderOptions {\n /**\n * The sentences an error toast is rewritten with (`requestError.*` in the active bundle), or\n * `undefined` for the English defaults. `<Toaster>` passes one built from the app's translate\n * function; a sink built outside React passes its own.\n *\n * ⚠️ It is a FUNCTION, not a value, and that is load-bearing: both examples\n * build this sink once (`React.useMemo(…, [])`) and it has no React context\n * to subscribe to, so strings captured at construction would freeze in\n * whatever language was active on the first render. Called per toast instead.\n */\n readonly resolveStrings?: () => FriendlyErrorStrings | undefined;\n}\n\nexport function createNotificationProvider(\n options: NotificationProviderOptions = {},\n): NotificationSink {\n return {\n open: ({ type, message, description }) => {\n let text = message;\n let detail = description;\n if (type === 'error') {\n const friendly = toFriendlyError(message, description, options.resolveStrings?.() ?? en);\n text = friendly.title;\n detail = friendly.detail;\n }\n // `info` is a plain notice that dismisses itself — never `toast.loading`,\n // whose spinner stays on screen until someone closes it.\n const fn = type === 'error' ? toast.error : type === 'info' ? toast.info : toast.success;\n fn(text, { description: detail });\n },\n close: (key: string) => {\n toast.dismiss(key);\n },\n };\n}\n","'use client';\nimport { useOptionalT, useRegisterNotificationSink } from '@burojs/react';\nimport { useMemo, useRef } from 'react';\nimport { Toaster as SonnerToaster, type ToasterProps as SonnerProps, toast } from 'sonner';\nimport { friendlyStringsFrom } from '../lib/friendly-error.js';\nimport { useTheme } from '../layout/use-theme.js';\nimport { createNotificationProvider } from './notification-provider.js';\n\nexport interface ToasterProps extends SonnerProps {}\n\n/**\n * Shadcn-canonical Sonner setup, and — inside a `<BuroProvider>` — the app's\n * notification sink: the dispatcher's default `notify` (every handler's\n * `notify`, and a thrown handler's error) is delivered here. Outside a\n * provider it registers nothing and still renders anything sent with `toast()`.\n *\n * Bound to design tokens via `toastOptions.classNames`, so toasts inherit the\n * app's light/dark theme through the same CSS-variable cascade as the rest of\n * the UI; `theme` is reactive via `useTheme()`. The error sentences come from\n * the ACTIVE bundle (`requestError.*`), read per toast so a language switch is\n * honoured; with no i18n provider they are the English defaults.\n */\nexport function Toaster(props: ToasterProps): React.ReactElement {\n // `useOptionalT`: the toaster may sit outside a BuroProvider, so it must\n // degrade rather than throw.\n const t = useOptionalT();\n const { resolved: theme } = useTheme();\n const tRef = useRef(t);\n tRef.current = t;\n const sink = useMemo(\n () => createNotificationProvider({ resolveStrings: () => friendlyStringsFrom(tRef.current) }),\n [],\n );\n useRegisterNotificationSink(sink);\n\n return (\n <SonnerToaster\n /*\n ⚠️ Sonner's default accessible name is \"Notifications alt+T\" and reaches\n the accessibility tree on every page; it is replaced by the bundle's\n `notifications.region`. The literal fallback is English, like every\n framework default.\n */\n containerAriaLabel={t('notifications.region', { defaultValue: 'Notifications' })}\n theme={theme}\n position=\"top-right\"\n closeButton\n className=\"toaster group\"\n toastOptions={{\n classNames: {\n toast:\n 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',\n description: 'group-[.toast]:text-muted-foreground',\n actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',\n cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',\n closeButton:\n 'group-[.toast]:bg-background group-[.toast]:text-muted-foreground group-[.toast]:border-border group-[.toast]:hover:bg-muted group-[.toast]:hover:text-foreground',\n },\n }}\n {...props}\n />\n );\n}\n\nexport { toast };\n"],"mappings":"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"}