@publish.os/nextjs 0.0.1-alpha.8 → 0.0.1-alpha.90

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 (201) hide show
  1. package/compiler-assets/nextjs/defaults/app/globals.css +1 -0
  2. package/compiler-assets/nextjs/defaults/group/index.tsx +50 -0
  3. package/compiler-assets/nextjs/defaults/group/layout.tsx +7 -3
  4. package/compiler-assets/nextjs/defaults/group/page.tsx +35 -43
  5. package/compiler-assets/nextjs/defaults/project/next.config.mjs +22 -11
  6. package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +1 -4
  7. package/compiler-assets/nextjs/defaults/root/css/animation.css +131 -0
  8. package/compiler-assets/nextjs/defaults/root/css/components/dialog.css +4 -1
  9. package/compiler-assets/nextjs/defaults/root/css/components/menu.css +1 -1
  10. package/compiler-assets/nextjs/defaults/root/css/components/navigation-menu.css +1 -1
  11. package/compiler-assets/nextjs/defaults/root/css/components/popover.css +1 -1
  12. package/compiler-assets/nextjs/defaults/root/css/components/select.css +1 -1
  13. package/compiler-assets/nextjs/defaults/root/css/components/tooltip.css +1 -1
  14. package/compiler-assets/nextjs/defaults/root/css/shadow.css +6 -25
  15. package/compiler-assets/nextjs/defaults/root/css/typography.css +51 -68
  16. package/compiler-assets/nextjs/defaults/root/globals.css +25 -0
  17. package/compiler-assets/nextjs/defaults/root/{page.tsx → index.tsx} +6 -2
  18. package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -1
  19. package/compiler-assets/nextjs/defaults/root/theme.css +22 -22
  20. package/compiler-assets/nextjs/shims/generated-group-page.tsx +19 -2
  21. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +16 -2
  22. package/compiler-assets/nextjs/shims/global-layout.tsx +11 -43
  23. package/compiler-assets/nextjs/shims/proxy.ts +21 -9
  24. package/compiler-assets/nextjs/shims/publish-os-theme-provider.tsx +76 -15
  25. package/compiler-assets/nextjs/shims/root-page.tsx +7 -1
  26. package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +34 -0
  27. package/compiler-assets/nextjs/shims/site-heading-level.tsx +14 -0
  28. package/compiler-assets/nextjs/shims/site-mdx-components.tsx +100 -0
  29. package/compiler-assets/nextjs/shims/site-mdx.tsx +12 -0
  30. package/compiler-assets/nextjs/shims/theme-defaults.css +41 -25
  31. package/dist/build-operation-2JAYBSKF.js +83 -0
  32. package/dist/button-CtzUJB8u.d.ts +11 -0
  33. package/dist/{button-CFdx-ohV.d.ts → button-c3WGnxIj.d.ts} +3 -3
  34. package/dist/chunk-535TM4A5.js +1455 -0
  35. package/dist/chunk-726GV2NF.js +2344 -0
  36. package/dist/chunk-7OGTPSKI.js +156 -0
  37. package/dist/chunk-AI2UULNX.js +9 -0
  38. package/dist/chunk-B2V22WRT.js +297 -0
  39. package/dist/chunk-DLDFHU7Y.js +191 -0
  40. package/dist/chunk-DWA3ZRGW.js +142 -0
  41. package/dist/chunk-GGKFQ3MA.js +621 -0
  42. package/dist/chunk-H6XILMZX.js +6154 -0
  43. package/dist/chunk-NECL5IBJ.js +318 -0
  44. package/dist/chunk-SVYGXXYR.js +36 -0
  45. package/dist/chunk-UF6ATARM.js +1100 -0
  46. package/dist/chunk-VO2I7MTM.js +56 -0
  47. package/dist/chunk-WDY4IK6Z.js +128 -0
  48. package/dist/chunk-WKTEO6RS.js +586 -0
  49. package/dist/chunk-YLTOLYWM.js +92 -0
  50. package/dist/compiler.d.ts +41 -7
  51. package/dist/compiler.js +48 -13143
  52. package/dist/compose-NN23UJGC.js +13 -0
  53. package/dist/config/createNextConfig.js +13 -0
  54. package/dist/dev-operation-TZDVJCN7.js +1055 -0
  55. package/dist/framework-selection-X4U6Y6QC.js +10 -0
  56. package/dist/ide-support-ZNW7ODFY.js +15 -0
  57. package/dist/index.d.ts +15 -3
  58. package/dist/kernel/components/blocks/AgentActions.js +71 -61
  59. package/dist/kernel/components/blocks/design-system-preview.d.ts +1 -1
  60. package/dist/kernel/components/blocks/design-system-preview.js +32 -13
  61. package/dist/kernel/components/blocks/link-playground.js +2 -2
  62. package/dist/kernel/components/blocks/logo-block.js +2 -2
  63. package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +10 -2
  64. package/dist/kernel/components/blocks/nav-breadcrumb.js +20 -5
  65. package/dist/kernel/components/blocks/navbar.d.ts +2 -2
  66. package/dist/kernel/components/blocks/navbar.js +401 -60
  67. package/dist/kernel/components/blocks/navigation-progress-playground.js +4 -4
  68. package/dist/kernel/components/blocks/navigation-progress.js +4 -1
  69. package/dist/kernel/components/blocks/next-prev.d.ts +1 -1
  70. package/dist/kernel/components/blocks/next-prev.js +11 -23
  71. package/dist/kernel/components/blocks/page-list.d.ts +1 -1
  72. package/dist/kernel/components/blocks/page-list.js +5 -4
  73. package/dist/kernel/components/blocks/search.d.ts +83 -58
  74. package/dist/kernel/components/blocks/search.js +553 -426
  75. package/dist/kernel/components/blocks/sidebar-node.d.ts +2 -2
  76. package/dist/kernel/components/blocks/sidebar-node.js +11 -9
  77. package/dist/kernel/components/blocks/sidebar.d.ts +3 -3
  78. package/dist/kernel/components/blocks/sidebar.js +18 -11
  79. package/dist/kernel/components/blocks/theme-toggle.d.ts +2 -2
  80. package/dist/kernel/components/blocks/theme-toggle.js +48 -2
  81. package/dist/kernel/components/blocks/toc.js +3 -3
  82. package/dist/kernel/components/mdx/components/blockquote.js +1 -1
  83. package/dist/kernel/components/mdx/components/callout.d.ts +5 -7
  84. package/dist/kernel/components/mdx/components/callout.js +66 -35
  85. package/dist/kernel/components/mdx/components/code-block.d.ts +5 -2
  86. package/dist/kernel/components/mdx/components/code-block.js +106 -5
  87. package/dist/kernel/components/mdx/components/copy-code-button.d.ts +8 -0
  88. package/dist/kernel/components/mdx/components/copy-code-button.js +60 -0
  89. package/dist/kernel/components/mdx/components/figure-caption.js +1 -1
  90. package/dist/kernel/components/mdx/components/figure.js +1 -1
  91. package/dist/kernel/components/mdx/components/heading-copy-link.js +3 -3
  92. package/dist/kernel/components/mdx/components/image-zoom.d.ts +2 -2
  93. package/dist/kernel/components/mdx/components/image-zoom.js +1 -1
  94. package/dist/kernel/components/mdx/components/image.js +77 -56
  95. package/dist/kernel/components/mdx/components/inline-code.js +1 -1
  96. package/dist/kernel/components/mdx/components/kbd.d.ts +2 -2
  97. package/dist/kernel/components/mdx/components/kbd.js +3 -2
  98. package/dist/kernel/components/mdx/components/link.d.ts +35 -4
  99. package/dist/kernel/components/mdx/components/link.js +1 -1
  100. package/dist/kernel/components/mdx/components/paragraph.js +1 -4
  101. package/dist/kernel/components/mdx/components/task-list.js +1 -1
  102. package/dist/kernel/components/mdx/components/video-player.js +2 -2
  103. package/dist/kernel/components/mdx/components/video.js +1 -1
  104. package/dist/kernel/components/mdx/error-boundary.d.ts +1 -1
  105. package/dist/kernel/components/mdx/mdx.d.ts +12 -8
  106. package/dist/kernel/components/mdx/mdx.js +115 -39
  107. package/dist/kernel/components/mdx/rehype-heading-ids.js +1 -1
  108. package/dist/kernel/components/mdx/types.d.ts +1 -1
  109. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +6 -23
  110. package/dist/kernel/components/ui/breadcrumb.d.ts +11 -3
  111. package/dist/kernel/components/ui/breadcrumb.js +6 -4
  112. package/dist/kernel/components/ui/button-group.d.ts +1 -1
  113. package/dist/kernel/components/ui/button-group.js +5 -4
  114. package/dist/kernel/components/ui/button.d.ts +2 -2
  115. package/dist/kernel/components/ui/button.js +6 -6
  116. package/dist/kernel/components/ui/card.d.ts +2 -2
  117. package/dist/kernel/components/ui/drawer.d.ts +3 -1
  118. package/dist/kernel/components/ui/drawer.js +5 -1
  119. package/dist/kernel/components/ui/input.d.ts +4 -4
  120. package/dist/kernel/components/ui/kbd.d.ts +1 -1
  121. package/dist/kernel/components/ui/kbd.js +9 -16
  122. package/dist/kernel/components/ui/link.d.ts +5 -3
  123. package/dist/kernel/components/ui/link.js +10 -9
  124. package/dist/kernel/components/ui/menu.js +5 -5
  125. package/dist/kernel/components/ui/mod-key.d.ts +10 -0
  126. package/dist/kernel/components/ui/mod-key.js +36 -0
  127. package/dist/kernel/components/ui/navigation-menu.js +2 -2
  128. package/dist/kernel/components/ui/progress.js +1 -1
  129. package/dist/kernel/components/ui/scroll-area.d.ts +8 -8
  130. package/dist/kernel/components/ui/scroll-area.js +4 -4
  131. package/dist/kernel/components/ui/select.js +7 -10
  132. package/dist/kernel/components/ui/text.d.ts +2 -2
  133. package/dist/kernel/components/ui/theme-toggle.d.ts +3 -3
  134. package/dist/kernel/components/ui/theme-toggle.js +4 -9
  135. package/dist/kernel/defaults.registry.d.ts +9 -9
  136. package/dist/kernel/defaults.registry.js +9 -9
  137. package/dist/kernel/routes/agent/markdown.d.ts +0 -1
  138. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  139. package/dist/kernel/routes/metadata/feed.d.ts +1 -1
  140. package/dist/kernel/routes/metadata/feed.js +13 -9
  141. package/dist/kernel/routes/metadata/llms.d.ts +2 -1
  142. package/dist/kernel/routes/metadata/llms.js +4 -4
  143. package/dist/kernel/routes/metadata/robots.d.ts +1 -1
  144. package/dist/kernel/routes/metadata/robots.js +2 -2
  145. package/dist/kernel/routes/metadata/sitemap.d.ts +6 -3
  146. package/dist/kernel/routes/metadata/sitemap.js +48 -20
  147. package/dist/kernel/routes/pages/group.d.ts +1 -1
  148. package/dist/kernel/routes/pages/group.js +20 -12
  149. package/dist/kernel/routes/pages/leaf.d.ts +2 -2
  150. package/dist/kernel/routes/pages/leaf.js +20 -12
  151. package/dist/kernel/routes/pages/root.d.ts +2 -2
  152. package/dist/kernel/routes/pages/root.js +18 -12
  153. package/dist/resolvers/manifest-resolver.js +29 -6
  154. package/dist/search.d.ts +6 -7
  155. package/dist/search.js +47 -22
  156. package/dist/{sidebar-types-Bgchdfd6.d.ts → sidebar-types-DKpR27II.d.ts} +1 -1
  157. package/dist/{types-CIwwieNw.d.ts → types-BzkZty3r.d.ts} +23 -3
  158. package/dist/{types-DHBm7-j6.d.ts → types-CJWRG05y.d.ts} +20 -1
  159. package/dist/{types-B_b3C0U6.d.ts → types-CUtrppxt.d.ts} +8 -2
  160. package/dist/types.d.ts +14 -6
  161. package/dist/validate-local-source-XRL2Y7XE.js +14 -0
  162. package/dist/validate-publish-os-source-DAPZ4QGE.js +12 -0
  163. package/init/seed/minimal/.gitignore +1 -0
  164. package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
  165. package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
  166. package/init/seed/minimal/publish.os/docs/content/get-started.mdx +6 -10
  167. package/init/seed/minimal/publish.os/docs/content/groups.mdx +1 -1
  168. package/init/seed/minimal/publish.os/docs/content/{theme.mdx → themes.mdx} +14 -12
  169. package/init/seed/minimal/publish.os/publish.os.config.json +3 -7
  170. package/init/seed/minimal/publish.os/theme.css +52 -24
  171. package/init/seed/minimal/publish.os/tsconfig.json +3 -0
  172. package/init/seed/minimal/seed.json +4 -1
  173. package/init/seed/scratch/publish.os/docs/content/get-started.mdx +6 -0
  174. package/init/seed/scratch/publish.os/publish.os.config.json +4 -0
  175. package/init/seed/scratch/seed.json +5 -0
  176. package/package.json +12 -26
  177. package/release/app/phosphor/package.json +3 -3
  178. package/release/app/phosphor/pnpm-lock.yaml.template +523 -360
  179. package/release/app/phosphor/pnpm-workspace.yaml +3 -0
  180. package/release/compiler/package.json +1 -1
  181. package/release/compiler/pnpm-lock.yaml.template +439 -286
  182. package/release/compiler/pnpm-workspace.yaml +3 -0
  183. package/release/release.json +7 -36
  184. package/compiler-assets/nextjs/defaults/group/leaf.tsx +0 -48
  185. package/dist/icons.d.ts +0 -1
  186. package/dist/icons.js +0 -2
  187. package/dist/kernel/components/mdx/components/pre.d.ts +0 -6
  188. package/dist/kernel/components/mdx/components/pre.js +0 -18
  189. package/dist/kernel/components/ui/meta-key.d.ts +0 -17
  190. package/dist/kernel/components/ui/meta-key.js +0 -9
  191. package/dist/kernel/framework-icons.d.ts +0 -1
  192. package/dist/kernel/framework-icons.js +0 -39
  193. package/dist/link-CYaCRDb6.d.ts +0 -35
  194. package/dist/route-metadata-og-image-BeKKFHg7.d.ts +0 -58
  195. package/dist/types-sM2g9nW0.d.ts +0 -12
  196. package/release/app/lucide/package.json +0 -30
  197. package/release/app/lucide/pnpm-lock.yaml.template +0 -2905
  198. package/release/app/lucide/pnpm-workspace.yaml +0 -11
  199. package/release/app/tabler/package.json +0 -30
  200. package/release/app/tabler/pnpm-lock.yaml.template +0 -2911
  201. package/release/app/tabler/pnpm-workspace.yaml +0 -11
@@ -1,418 +1,237 @@
1
1
  "use client";
2
2
 
3
- // src/components/block/search.tsx
4
- import { buttonVariants } from "@publish.os/nextjs/kernel/components/ui/button";
3
+ // src/modules/search/search-block.tsx
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import { Button } from "@publish.os/nextjs/kernel/components/ui/button";
5
6
  import {
6
7
  Dialog,
7
8
  DialogBackdrop,
8
- DialogClose,
9
9
  DialogDescription,
10
10
  DialogPopup,
11
11
  DialogPortal,
12
- DialogTitle,
13
- DialogTrigger
12
+ DialogTitle
14
13
  } from "@publish.os/nextjs/kernel/components/ui/dialog";
15
- import { Input } from "@publish.os/nextjs/kernel/components/ui/input";
16
- import { Kbd } from "@publish.os/nextjs/kernel/components/ui/kbd";
17
14
  import {
18
- Progress,
19
- ProgressIndicator,
20
- ProgressTrack
21
- } from "@publish.os/nextjs/kernel/components/ui/progress";
22
- import { ScrollArea } from "@publish.os/nextjs/kernel/components/ui/scroll-area";
23
- import { FrameworkSearchIcon as SearchIcon } from "@publish.os/nextjs/kernel/framework-icons";
15
+ Drawer,
16
+ DrawerBackdrop,
17
+ DrawerDescription,
18
+ DrawerPopup,
19
+ DrawerPortal,
20
+ DrawerTitle,
21
+ DrawerViewport,
22
+ DrawerVirtualKeyboardProvider
23
+ } from "@publish.os/nextjs/kernel/components/ui/drawer";
24
+ import { Input } from "@publish.os/nextjs/kernel/components/ui/input";
25
+ import { Kbd as Kbd2, kbdVariants } from "@publish.os/nextjs/kernel/components/ui/kbd";
26
+ import { ModKey } from "@publish.os/nextjs/kernel/components/ui/mod-key";
27
+ import { Separator as Separator2 } from "@publish.os/nextjs/kernel/components/ui/separator";
28
+ import { MagnifyingGlassIcon } from "@phosphor-icons/react/ssr";
24
29
  import { formatShortcut } from "@publish.os/nextjs/keyboard-shortcuts";
25
30
  import {
26
- SearchProvider,
27
- useSearchBlock
31
+ SearchProvider as Root,
32
+ useSearchBlock as useSearchBlock3
28
33
  } from "@publish.os/nextjs/search";
29
- import { cn } from "@publish.os/nextjs/utils/cn";
30
- import NextLink from "next/link";
34
+ import { cn as cn3 } from "@publish.os/nextjs/utils/cn";
31
35
  import {
32
- cloneElement,
33
- useCallback,
34
- useEffect,
35
- useMemo,
36
- useRef,
37
- useState
36
+ useEffect as useEffect2,
37
+ useState as useState2
38
38
  } from "react";
39
+
40
+ // src/modules/search/results.tsx
41
+ import { Kbd } from "@publish.os/nextjs/kernel/components/ui/kbd";
42
+ import {
43
+ ScrollAreaContent,
44
+ ScrollAreaRoot,
45
+ ScrollAreaScrollbar,
46
+ ScrollAreaThumb,
47
+ ScrollAreaViewport
48
+ } from "@publish.os/nextjs/kernel/components/ui/scroll-area";
49
+ import { Separator } from "@publish.os/nextjs/kernel/components/ui/separator";
50
+ import { ArrowElbowDownLeftIcon } from "@phosphor-icons/react/ssr";
51
+ import { useSearchBlock } from "@publish.os/nextjs/search";
52
+ import { cn } from "@publish.os/nextjs/utils/cn";
53
+ import NextLink from "next/link";
54
+ import { useEffect } from "react";
39
55
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
40
- var SEARCH_ROW_HEIGHT = 58, SEARCH_ROW_WITH_DESCRIPTION_HEIGHT = 72, SEARCH_RESULTS_PADDING = 16, SEARCH_RESULTS_MAX_HEIGHT = 360, SEARCH_RESULT_SNIPPET_LENGTH = 180, searchProgressConfig = {
41
- completeDelay: 100,
42
- exitDuration: 150,
43
- fastPhaseEnd: 25,
44
- initialProgress: 5,
45
- maxDuration: 5e3,
46
- minDuration: 50,
47
- targetProgress: 95
48
- };
49
- function SearchProgressBar({ className }) {
50
- let {
51
- state: { isTyping, loading, status }
52
- } = useSearchBlock(), [progress, setProgress] = useState(0), [isExiting, setIsExiting] = useState(!1), animationFrame = useRef(null), cleanupTimer = useRef(null), progressRef = useRef(0), active = loading || status === "building" || status === "stale", setProgressValue = useCallback((value) => {
53
- progressRef.current = value, setProgress(value);
54
- }, []), cleanup = useCallback(() => {
55
- animationFrame.current !== null && (window.cancelAnimationFrame(animationFrame.current), animationFrame.current = null), cleanupTimer.current && (window.clearTimeout(cleanupTimer.current), cleanupTimer.current = null);
56
- }, []);
57
- useEffect(() => {
58
- if (isTyping) {
59
- cleanup(), setProgressValue(0), setIsExiting(!1);
60
- return;
61
- }
62
- if (!active) {
63
- progressRef.current > 0 && (cleanup(), setProgressValue(100), cleanupTimer.current = window.setTimeout(() => {
64
- setIsExiting(!0), cleanupTimer.current = window.setTimeout(() => {
65
- setProgressValue(0), setIsExiting(!1), cleanupTimer.current = null;
66
- }, searchProgressConfig.exitDuration);
67
- }, searchProgressConfig.completeDelay));
68
- return;
69
- }
70
- cleanup(), setIsExiting(!1), setProgressValue(searchProgressConfig.initialProgress);
71
- let start = performance.now(), animate = (now) => {
72
- let elapsed = now - start, progressValue;
73
- if (elapsed < searchProgressConfig.minDuration)
74
- progressValue = searchProgressConfig.initialProgress + elapsed / searchProgressConfig.minDuration * (searchProgressConfig.fastPhaseEnd - searchProgressConfig.initialProgress);
75
- else {
76
- let remainingProgress = searchProgressConfig.targetProgress - searchProgressConfig.fastPhaseEnd, easedTime = 1 - (1 - Math.min(
77
- 1,
78
- (elapsed - searchProgressConfig.minDuration) / (searchProgressConfig.maxDuration - searchProgressConfig.minDuration)
79
- )) ** 3;
80
- progressValue = searchProgressConfig.fastPhaseEnd + easedTime * remainingProgress;
81
- }
82
- setProgressValue(
83
- Math.min(searchProgressConfig.targetProgress, progressValue)
84
- ), animationFrame.current = window.requestAnimationFrame(animate);
85
- };
86
- return animationFrame.current = window.requestAnimationFrame(animate), cleanup;
87
- }, [active, cleanup, isTyping, setProgressValue]);
88
- let isVisible = progress > 0;
89
- return /* @__PURE__ */ jsx(
90
- Progress,
91
- {
92
- "aria-hidden": !isVisible,
93
- className: cn(
94
- "pointer-events-none h-[1.5px] overflow-hidden opacity-75 transition-opacity duration-300",
95
- isVisible ? "opacity-100" : "opacity-0",
96
- className
97
- ),
98
- value: isVisible ? progress : null,
99
- children: /* @__PURE__ */ jsx(ProgressTrack, { className: "h-full rounded-none bg-transparent", children: /* @__PURE__ */ jsx(
100
- ProgressIndicator,
101
- {
102
- className: "h-full w-full rounded-none bg-accent-1",
103
- style: {
104
- transform: isExiting ? "scaleX(0)" : `scaleX(${Math.max(0, Math.min(100, progress)) / 100})`,
105
- transformOrigin: isExiting ? "right" : "left",
106
- transition: isExiting ? `transform ${searchProgressConfig.exitDuration}ms cubic-bezier(.18,.66,.89,.35)` : "transform 200ms cubic-bezier(.04,.06,0,.61)"
107
- }
108
- }
109
- ) })
110
- }
111
- );
112
- }
113
- function RootDialogFrame({
114
- children,
115
- className
56
+ var SEARCH_RESULT_SNIPPET_LENGTH = 180;
57
+ function getSearchPanel({
58
+ displayResults,
59
+ isTyping,
60
+ loading,
61
+ query,
62
+ status
116
63
  }) {
117
- let {
118
- actions: { setOpen },
119
- meta: { variant },
120
- state: { open }
121
- } = useSearchBlock();
122
- return /* @__PURE__ */ jsx(Dialog, { onOpenChange: setOpen, open, children: /* @__PURE__ */ jsx("div", { className, "data-search-root": "", "data-variant": variant, children }) });
123
- }
124
- function Root({ children, className, ...providerProps }) {
125
- return /* @__PURE__ */ jsx(SearchProvider, { ...providerProps, children: /* @__PURE__ */ jsx(RootDialogFrame, { className, children }) });
64
+ return query.trim() ? displayResults.length > 0 ? "results" : isTyping || loading ? null : status === "ready" ? "empty" : status === "building" || status === "stale" || status === "error" ? "status" : null : null;
126
65
  }
127
- function Trigger({
128
- asChild = !1,
66
+ function SearchResults({
129
67
  children,
130
68
  className,
131
- icon,
132
- placeholder = "Search...",
133
- shortcut,
134
- shortcutClassName,
135
- showIcon = !0,
136
- showShortcut = !0,
137
- size = "sm",
138
- variant = "outline"
69
+ emptyText = "No results found.",
70
+ itemClassName,
71
+ ...props
139
72
  }) {
140
73
  let {
141
- meta: { inputId, shortcut: registeredShortcut },
142
- state: { open }
143
- } = useSearchBlock(), visibleShortcut = shortcut ?? registeredShortcut, shortcutTokens = useMemo(
144
- () => formatShortcut(visibleShortcut),
145
- [visibleShortcut]
146
- );
147
- return asChild && children ? /* @__PURE__ */ jsx(
148
- DialogTrigger,
149
- {
150
- render: cloneElement(children, {
151
- "aria-controls": inputId,
152
- "aria-expanded": open,
153
- className: cn(children.props.className, className)
154
- })
155
- }
156
- ) : /* @__PURE__ */ jsxs(
157
- DialogTrigger,
74
+ actions: { navigateToResult, setOpen, setSelectedValue },
75
+ meta: { listId, listRef },
76
+ state
77
+ } = useSearchBlock(), {
78
+ displayResults,
79
+ isTyping,
80
+ loading,
81
+ message,
82
+ query,
83
+ selectedValue,
84
+ status
85
+ } = state, hasQuery = query.trim().length > 0, panel = getSearchPanel(state), showResults = panel === "results", settled = hasQuery && !isTyping && !loading, showStatusPanel = panel === "status", showEmptyPanel = panel === "empty";
86
+ return useEffect(() => {
87
+ let list = listRef.current;
88
+ if (!list || !selectedValue || !showResults) return;
89
+ let win = list.ownerDocument.defaultView;
90
+ if (!win) return;
91
+ let frame = win.requestAnimationFrame(() => {
92
+ let index = selectedIndex(displayResults, selectedValue);
93
+ list.ownerDocument.getElementById(resultOptionId(listId, index))?.scrollIntoView({ block: "nearest" });
94
+ });
95
+ return () => win.cancelAnimationFrame(frame);
96
+ }, [displayResults, listId, listRef, selectedValue, showResults]), panel ? /* @__PURE__ */ jsxs(
97
+ ScrollAreaRoot,
158
98
  {
159
- "aria-controls": inputId,
160
- "aria-expanded": open,
161
- "aria-label": "Open search",
162
99
  className: cn(
163
- buttonVariants({ size, variant }),
164
- "justify-start gap-2 px-2.5 text-foreground-3",
165
- "hover:text-foreground-1",
100
+ "flex min-h-0 flex-1 flex-col bg-transparent md:max-h-90",
166
101
  className
167
102
  ),
103
+ ...props,
168
104
  children: [
169
- showIcon ? icon ?? /* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": "true", size: 16 }) : null,
170
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: placeholder }),
171
- showShortcut ? /* @__PURE__ */ jsx(
172
- "span",
173
- {
174
- "aria-hidden": "true",
175
- className: cn(
176
- "hidden shrink-0 items-center gap-1 md:inline-flex",
177
- shortcutClassName
178
- ),
179
- children: shortcutTokens.map((token) => /* @__PURE__ */ jsx(Kbd, { size: "xs", children: token }, token))
180
- }
181
- ) : null
182
- ]
183
- }
184
- );
185
- }
186
- function DialogContent({
187
- children,
188
- className,
189
- description = "Search this site and navigate to a result.",
190
- title = "Search"
191
- }) {
192
- let {
193
- meta: { variant }
194
- } = useSearchBlock();
195
- return /* @__PURE__ */ jsxs(DialogPortal, { children: [
196
- /* @__PURE__ */ jsx(DialogBackdrop, { className: "bg-backdrop backdrop-blur-[2px]" }),
197
- /* @__PURE__ */ jsxs(
198
- DialogPopup,
199
- {
200
- className: cn(
201
- "top-[12dvh] flex max-h-[min(42rem,82dvh)] w-[min(92vw,575px)] translate-y-0 flex-col overflow-hidden p-0 shadow-xl",
202
- variant === "mobile" && "bottom-auto left-1/2 top-[8dvh] h-auto max-h-[86dvh] w-[min(94vw,575px)] max-w-none -translate-x-1/2 translate-y-0",
203
- className
204
- ),
205
- children: [
206
- /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: title }),
207
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: description }),
208
- children
209
- ]
210
- }
211
- )
212
- ] });
213
- }
214
- function SearchInputField({
215
- className,
216
- closeClassName,
217
- closeLabel = "ESC",
218
- placeholder = "Search docs...",
219
- rootClassName,
220
- showCloseShortcut = !0
221
- }) {
222
- let {
223
- actions: { handleInputChange, navigateToResult, setOpen, setSelectedValue },
224
- meta: { inputId, inputRef, listId },
225
- state: { displayResults, query, selectedValue }
226
- } = useSearchBlock(), closeDisplayLabel = typeof closeLabel == "string" ? closeLabel.toUpperCase() : closeLabel, selectedIndex2 = Math.max(
227
- 0,
228
- displayResults.findIndex((result) => result.id === selectedValue)
229
- ), selectedOptionId = displayResults.length > 0 ? resultOptionId(listId, selectedIndex2) : void 0, handleKeyDown = (event) => {
230
- if (event.key === "Escape") {
231
- event.preventDefault(), setOpen(!1);
232
- return;
233
- }
234
- if (event.key === "ArrowDown") {
235
- event.preventDefault();
236
- let next = displayResults[Math.min(selectedIndex2 + 1, displayResults.length - 1)];
237
- next && setSelectedValue(next.id);
238
- return;
239
- }
240
- if (event.key === "ArrowUp") {
241
- event.preventDefault();
242
- let next = displayResults[Math.max(selectedIndex2 - 1, 0)];
243
- next && setSelectedValue(next.id);
244
- return;
245
- }
246
- if (event.key === "Enter") {
247
- let selected = displayResults[selectedIndex2];
248
- if (!selected) return;
249
- event.preventDefault(), navigateToResult(selected);
250
- }
251
- };
252
- return /* @__PURE__ */ jsxs(
253
- "div",
254
- {
255
- className: cn("relative rounded-t-[inherit] bg-surface-1", rootClassName),
256
- children: [
105
+ /* @__PURE__ */ jsx(Separator, { className: "absolute inset-x-0 top-0 z-content hidden md:block group-data-[search-presentation=drawer]/search:hidden group-data-[search-presentation=dialog]/search:block" }),
106
+ /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "sr-only", children: settled && showResults ? `${displayResults.length} search ${displayResults.length === 1 ? "result" : "results"}` : "" }),
107
+ /* @__PURE__ */ jsx(ScrollAreaViewport, { className: "min-h-0 h-auto max-h-[inherit] flex-1 overscroll-contain", children: /* @__PURE__ */ jsxs(ScrollAreaContent, { children: [
108
+ showStatusPanel ? /* @__PURE__ */ jsx(
109
+ "div",
110
+ {
111
+ "aria-live": "polite",
112
+ className: "flex min-h-36 items-center justify-center px-6 py-10 text-center text-sm",
113
+ children: /* @__PURE__ */ jsx("p", { className: "max-w-full text-foreground-2", children: message ?? statusLabel(status) })
114
+ }
115
+ ) : null,
116
+ showEmptyPanel ? /* @__PURE__ */ jsx(
117
+ "div",
118
+ {
119
+ "aria-live": "polite",
120
+ className: "flex min-h-36 items-center justify-center px-6 py-10 text-center text-sm",
121
+ children: /* @__PURE__ */ jsxs("p", { className: "min-w-0 max-w-full text-foreground-2", children: [
122
+ emptyText.replace(/\.$/, ""),
123
+ " for",
124
+ " ",
125
+ /* @__PURE__ */ jsxs("span", { className: "inline-block max-w-full truncate align-bottom font-medium text-foreground-1", children: [
126
+ '"',
127
+ query.trim(),
128
+ '"'
129
+ ] })
130
+ ] })
131
+ }
132
+ ) : null,
133
+ /* @__PURE__ */ jsx(
134
+ "div",
135
+ {
136
+ "aria-label": "Search results",
137
+ className: showResults ? "space-y-1 p-2" : void 0,
138
+ id: listId,
139
+ ref: listRef,
140
+ role: "listbox",
141
+ children: showResults ? displayResults.map((result, index) => {
142
+ let snippet = resultSnippet({ query, result });
143
+ return /* @__PURE__ */ jsx(
144
+ NextLink,
145
+ {
146
+ "aria-describedby": !children && snippet ? `${resultOptionId(listId, index)}-snippet` : void 0,
147
+ "aria-labelledby": children ? void 0 : `${resultOptionId(listId, index)}-title`,
148
+ "aria-selected": result.id === selectedValue,
149
+ className: cn(
150
+ "block min-h-14.5 min-w-0 overflow-hidden rounded-button px-3 py-3 text-start",
151
+ "focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-focus-stroke",
152
+ result.id === selectedValue ? "bg-surface-active text-foreground-1" : "text-foreground-2",
153
+ itemClassName
154
+ ),
155
+ href: result.url,
156
+ id: resultOptionId(listId, index),
157
+ onClick: () => setOpen(!1),
158
+ onKeyDown: (event) => {
159
+ event.key !== "Enter" || event.nativeEvent.isComposing || (event.preventDefault(), navigateToResult(result));
160
+ },
161
+ onMouseMove: () => {
162
+ selectedValue !== result.id && setSelectedValue(result.id);
163
+ },
164
+ role: "option",
165
+ tabIndex: -1,
166
+ children: children ? children(result) : /* @__PURE__ */ jsxs(Fragment, { children: [
167
+ /* @__PURE__ */ jsx(
168
+ "span",
169
+ {
170
+ className: "line-clamp-2 wrap-break-word text-base leading-6 font-medium text-foreground-1 md:text-sm md:leading-5",
171
+ id: `${resultOptionId(listId, index)}-title`,
172
+ children: /* @__PURE__ */ jsx(
173
+ HighlightedText,
174
+ {
175
+ text: result.title,
176
+ query
177
+ }
178
+ )
179
+ }
180
+ ),
181
+ snippet ? /* @__PURE__ */ jsx(
182
+ "span",
183
+ {
184
+ className: "mt-1.5 line-clamp-2 wrap-break-word text-sm leading-5 text-foreground-2",
185
+ id: `${resultOptionId(listId, index)}-snippet`,
186
+ children: /* @__PURE__ */ jsx(HighlightedText, { text: snippet, query })
187
+ }
188
+ ) : null
189
+ ] })
190
+ },
191
+ result.id
192
+ );
193
+ }) : null
194
+ }
195
+ )
196
+ ] }) }),
257
197
  /* @__PURE__ */ jsx(
258
- Input,
259
- {
260
- "aria-activedescendant": selectedOptionId,
261
- "aria-autocomplete": "list",
262
- "aria-controls": listId,
263
- "aria-expanded": displayResults.length > 0,
264
- autoComplete: "off",
265
- className: cn(
266
- "supports-[caret-color:var(--accent-1)]:caret-(--accent-1)",
267
- "h-12 rounded-none border-0 bg-transparent px-4 pr-16 text-sm shadow-none outline-none focus:border-transparent focus-visible:outline-0",
268
- className
269
- ),
270
- id: inputId,
271
- onChange: (event) => handleInputChange(event.currentTarget.value),
272
- onKeyDown: handleKeyDown,
273
- placeholder,
274
- ref: inputRef,
275
- role: "combobox",
276
- spellCheck: !1,
277
- type: "text",
278
- value: query
279
- }
280
- ),
281
- showCloseShortcut ? /* @__PURE__ */ jsx(
282
- DialogClose,
198
+ ScrollAreaScrollbar,
283
199
  {
284
- "aria-label": "Close search",
285
- className: cn(
286
- "absolute right-3 top-1/2 size-auto -translate-y-1/2 bg-transparent p-0 text-foreground-3 hover:bg-transparent hover:text-foreground-1",
287
- closeClassName
288
- ),
289
- children: /* @__PURE__ */ jsx(Kbd, { className: "text-foreground-2", size: "sm", children: closeDisplayLabel })
200
+ className: "absolute inset-y-2 right-0 w-1.5 rounded-scrollbar-thumb bg-transparent p-0",
201
+ orientation: "vertical",
202
+ children: /* @__PURE__ */ jsx(ScrollAreaThumb, {})
290
203
  }
291
- ) : null
204
+ )
292
205
  ]
293
206
  }
294
- );
207
+ ) : null;
295
208
  }
296
- function SearchResults({
209
+ function SearchFooter({
210
+ children,
297
211
  className,
298
- emptyText = "No results found."
212
+ ...props
299
213
  }) {
300
214
  let {
301
- actions: { navigateToResult, setOpen, setSelectedValue },
302
- meta: { listId, listRef },
303
- state: {
304
- displayResults,
305
- isTyping,
306
- loading,
307
- message,
308
- query,
309
- selectedValue,
310
- status
311
- }
312
- } = useSearchBlock(), hasQuery = query.trim().length > 0, showResults = displayResults.length > 0, statusMessage = message ?? statusLabel(status), showStatusPanel = hasQuery && !isTyping && !loading && !showResults && (status === "building" || status === "stale" || status === "error"), showEmptyPanel = hasQuery && !isTyping && !loading && !showResults && !showStatusPanel && status === "ready", showPanel = showResults || showStatusPanel || showEmptyPanel, resultRowHeight = displayResults.some(
313
- (result) => resultSnippet({ query, result })
314
- ) ? SEARCH_ROW_WITH_DESCRIPTION_HEIGHT : SEARCH_ROW_HEIGHT, fullResultsHeight = displayResults.length * resultRowHeight + SEARCH_RESULTS_PADDING, resultsAreScrollable = fullResultsHeight > SEARCH_RESULTS_MAX_HEIGHT, resultsHeight = Math.min(fullResultsHeight, SEARCH_RESULTS_MAX_HEIGHT), panelHeight = showResults ? resultsHeight : showStatusPanel || showEmptyPanel ? 144 : 0, containerDuration = showResults ? Math.min(320, 150 + displayResults.length * 18) : 180;
315
- return useEffect(() => {
316
- if (!selectedValue || !showResults) return;
317
- let frame = window.requestAnimationFrame(() => {
318
- let index = selectedIndex(displayResults, selectedValue);
319
- document.getElementById(resultOptionId(listId, index))?.scrollIntoView({ block: "nearest" });
320
- });
321
- return () => window.cancelAnimationFrame(frame);
322
- }, [displayResults, listId, selectedValue, showResults]), /* @__PURE__ */ jsx(
215
+ state: { displayResults, isTyping, loading, query, status }
216
+ } = useSearchBlock(), hasResults = displayResults.length > 0, empty = !isTyping && !loading && status === "ready";
217
+ return !query.trim() || !hasResults && !empty ? null : /* @__PURE__ */ jsx(
323
218
  "div",
324
219
  {
325
220
  className: cn(
326
- "relative grid rounded-b-[inherit] bg-surface-1 transition-[grid-template-rows,opacity] ease-[cubic-bezier(0.4,0,0.2,1)] before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:z-content before:h-px before:bg-stroke-3 before:transition-opacity motion-reduce:transition-none",
327
- showPanel ? "opacity-100" : "opacity-0",
328
- showPanel ? "before:opacity-100" : "before:opacity-0",
221
+ "flex shrink-0 items-center gap-2 border-t border-stroke-3 px-4 py-3 text-xs text-foreground-3",
329
222
  className
330
223
  ),
331
- style: {
332
- gridTemplateRows: `${panelHeight}px`,
333
- transitionDuration: `${containerDuration}ms`
334
- },
335
- children: /* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-hidden", children: [
336
- showStatusPanel ? /* @__PURE__ */ jsx(
337
- "div",
338
- {
339
- "aria-live": "polite",
340
- className: "flex h-full items-center justify-center px-6 py-10 text-center text-sm",
341
- children: /* @__PURE__ */ jsx("p", { className: "max-w-full text-foreground-2", children: statusMessage })
342
- }
343
- ) : null,
344
- showEmptyPanel ? /* @__PURE__ */ jsx(
345
- "div",
346
- {
347
- "aria-live": "polite",
348
- className: "flex h-full items-center justify-center px-6 py-10 text-center text-sm",
349
- children: /* @__PURE__ */ jsxs("p", { className: "max-w-full text-base text-foreground-2", children: [
350
- emptyText.replace(/\.$/, ""),
351
- " for",
352
- " ",
353
- /* @__PURE__ */ jsxs("span", { className: "inline-block max-w-full truncate align-bottom font-medium text-foreground-1", children: [
354
- '"',
355
- query.trim(),
356
- '"'
357
- ] })
358
- ] })
359
- }
360
- ) : null,
361
- showResults ? /* @__PURE__ */ jsx("div", { className: "h-full", children: /* @__PURE__ */ jsx(
362
- ScrollArea,
363
- {
364
- className: "h-full",
365
- mask: resultsAreScrollable ? { height: 40, triggerSize: 32 } : !1,
366
- scrollbar: resultsAreScrollable,
367
- variant: "gradient",
368
- children: /* @__PURE__ */ jsx(
369
- "div",
370
- {
371
- "aria-label": "Search results",
372
- className: "p-2",
373
- id: listId,
374
- ref: listRef,
375
- role: "listbox",
376
- children: displayResults.map((result, index) => {
377
- let snippet = resultSnippet({ query, result });
378
- return /* @__PURE__ */ jsxs(
379
- NextLink,
380
- {
381
- "aria-selected": result.id === selectedValue,
382
- className: cn(
383
- "block min-h-[58px] min-w-0 overflow-hidden rounded-[max(calc(var(--radius-dialog)_-_0.25rem),0px)] px-3 py-2 text-left transition-colors",
384
- "focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-focus-stroke",
385
- result.id === selectedValue ? "bg-surface-active text-foreground-1" : "text-foreground-2"
386
- ),
387
- href: result.url,
388
- id: resultOptionId(listId, index),
389
- onClick: () => setOpen(!1),
390
- onKeyDown: (event) => {
391
- event.key === "Enter" && (event.preventDefault(), navigateToResult(result));
392
- },
393
- onMouseEnter: () => setSelectedValue(result.id),
394
- onMouseMove: () => setSelectedValue(result.id),
395
- role: "option",
396
- children: [
397
- /* @__PURE__ */ jsx("span", { className: "block truncate break-all text-xs leading-4 text-foreground-3", children: /* @__PURE__ */ jsx(
398
- HighlightedText,
399
- {
400
- text: resultSubtitle(result),
401
- query
402
- }
403
- ) }),
404
- /* @__PURE__ */ jsx("span", { className: "line-clamp-1 break-all text-sm font-medium", children: /* @__PURE__ */ jsx(HighlightedText, { text: result.title, query }) }),
405
- snippet ? /* @__PURE__ */ jsx("span", { className: "mt-1 line-clamp-2 break-words text-xs leading-4 text-foreground-3", children: /* @__PURE__ */ jsx(HighlightedText, { text: snippet, query }) }) : null
406
- ]
407
- },
408
- result.id
409
- );
410
- })
411
- }
412
- )
413
- }
414
- ) }) : null
415
- ] })
224
+ ...props,
225
+ children: children === void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
226
+ /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-2 md:inline-flex", children: [
227
+ /* @__PURE__ */ jsx(Kbd, { size: "xs", square: hasResults, children: hasResults ? /* @__PURE__ */ jsxs(Fragment, { children: [
228
+ /* @__PURE__ */ jsx(ArrowElbowDownLeftIcon, { "aria-hidden": "true", size: 12 }),
229
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Enter" })
230
+ ] }) : "Esc" }),
231
+ /* @__PURE__ */ jsx("span", { children: hasResults ? "Go to page" : "Close search" })
232
+ ] }),
233
+ /* @__PURE__ */ jsx("span", { className: "md:hidden", children: hasResults ? "Tap a result to open" : "Swipe down to close" })
234
+ ] }) : children
416
235
  }
417
236
  );
418
237
  }
@@ -428,22 +247,9 @@ function selectedIndex(results, selectedValue) {
428
247
  results.findIndex((result) => result.id === selectedValue)
429
248
  );
430
249
  }
431
- function cleanResultPath(url) {
432
- return (url.split("#")[0]?.replace(/^\/+|\/+$/g, "") ?? "") || "/";
433
- }
434
- function resultSubtitle(result) {
435
- let section = result.meta.section ?? result.meta.group, path = cleanResultPath(result.meta.path ?? result.url);
436
- return section && path && !path.startsWith(section) ? `${section} / ${path}` : path || section || result.url;
437
- }
438
250
  function normalizedSnippetText(text) {
439
251
  return (text ?? "").replace(/\s+/g, " ").trim();
440
252
  }
441
- function queryMatches(text, query) {
442
- let normalizedQuery = query.trim().toLowerCase();
443
- if (!normalizedQuery) return !1;
444
- let lower = text.toLowerCase();
445
- return lower.includes(normalizedQuery) ? !0 : normalizedQuery.split(/\s+/).filter((term) => term.length > 1).some((term) => lower.includes(term));
446
- }
447
253
  function firstMatchIndex(text, query) {
448
254
  let normalizedQuery = query.trim().toLowerCase();
449
255
  if (!normalizedQuery) return -1;
@@ -458,7 +264,7 @@ function contextualSnippet({
458
264
  let normalizedText = normalizedSnippetText(text);
459
265
  if (normalizedText.length <= length) return normalizedText;
460
266
  let matchIndex = firstMatchIndex(normalizedText, query);
461
- if (matchIndex < 0) return `${normalizedText.slice(0, length).trimEnd()}...`;
267
+ if (matchIndex < 0) return `${normalizedText.slice(0, length).trimEnd()}\u2026`;
462
268
  let halfLength = Math.floor(length / 2), start = Math.max(0, matchIndex - halfLength), end = Math.min(normalizedText.length, start + length);
463
269
  if (end - start < length && (start = Math.max(0, end - length)), start > 0) {
464
270
  let nextSpace = normalizedText.indexOf(" ", start);
@@ -468,7 +274,7 @@ function contextualSnippet({
468
274
  let previousSpace = normalizedText.lastIndexOf(" ", end);
469
275
  previousSpace > matchIndex && (end = previousSpace);
470
276
  }
471
- let prefix = start > 0 ? "... " : "", suffix = end < normalizedText.length ? " ..." : "";
277
+ let prefix = start > 0 ? "\u2026 " : "", suffix = end < normalizedText.length ? " \u2026" : "";
472
278
  return `${prefix}${normalizedText.slice(start, end).trim()}${suffix}`;
473
279
  }
474
280
  function resultSnippet({
@@ -476,10 +282,10 @@ function resultSnippet({
476
282
  result
477
283
  }) {
478
284
  let description = normalizedSnippetText(result.meta.description);
479
- if (description && queryMatches(description, query))
285
+ if (description)
480
286
  return contextualSnippet({ query, text: description });
481
287
  let excerpt = normalizedSnippetText(result.plainExcerpt);
482
- return excerpt ? contextualSnippet({ query, text: excerpt }) : description ? contextualSnippet({ query, text: description }) : "";
288
+ return excerpt ? contextualSnippet({ query, text: excerpt }) : "";
483
289
  }
484
290
  function HighlightedText({ query, text }) {
485
291
  let normalizedQuery = query.trim().toLowerCase();
@@ -503,7 +309,7 @@ function HighlightedText({ query, text }) {
503
309
  /* @__PURE__ */ jsx(
504
310
  "mark",
505
311
  {
506
- className: "rounded-[1px] bg-accent-2 text-accent-1",
312
+ className: "rounded-[min(var(--theme-radius),2px)] bg-accent-1/15 text-accent-1",
507
313
  children: text.slice(range.start, range.end)
508
314
  },
509
315
  `${range.start}-${range.end}`
@@ -511,52 +317,373 @@ function HighlightedText({ query, text }) {
511
317
  ), cursor = range.end;
512
318
  return cursor < text.length && parts.push(text.slice(cursor)), /* @__PURE__ */ jsx(Fragment, { children: parts });
513
319
  }
514
- function Command({ children, className } = {}) {
515
- return /* @__PURE__ */ jsx(
516
- "div",
320
+
321
+ // src/modules/search/search-progress.tsx
322
+ import {
323
+ Progress,
324
+ ProgressIndicator,
325
+ ProgressTrack
326
+ } from "@publish.os/nextjs/kernel/components/ui/progress";
327
+ import { useSearchBlock as useSearchBlock2 } from "@publish.os/nextjs/search";
328
+ import { cn as cn2 } from "@publish.os/nextjs/utils/cn";
329
+ import { useLayoutEffect, useRef, useState } from "react";
330
+ import { jsx as jsx2 } from "react/jsx-runtime";
331
+ var INITIAL_PROGRESS = 5, FAST_PHASE_END = 25, TARGET_PROGRESS = 95, FAST_DURATION = 50, MAX_DURATION = 5e3, COMPLETE_DURATION = 200, EXIT_DURATION = 150;
332
+ function SearchProgressBar({ className }) {
333
+ let {
334
+ meta: { inputRef, triggerRef },
335
+ state: { isTyping, loading, open, query, status }
336
+ } = useSearchBlock2(), enabled = open && !!query.trim(), active = enabled && (isTyping || loading || status === "building" || status === "stale"), [progress, setProgress] = useState(active ? INITIAL_PROGRESS : 0), [isExiting, setIsExiting] = useState(!1), progressRef = useRef(progress);
337
+ useLayoutEffect(() => {
338
+ let win = inputRef.current?.ownerDocument.defaultView ?? triggerRef.current?.ownerDocument.defaultView ?? window, frame = null, timer = null, updateProgress = (value) => {
339
+ progressRef.current = value, setProgress(value);
340
+ };
341
+ if (!enabled)
342
+ updateProgress(0), setIsExiting(!1);
343
+ else if (active)
344
+ if (setIsExiting(!1), updateProgress(INITIAL_PROGRESS), win.matchMedia("(prefers-reduced-motion: reduce)").matches)
345
+ updateProgress(FAST_PHASE_END);
346
+ else {
347
+ let start = win.performance.now(), animate = (now) => {
348
+ let elapsed = Math.max(0, now - start), normalizedTime = Math.min(
349
+ 1,
350
+ Math.max(0, elapsed - FAST_DURATION) / (MAX_DURATION - FAST_DURATION)
351
+ );
352
+ updateProgress(
353
+ elapsed < FAST_DURATION ? INITIAL_PROGRESS + elapsed / FAST_DURATION * (FAST_PHASE_END - INITIAL_PROGRESS) : FAST_PHASE_END + (1 - (1 - normalizedTime) ** 3) * (TARGET_PROGRESS - FAST_PHASE_END)
354
+ ), normalizedTime < 1 && (frame = win.requestAnimationFrame(animate));
355
+ };
356
+ frame = win.requestAnimationFrame(animate);
357
+ }
358
+ else progressRef.current > 0 && (updateProgress(100), timer = win.setTimeout(() => {
359
+ setIsExiting(!0), timer = win.setTimeout(() => {
360
+ updateProgress(0), setIsExiting(!1);
361
+ }, EXIT_DURATION);
362
+ }, COMPLETE_DURATION));
363
+ return () => {
364
+ frame !== null && win.cancelAnimationFrame(frame), timer !== null && win.clearTimeout(timer);
365
+ };
366
+ }, [active, enabled, inputRef, triggerRef]);
367
+ let visible = enabled && progress > 0, phase = visible ? isExiting ? "exiting" : active ? "loading" : "complete" : "idle";
368
+ return /* @__PURE__ */ jsx2(
369
+ Progress,
517
370
  {
518
- className: cn(
519
- "relative overflow-hidden rounded-dialog bg-surface-1 text-foreground-1",
371
+ "aria-label": "Searching",
372
+ "aria-hidden": !visible,
373
+ className: cn2(
374
+ "pointer-events-none h-px shrink-0 overflow-hidden transition-opacity duration-100",
375
+ visible ? "opacity-100" : "opacity-0",
520
376
  className
521
377
  ),
522
- children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
523
- /* @__PURE__ */ jsx(SearchInputField, {}),
524
- /* @__PURE__ */ jsx(SearchProgressBar, { className: "absolute inset-x-0 top-12 z-content" }),
525
- /* @__PURE__ */ jsx(SearchResults, {})
526
- ] })
378
+ "data-state": phase,
379
+ value: null,
380
+ children: /* @__PURE__ */ jsx2(ProgressTrack, { className: "h-full rounded-none bg-transparent", children: /* @__PURE__ */ jsx2(
381
+ ProgressIndicator,
382
+ {
383
+ className: "animate-search-progress h-full w-full rounded-none",
384
+ "data-state": phase,
385
+ style: {
386
+ width: "100%",
387
+ "--search-progress": progress / 100
388
+ }
389
+ }
390
+ ) })
527
391
  }
528
392
  );
529
393
  }
530
- function DefaultDesktop({ children, ...rootProps }) {
531
- return /* @__PURE__ */ jsx(Root, { variant: "desktop", ...rootProps, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
532
- /* @__PURE__ */ jsx(Trigger, {}),
533
- /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(Command, {}) })
534
- ] }) });
394
+
395
+ // src/modules/search/search-block.tsx
396
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
397
+ function Trigger({
398
+ children,
399
+ className,
400
+ icon = /* @__PURE__ */ jsx3(MagnifyingGlassIcon, { "aria-hidden": "true", size: 16 }),
401
+ placeholder = "Search",
402
+ ref,
403
+ render,
404
+ shortcutClassName,
405
+ size = "sm",
406
+ variant = "outline",
407
+ ...props
408
+ }) {
409
+ let {
410
+ actions: { setOpen },
411
+ meta: { popupId, shortcut: registeredShortcut, triggerRef },
412
+ state: { open }
413
+ } = useSearchBlock3();
414
+ return useRender({
415
+ defaultTagName: "button",
416
+ render: render ?? /* @__PURE__ */ jsx3(Button, { size, variant }),
417
+ ref: [triggerRef, ref ?? null],
418
+ state: { open },
419
+ props: {
420
+ ...props,
421
+ "aria-controls": open ? popupId : void 0,
422
+ "aria-expanded": open,
423
+ "aria-haspopup": "dialog",
424
+ "aria-label": props["aria-label"] ?? placeholder,
425
+ className: cn3("md:min-w-32", className),
426
+ onClick: (event) => {
427
+ props.onClick?.(event), event.defaultPrevented || (triggerRef.current = event.currentTarget, setOpen(!0));
428
+ },
429
+ children: children ?? /* @__PURE__ */ jsxs2(Fragment2, { children: [
430
+ icon,
431
+ /* @__PURE__ */ jsx3("span", { className: "hidden min-w-0 flex-1 truncate text-start leading-none md:inline", children: placeholder }),
432
+ registeredShortcut ? /* @__PURE__ */ jsx3(
433
+ "span",
434
+ {
435
+ "aria-hidden": "true",
436
+ className: cn3(
437
+ "hidden shrink-0 items-center gap-1 md:inline-flex",
438
+ shortcutClassName
439
+ ),
440
+ children: /* @__PURE__ */ jsx3(SearchShortcutHint, { shortcut: registeredShortcut })
441
+ }
442
+ ) : null
443
+ ] })
444
+ }
445
+ });
446
+ }
447
+ function SearchShortcutHint({ shortcut }) {
448
+ let [tokens, setTokens] = useState2(null);
449
+ return useEffect2(() => {
450
+ setTokens(formatShortcut(shortcut));
451
+ }, [shortcut]), shortcut.toLowerCase() === "mod+k" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
452
+ /* @__PURE__ */ jsx3(ModKey, { size: "xs" }),
453
+ /* @__PURE__ */ jsx3(Kbd2, { size: "xs", children: "K" })
454
+ ] }) : (tokens ?? ["Ctrl", "K"]).map((token) => /* @__PURE__ */ jsx3(
455
+ Kbd2,
456
+ {
457
+ className: tokens === null ? "invisible" : void 0,
458
+ size: "xs",
459
+ children: token
460
+ },
461
+ token
462
+ ));
535
463
  }
536
- function DefaultMobile({ children, ...rootProps }) {
537
- return /* @__PURE__ */ jsx(Root, { variant: "mobile", ...rootProps, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
538
- /* @__PURE__ */ jsx(Trigger, { showShortcut: !1 }),
539
- /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(Command, {}) })
464
+ function DialogContent({
465
+ children,
466
+ className,
467
+ container,
468
+ description = "Search this site and navigate to a result.",
469
+ mobileQuery = "(max-width: 767px)",
470
+ title = "Search"
471
+ }) {
472
+ let {
473
+ actions: { setOpen },
474
+ meta: { inputRef, popupId, triggerRef },
475
+ state: { open }
476
+ } = useSearchBlock3(), [mobile, setMobile] = useState2(!1);
477
+ return useEffect2(() => {
478
+ let media = (triggerRef.current?.ownerDocument.defaultView ?? window).matchMedia(mobileQuery), update = () => setMobile(media.matches);
479
+ return update(), media.addEventListener("change", update), () => media.removeEventListener("change", update);
480
+ }, [mobileQuery, triggerRef]), mobile ? /* @__PURE__ */ jsx3(Drawer, { onOpenChange: setOpen, open, swipeDirection: "down", children: /* @__PURE__ */ jsx3(DrawerVirtualKeyboardProvider, { children: /* @__PURE__ */ jsxs2(DrawerPortal, { container, children: [
481
+ /* @__PURE__ */ jsx3(DrawerBackdrop, { className: "animate-search-backdrop bg-backdrop" }),
482
+ /* @__PURE__ */ jsx3(DrawerViewport, { children: /* @__PURE__ */ jsxs2(
483
+ DrawerPopup,
484
+ {
485
+ "aria-label": title,
486
+ "data-search-presentation": "drawer",
487
+ id: popupId,
488
+ initialFocus: inputRef,
489
+ finalFocus: triggerRef,
490
+ className: cn3(
491
+ "group/search animate-search-drawer inset-x-0 top-auto bottom-(--drawer-keyboard-inset,0px) flex h-[min(94dvh,52rem)] max-h-[calc(100dvh-var(--drawer-keyboard-inset,0)-env(safe-area-inset-top))] w-full max-w-none flex-col overflow-hidden rounded-t-dialog border-0 bg-surface-1 p-0 pb-[env(safe-area-inset-bottom)] text-foreground-1 border-shadow-1 overscroll-contain",
492
+ className
493
+ ),
494
+ children: [
495
+ /* @__PURE__ */ jsx3(DrawerTitle, { className: "sr-only", children: title }),
496
+ /* @__PURE__ */ jsx3(DrawerDescription, { className: "sr-only", children: description }),
497
+ /* @__PURE__ */ jsx3(
498
+ "div",
499
+ {
500
+ "aria-hidden": "true",
501
+ className: "mx-auto mt-2 h-1.5 w-10 shrink-0 rounded-scrollbar-thumb bg-stroke-2"
502
+ }
503
+ ),
504
+ /* @__PURE__ */ jsx3("div", { className: "flex min-h-0 flex-1 flex-col", children: children === void 0 ? /* @__PURE__ */ jsx3(Command, {}) : children })
505
+ ]
506
+ }
507
+ ) })
508
+ ] }) }) }) : /* @__PURE__ */ jsx3(Dialog, { onOpenChange: setOpen, open, children: /* @__PURE__ */ jsxs2(DialogPortal, { container, children: [
509
+ /* @__PURE__ */ jsx3(DialogBackdrop, { className: "animate-search-backdrop bg-dialog-backdrop" }),
510
+ /* @__PURE__ */ jsxs2(
511
+ DialogPopup,
512
+ {
513
+ "data-search-presentation": "dialog",
514
+ id: popupId,
515
+ initialFocus: inputRef,
516
+ finalFocus: triggerRef,
517
+ className: cn3(
518
+ "group/search animate-search-dialog top-[12dvh] flex max-h-[min(42rem,82dvh)] w-[min(92vw,575px)] -translate-x-1/2 translate-y-0 flex-col overflow-hidden border-0 p-0",
519
+ className
520
+ ),
521
+ children: [
522
+ /* @__PURE__ */ jsx3(DialogTitle, { className: "sr-only", children: title }),
523
+ /* @__PURE__ */ jsx3(DialogDescription, { className: "sr-only", children: description }),
524
+ children === void 0 ? /* @__PURE__ */ jsx3(Command, {}) : children
525
+ ]
526
+ }
527
+ )
540
528
  ] }) });
541
529
  }
542
- var Search = {
543
- Command,
544
- DefaultDesktop,
545
- DefaultMobile,
546
- DesktopDialog: DialogContent,
547
- Dialog: DialogContent,
548
- Input: SearchInputField,
549
- MobileDialog: DialogContent,
550
- Progress: SearchProgressBar,
551
- Results: SearchResults,
552
- Root,
553
- Trigger
554
- };
530
+ function SearchInputField({
531
+ className,
532
+ closeClassName,
533
+ closeLabel = "ESC",
534
+ icon = /* @__PURE__ */ jsx3(MagnifyingGlassIcon, { "aria-hidden": "true", size: 20, weight: "light" }),
535
+ onChange,
536
+ onKeyDown,
537
+ placeholder = "Search\u2026",
538
+ ref,
539
+ rootClassName,
540
+ showCloseShortcut = !0,
541
+ ...props
542
+ }) {
543
+ let {
544
+ actions: { handleInputChange, navigateToResult, setOpen, setSelectedValue },
545
+ meta: { inputId, inputRef, listId },
546
+ state: { displayResults, query, selectedValue }
547
+ } = useSearchBlock3(), selectedIndex2 = Math.max(
548
+ 0,
549
+ displayResults.findIndex((result) => result.id === selectedValue)
550
+ ), selectedOptionId = displayResults.length > 0 ? resultOptionId(listId, selectedIndex2) : void 0, handleKeyDown = (event) => {
551
+ if (onKeyDown?.(event), !(event.defaultPrevented || event.nativeEvent.isComposing || event.keyCode === 229)) {
552
+ if (event.key === "Escape") {
553
+ event.preventDefault(), setOpen(!1);
554
+ return;
555
+ }
556
+ if (event.key === "ArrowDown") {
557
+ event.preventDefault();
558
+ let next = displayResults[Math.min(selectedIndex2 + 1, displayResults.length - 1)];
559
+ next && setSelectedValue(next.id);
560
+ return;
561
+ }
562
+ if (event.key === "ArrowUp") {
563
+ event.preventDefault();
564
+ let next = displayResults[Math.max(selectedIndex2 - 1, 0)];
565
+ next && setSelectedValue(next.id);
566
+ return;
567
+ }
568
+ if (event.key === "Enter") {
569
+ let selected = displayResults[selectedIndex2];
570
+ if (!selected) return;
571
+ event.preventDefault(), navigateToResult(selected);
572
+ }
573
+ }
574
+ };
575
+ return /* @__PURE__ */ jsxs2("div", { className: cn3("relative shrink-0 bg-transparent", rootClassName), children: [
576
+ icon ? /* @__PURE__ */ jsx3(
577
+ "span",
578
+ {
579
+ "aria-hidden": "true",
580
+ className: "pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-foreground-3/50",
581
+ children: icon
582
+ }
583
+ ) : null,
584
+ /* @__PURE__ */ jsx3(
585
+ Input,
586
+ {
587
+ ...props,
588
+ "aria-activedescendant": selectedOptionId,
589
+ "aria-autocomplete": "list",
590
+ "aria-controls": listId,
591
+ "aria-expanded": displayResults.length > 0,
592
+ autoComplete: "off",
593
+ "aria-label": props["aria-label"] ?? "Search this site",
594
+ className: cn3(
595
+ "supports-[caret-color:var(--accent-1)]:caret-(--accent-1)",
596
+ "h-14 rounded-none border-0 bg-transparent px-4 text-base shadow-none focus:border-transparent focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-stroke",
597
+ icon && "pl-12",
598
+ showCloseShortcut && "md:pr-16",
599
+ className
600
+ ),
601
+ id: inputId,
602
+ name: "search",
603
+ onChange: (event) => {
604
+ onChange?.(event), event.defaultPrevented || handleInputChange(event.currentTarget.value);
605
+ },
606
+ onKeyDown: handleKeyDown,
607
+ placeholder,
608
+ ref: (node) => {
609
+ if (inputRef.current = node, typeof ref == "function") {
610
+ let cleanup = ref(node);
611
+ if (typeof cleanup == "function")
612
+ return () => {
613
+ inputRef.current = null, cleanup();
614
+ };
615
+ } else ref && (ref.current = node);
616
+ },
617
+ role: "combobox",
618
+ spellCheck: !1,
619
+ type: "text",
620
+ value: query
621
+ }
622
+ ),
623
+ showCloseShortcut ? /* @__PURE__ */ jsx3(
624
+ "button",
625
+ {
626
+ "aria-label": "Close search",
627
+ className: cn3(
628
+ kbdVariants({ size: "xs" }),
629
+ "absolute right-4 top-1/2 hidden -translate-y-1/2 cursor-pointer text-foreground-2 before:absolute before:left-1/2 before:top-1/2 before:size-10 before:-translate-1/2 before:content-[''] active:translate-y-[calc(-50%_+_1px)] md:inline-flex",
630
+ closeClassName
631
+ ),
632
+ onClick: () => setOpen(!1),
633
+ type: "button",
634
+ children: closeLabel
635
+ }
636
+ ) : null,
637
+ /* @__PURE__ */ jsx3(Separator2, { className: "absolute inset-x-0 bottom-0 z-content md:hidden group-data-[search-presentation=drawer]/search:block group-data-[search-presentation=dialog]/search:hidden" })
638
+ ] });
639
+ }
640
+ function Command({ children, className, ...props } = {}) {
641
+ let { state } = useSearchBlock3();
642
+ return /* @__PURE__ */ jsx3(
643
+ "div",
644
+ {
645
+ className: cn3(
646
+ // The popup owns the fill; opaque children would cover its inset edge.
647
+ "relative flex min-h-0 flex-1 flex-col overflow-hidden bg-transparent text-foreground-1",
648
+ className
649
+ ),
650
+ ...props,
651
+ children: children === void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
652
+ /* @__PURE__ */ jsx3(SearchInputField, {}),
653
+ /* @__PURE__ */ jsx3(SearchProgressBar, { className: "absolute inset-x-0 top-14 z-content -translate-y-full" }),
654
+ /* @__PURE__ */ jsx3(
655
+ "div",
656
+ {
657
+ className: "animate-search-results grid min-h-0 flex-1",
658
+ "data-expanded": getSearchPanel(state) !== null,
659
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-col overflow-hidden", children: [
660
+ /* @__PURE__ */ jsx3(SearchResults, {}),
661
+ /* @__PURE__ */ jsx3(SearchFooter, {})
662
+ ] })
663
+ }
664
+ )
665
+ ] }) : children
666
+ }
667
+ );
668
+ }
669
+ function Search(props) {
670
+ return /* @__PURE__ */ jsxs2(Root, { ...props, children: [
671
+ /* @__PURE__ */ jsx3(Trigger, {}),
672
+ /* @__PURE__ */ jsx3(DialogContent, {})
673
+ ] });
674
+ }
675
+ Search.Root = Root;
676
+ Search.Trigger = Trigger;
677
+ Search.Dialog = DialogContent;
678
+ Search.Command = Command;
679
+ Search.Input = SearchInputField;
680
+ Search.Progress = SearchProgressBar;
681
+ Search.Results = SearchResults;
682
+ Search.Footer = SearchFooter;
555
683
  export {
556
684
  Command,
557
- DefaultDesktop,
558
- DefaultMobile,
559
685
  DialogContent as Dialog,
686
+ SearchFooter as Footer,
560
687
  SearchInputField as Input,
561
688
  SearchProgressBar as Progress,
562
689
  SearchResults as Results,