livemark 0.25.0 → 0.27.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 (427) hide show
  1. package/README.md +3 -2
  2. package/build/actions/config/define.d.ts +12 -0
  3. package/build/actions/config/define.unit.js +8 -1
  4. package/build/actions/escape/copy.d.ts +11 -0
  5. package/build/actions/escape/copy.js +29 -0
  6. package/build/actions/escape/copy.unit.d.ts +1 -0
  7. package/build/actions/escape/copy.unit.js +37 -0
  8. package/build/actions/escape/list.d.ts +12 -0
  9. package/build/actions/escape/list.js +27 -0
  10. package/build/actions/escape/list.unit.d.ts +1 -0
  11. package/build/actions/escape/list.unit.js +36 -0
  12. package/build/actions/target/build.d.ts +9 -26
  13. package/build/actions/target/build.js +50 -71
  14. package/build/actions/target/build.unit.d.ts +1 -0
  15. package/build/actions/target/build.unit.js +76 -0
  16. package/build/actions/target/clear.d.ts +4 -0
  17. package/build/actions/target/clear.js +14 -0
  18. package/build/actions/target/clear.unit.d.ts +1 -0
  19. package/build/actions/target/clear.unit.js +23 -0
  20. package/build/commands/build.js +1 -2
  21. package/build/commands/clear.d.ts +5 -0
  22. package/build/commands/clear.js +15 -0
  23. package/build/commands/escape.d.ts +4 -3
  24. package/build/commands/escape.js +45 -48
  25. package/build/commands/preview.js +1 -2
  26. package/build/commands/start.js +1 -2
  27. package/build/components/Abbr.d.ts +1 -1
  28. package/build/components/Abbr.js +2 -4
  29. package/build/components/AnsiCode.d.ts +1 -1
  30. package/build/components/AnsiCode.js +2 -2
  31. package/build/components/Article.d.ts +2 -1
  32. package/build/components/Article.js +7 -14
  33. package/build/components/BackToTop.d.ts +1 -1
  34. package/build/components/Banner.d.ts +2 -2
  35. package/build/components/Banner.js +7 -3
  36. package/build/components/BlogIndex.d.ts +1 -1
  37. package/build/components/BlogIndex.js +2 -11
  38. package/build/components/Breadcrumbs.d.ts +1 -1
  39. package/build/components/Breadcrumbs.js +2 -3
  40. package/build/components/Callout.d.ts +1 -1
  41. package/build/components/Callout.js +5 -9
  42. package/build/components/Card.d.ts +1 -1
  43. package/build/components/Card.js +2 -3
  44. package/build/components/Cards.d.ts +1 -1
  45. package/build/components/ChangelogIndex.d.ts +1 -1
  46. package/build/components/ChangelogIndex.js +2 -11
  47. package/build/components/ChoiceList.d.ts +15 -0
  48. package/build/components/ChoiceList.js +13 -0
  49. package/build/components/CodeBlock.d.ts +1 -1
  50. package/build/components/CodeBlock.js +3 -16
  51. package/build/components/CodeTabs.d.ts +1 -1
  52. package/build/components/CodeTabs.js +2 -3
  53. package/build/components/Column.d.ts +1 -1
  54. package/build/components/Columns.d.ts +1 -1
  55. package/build/components/Content.d.ts +1 -1
  56. package/build/components/ContentTab.d.ts +1 -1
  57. package/build/components/ContentTabs.d.ts +1 -1
  58. package/build/components/ContentTabs.js +2 -3
  59. package/build/components/DefaultCatchBoundary.d.ts +1 -1
  60. package/build/components/DefaultCatchBoundary.js +5 -7
  61. package/build/components/DefinitionList.d.ts +3 -3
  62. package/build/components/Details.d.ts +1 -1
  63. package/build/components/Details.js +2 -5
  64. package/build/components/FileTree.d.ts +1 -1
  65. package/build/components/FileTree.js +3 -5
  66. package/build/components/Footer.d.ts +1 -1
  67. package/build/components/Footer.js +2 -5
  68. package/build/components/FootnoteRef.d.ts +1 -1
  69. package/build/components/FootnoteRef.js +2 -4
  70. package/build/components/Header.d.ts +6 -0
  71. package/build/components/Header.js +26 -0
  72. package/build/components/HeaderMenu.d.ts +2 -0
  73. package/build/components/HeaderMenu.js +27 -0
  74. package/build/components/HeaderNav.d.ts +6 -0
  75. package/build/components/HeaderNav.js +74 -0
  76. package/build/components/HeaderSearch.d.ts +4 -0
  77. package/build/components/HeaderSearch.js +33 -0
  78. package/build/components/Heading.d.ts +6 -6
  79. package/build/components/InlineBadge.d.ts +2 -2
  80. package/build/components/InlineBadge.js +1 -1
  81. package/build/components/InlineIcon.d.ts +1 -1
  82. package/build/components/InlineToc.d.ts +1 -1
  83. package/build/components/InlineToc.js +2 -4
  84. package/build/components/Layout.d.ts +2 -1
  85. package/build/components/Layout.js +5 -58
  86. package/build/components/LinkButton.d.ts +2 -2
  87. package/build/components/LinkButton.js +1 -1
  88. package/build/components/MenuSheet.d.ts +22 -0
  89. package/build/components/MenuSheet.js +27 -0
  90. package/build/components/Mermaid.d.ts +1 -1
  91. package/build/components/MermaidRenderer.d.ts +1 -1
  92. package/build/components/MermaidRenderer.js +20 -8
  93. package/build/components/NotFound.d.ts +1 -1
  94. package/build/components/NotFound.js +3 -6
  95. package/build/components/PackageTabs.d.ts +1 -1
  96. package/build/components/PackageTabs.js +3 -7
  97. package/build/components/PageToolbar.d.ts +1 -1
  98. package/build/components/PageToolbar.js +2 -10
  99. package/build/components/PrevNext.d.ts +1 -1
  100. package/build/components/PrevNext.js +2 -10
  101. package/build/components/Root.d.ts +4 -0
  102. package/build/components/Root.js +8 -0
  103. package/build/components/Search.d.ts +5 -2
  104. package/build/components/Search.js +11 -9
  105. package/build/components/SearchDialog.d.ts +1 -1
  106. package/build/components/SearchDialog.js +10 -133
  107. package/build/components/Section.d.ts +1 -1
  108. package/build/components/SectionHeader.d.ts +1 -1
  109. package/build/components/SheetPanel.d.ts +12 -0
  110. package/build/components/SheetPanel.js +19 -0
  111. package/build/components/Sidebar.d.ts +1 -1
  112. package/build/components/Sidebar.js +12 -14
  113. package/build/components/SidebarArticles.d.ts +4 -1
  114. package/build/components/SidebarArticles.js +5 -9
  115. package/build/components/SidebarLinks.d.ts +4 -1
  116. package/build/components/SidebarLinks.js +6 -9
  117. package/build/components/SidebarPosts.d.ts +4 -1
  118. package/build/components/SidebarPosts.js +7 -23
  119. package/build/components/SidebarReleases.d.ts +4 -1
  120. package/build/components/SidebarReleases.js +5 -15
  121. package/build/components/SidebarSectionPicker.d.ts +8 -0
  122. package/build/components/SidebarSectionPicker.js +31 -0
  123. package/build/components/SidebarSections.d.ts +1 -1
  124. package/build/components/SidebarSections.js +5 -9
  125. package/build/components/SiteLink.d.ts +1 -1
  126. package/build/components/SiteTitle.d.ts +1 -1
  127. package/build/components/SiteTitle.js +2 -6
  128. package/build/components/SortableTable.d.ts +1 -1
  129. package/build/components/SoundCloud.d.ts +1 -1
  130. package/build/components/SoundCloud.js +2 -2
  131. package/build/components/TagIndex.d.ts +1 -1
  132. package/build/components/TagIndex.js +2 -11
  133. package/build/components/Theme.d.ts +2 -1
  134. package/build/components/Theme.js +35 -15
  135. package/build/components/Toc.d.ts +5 -4
  136. package/build/components/Toc.js +35 -35
  137. package/build/components/YouTube.d.ts +1 -1
  138. package/build/components/YouTube.js +2 -2
  139. package/build/components/ZoomImage.d.ts +1 -1
  140. package/build/content/article.js +4 -11
  141. package/build/content-collections.js +6 -4
  142. package/build/elements/accordion.d.ts +4 -4
  143. package/build/elements/accordion.js +2 -3
  144. package/build/elements/alert-dialog.d.ts +12 -12
  145. package/build/elements/alert-dialog.js +2 -4
  146. package/build/elements/alert.d.ts +4 -4
  147. package/build/elements/alert.js +2 -2
  148. package/build/elements/aspect-ratio.d.ts +1 -1
  149. package/build/elements/avatar.d.ts +6 -6
  150. package/build/elements/badge.d.ts +1 -1
  151. package/build/elements/badge.js +3 -1
  152. package/build/elements/breadcrumb.d.ts +6 -6
  153. package/build/elements/breadcrumb.js +2 -4
  154. package/build/elements/button-group.d.ts +2 -2
  155. package/build/elements/button-group.js +5 -5
  156. package/build/elements/button.d.ts +2 -2
  157. package/build/elements/button.js +9 -7
  158. package/build/elements/calendar.d.ts +2 -2
  159. package/build/elements/calendar.js +2 -2
  160. package/build/elements/card.d.ts +7 -7
  161. package/build/elements/card.js +4 -4
  162. package/build/elements/carousel.d.ts +5 -5
  163. package/build/elements/carousel.js +3 -7
  164. package/build/elements/chart.d.ts +6 -6
  165. package/build/elements/chart.js +5 -11
  166. package/build/elements/checkbox.d.ts +1 -1
  167. package/build/elements/collapsible.d.ts +3 -3
  168. package/build/elements/command.d.ts +9 -9
  169. package/build/elements/command.js +5 -11
  170. package/build/elements/context-menu.d.ts +15 -15
  171. package/build/elements/context-menu.js +6 -9
  172. package/build/elements/dialog.d.ts +10 -10
  173. package/build/elements/dialog.js +2 -6
  174. package/build/elements/drawer.d.ts +14 -12
  175. package/build/elements/drawer.js +14 -15
  176. package/build/elements/dropdown-menu.d.ts +15 -15
  177. package/build/elements/dropdown-menu.js +7 -10
  178. package/build/elements/empty.d.ts +6 -6
  179. package/build/elements/empty.js +2 -2
  180. package/build/elements/field.d.ts +10 -10
  181. package/build/elements/field.js +3 -4
  182. package/build/elements/hover-card.d.ts +3 -3
  183. package/build/elements/hover-card.js +2 -2
  184. package/build/elements/input-group.d.ts +7 -7
  185. package/build/elements/input-group.js +5 -5
  186. package/build/elements/input-otp.d.ts +4 -4
  187. package/build/elements/input.d.ts +1 -1
  188. package/build/elements/input.js +2 -2
  189. package/build/elements/item.d.ts +9 -9
  190. package/build/elements/item.js +2 -2
  191. package/build/elements/kbd.d.ts +2 -2
  192. package/build/elements/label.d.ts +1 -1
  193. package/build/elements/menubar.d.ts +16 -16
  194. package/build/elements/menubar.js +9 -11
  195. package/build/elements/native-select.d.ts +3 -3
  196. package/build/elements/native-select.js +2 -4
  197. package/build/elements/navigation-menu.d.ts +8 -8
  198. package/build/elements/navigation-menu.js +3 -5
  199. package/build/elements/pagination.d.ts +7 -7
  200. package/build/elements/pagination.js +4 -10
  201. package/build/elements/popover.d.ts +6 -6
  202. package/build/elements/popover.js +2 -2
  203. package/build/elements/progress.d.ts +5 -5
  204. package/build/elements/progress.js +2 -3
  205. package/build/elements/radio-group.d.ts +2 -2
  206. package/build/elements/resizable.d.ts +3 -3
  207. package/build/elements/scroll-area.d.ts +2 -2
  208. package/build/elements/scroll-area.js +2 -4
  209. package/build/elements/select.d.ts +9 -9
  210. package/build/elements/select.js +4 -9
  211. package/build/elements/separator.d.ts +1 -1
  212. package/build/elements/sheet.d.ts +8 -8
  213. package/build/elements/sheet.js +2 -6
  214. package/build/elements/sidebar.d.ts +19 -19
  215. package/build/elements/sidebar.js +15 -37
  216. package/build/elements/skeleton.d.ts +1 -1
  217. package/build/elements/slider.d.ts +2 -1
  218. package/build/elements/slider.js +2 -3
  219. package/build/elements/sonner.d.ts +1 -1
  220. package/build/elements/spinner.d.ts +1 -1
  221. package/build/elements/switch.d.ts +1 -1
  222. package/build/elements/table.d.ts +8 -8
  223. package/build/elements/tabs.d.ts +4 -4
  224. package/build/elements/tabs.js +3 -3
  225. package/build/elements/textarea.d.ts +1 -1
  226. package/build/elements/textarea.js +2 -2
  227. package/build/elements/toggle-group.d.ts +3 -2
  228. package/build/elements/toggle-group.js +3 -3
  229. package/build/elements/toggle.d.ts +1 -1
  230. package/build/elements/toggle.js +2 -2
  231. package/build/elements/tooltip.d.ts +4 -4
  232. package/build/elements/tooltip.js +2 -3
  233. package/build/head.d.ts +4 -0
  234. package/build/head.js +6 -0
  235. package/build/helpers/agent-skills.d.ts +22 -0
  236. package/build/helpers/agent-skills.js +46 -0
  237. package/build/helpers/agent-skills.unit.d.ts +1 -0
  238. package/build/helpers/agent-skills.unit.js +49 -0
  239. package/build/helpers/article-search.d.ts +19 -0
  240. package/build/helpers/article-search.js +134 -0
  241. package/build/helpers/article.d.ts +13 -0
  242. package/build/helpers/article.js +17 -1
  243. package/build/helpers/dependency-dir.d.ts +5 -0
  244. package/build/helpers/dependency-dir.js +20 -0
  245. package/build/helpers/dependency-dir.unit.d.ts +1 -0
  246. package/build/helpers/dependency-dir.unit.js +25 -0
  247. package/build/helpers/dynamic-icon-resolver.d.ts +1 -1
  248. package/build/helpers/dynamic-icon.d.ts +1 -1
  249. package/build/helpers/head.d.ts +58 -0
  250. package/build/helpers/head.js +80 -0
  251. package/build/helpers/head.unit.d.ts +1 -0
  252. package/build/helpers/head.unit.js +110 -0
  253. package/build/helpers/llms.d.ts +33 -0
  254. package/build/helpers/llms.js +72 -0
  255. package/build/helpers/llms.unit.d.ts +1 -0
  256. package/build/helpers/llms.unit.js +98 -0
  257. package/build/helpers/nav-items.d.ts +11 -0
  258. package/build/helpers/nav-items.js +65 -0
  259. package/build/helpers/render-inline-markdown.d.ts +1 -0
  260. package/build/helpers/render-inline-markdown.js +34 -0
  261. package/build/helpers/render-inline-markdown.unit.d.ts +1 -0
  262. package/build/helpers/render-inline-markdown.unit.js +20 -0
  263. package/build/helpers/search-highlight.d.ts +8 -0
  264. package/build/helpers/search-highlight.js +59 -0
  265. package/build/helpers/search-highlight.unit.d.ts +1 -0
  266. package/build/helpers/search-highlight.unit.js +36 -0
  267. package/build/helpers/target-export.d.ts +9 -0
  268. package/build/helpers/target-export.js +24 -0
  269. package/build/helpers/target-export.unit.d.ts +1 -0
  270. package/build/helpers/target-export.unit.js +25 -0
  271. package/build/helpers/theme.d.ts +6 -2
  272. package/build/helpers/theme.js +40 -7
  273. package/build/helpers/theme.unit.d.ts +1 -0
  274. package/build/helpers/theme.unit.js +30 -0
  275. package/build/hooks/hydrated.d.ts +2 -0
  276. package/build/hooks/hydrated.js +15 -0
  277. package/build/index.d.ts +1 -0
  278. package/build/index.js +1 -1
  279. package/build/models/config.d.ts +52 -6
  280. package/build/models/config.js +17 -1
  281. package/build/models/section.d.ts +4 -2
  282. package/build/models/section.js +1 -1
  283. package/build/plugins/vite-agents.d.ts +17 -0
  284. package/build/plugins/vite-agents.js +109 -0
  285. package/build/plugins/vite-agents.unit.d.ts +1 -0
  286. package/build/plugins/vite-agents.unit.js +20 -0
  287. package/build/plugins/vite-livemark.d.ts +4 -5
  288. package/build/plugins/vite-livemark.js +56 -90
  289. package/build/plugins/vite-robots.d.ts +4 -2
  290. package/build/plugins/vite-robots.js +15 -2
  291. package/build/plugins/vite-robots.unit.js +5 -1
  292. package/build/program.js +4 -2
  293. package/build/routes/$.d.ts +4 -1
  294. package/build/routes/$.js +20 -9
  295. package/build/routes/__root.js +29 -63
  296. package/build/settings.js +2 -1
  297. package/build/vite.config.js +3 -1
  298. package/package.json +69 -66
  299. package/{.agents/skills → skills}/livemark-authoring/SKILL.md +2 -2
  300. package/{.agents/skills → skills}/livemark-configuration/SKILL.md +16 -5
  301. package/skills/livemark-customization/SKILL.md +146 -0
  302. package/source/actions/config/define.unit.ts +9 -0
  303. package/source/actions/escape/copy.ts +35 -0
  304. package/source/actions/escape/copy.unit.ts +51 -0
  305. package/source/actions/escape/list.ts +30 -0
  306. package/source/actions/escape/list.unit.ts +42 -0
  307. package/source/actions/target/build.ts +49 -69
  308. package/source/actions/target/build.unit.ts +95 -0
  309. package/source/actions/target/clear.ts +14 -0
  310. package/source/actions/target/clear.unit.ts +25 -0
  311. package/source/commands/build.ts +0 -1
  312. package/source/commands/clear.ts +15 -0
  313. package/source/commands/escape.ts +42 -59
  314. package/source/commands/preview.ts +0 -1
  315. package/source/commands/start.ts +0 -1
  316. package/source/components/AnsiCode.tsx +1 -1
  317. package/source/components/Article.tsx +8 -4
  318. package/source/components/Banner.tsx +12 -2
  319. package/source/components/BlogIndex.tsx +1 -1
  320. package/source/components/Callout.tsx +4 -6
  321. package/source/components/ChangelogIndex.tsx +1 -1
  322. package/source/components/ChoiceList.tsx +69 -0
  323. package/source/components/CodeBlock.tsx +6 -6
  324. package/source/components/CodeTabs.tsx +1 -1
  325. package/source/components/ContentTabs.tsx +1 -1
  326. package/source/components/DefaultCatchBoundary.tsx +4 -6
  327. package/source/components/Details.tsx +1 -1
  328. package/source/components/FileTree.tsx +1 -1
  329. package/source/components/Header.tsx +52 -0
  330. package/source/components/HeaderMenu.tsx +41 -0
  331. package/source/components/HeaderNav.tsx +145 -0
  332. package/source/components/HeaderSearch.tsx +77 -0
  333. package/source/components/InlineBadge.tsx +1 -1
  334. package/source/components/InlineToc.tsx +1 -1
  335. package/source/components/Layout.tsx +16 -160
  336. package/source/components/LinkButton.tsx +9 -1
  337. package/source/components/MenuSheet.tsx +92 -0
  338. package/source/components/MermaidRenderer.tsx +19 -8
  339. package/source/components/NotFound.tsx +3 -5
  340. package/source/components/PackageTabs.tsx +2 -2
  341. package/source/components/PageToolbar.tsx +11 -3
  342. package/source/components/PrevNext.tsx +2 -2
  343. package/source/components/Root.tsx +13 -0
  344. package/source/components/Search.tsx +28 -8
  345. package/source/components/SearchDialog.tsx +15 -189
  346. package/source/components/SheetPanel.tsx +69 -0
  347. package/source/components/Sidebar.tsx +27 -14
  348. package/source/components/SidebarArticles.tsx +4 -3
  349. package/source/components/SidebarLinks.tsx +3 -2
  350. package/source/components/SidebarPosts.tsx +3 -2
  351. package/source/components/SidebarReleases.tsx +3 -2
  352. package/source/components/SidebarSectionPicker.tsx +70 -0
  353. package/source/components/SiteTitle.tsx +5 -5
  354. package/source/components/SoundCloud.tsx +1 -1
  355. package/source/components/TagIndex.tsx +1 -1
  356. package/source/components/Theme.tsx +55 -29
  357. package/source/components/Toc.tsx +76 -55
  358. package/source/components/YouTube.tsx +1 -1
  359. package/source/content/article.ts +8 -13
  360. package/source/content-collections.ts +5 -4
  361. package/source/elements/alert-dialog.tsx +1 -1
  362. package/source/elements/alert.tsx +1 -1
  363. package/source/elements/badge.tsx +4 -0
  364. package/source/elements/button-group.tsx +4 -4
  365. package/source/elements/button.tsx +10 -6
  366. package/source/elements/calendar.tsx +1 -1
  367. package/source/elements/card.tsx +3 -3
  368. package/source/elements/command.tsx +7 -4
  369. package/source/elements/context-menu.tsx +5 -5
  370. package/source/elements/dialog.tsx +1 -1
  371. package/source/elements/drawer.tsx +59 -43
  372. package/source/elements/dropdown-menu.tsx +6 -6
  373. package/source/elements/empty.tsx +1 -1
  374. package/source/elements/field.tsx +1 -1
  375. package/source/elements/hover-card.tsx +1 -1
  376. package/source/elements/input-group.tsx +4 -4
  377. package/source/elements/input.tsx +1 -1
  378. package/source/elements/item.tsx +1 -1
  379. package/source/elements/menubar.tsx +8 -8
  380. package/source/elements/native-select.tsx +1 -1
  381. package/source/elements/popover.tsx +1 -1
  382. package/source/elements/select.tsx +3 -3
  383. package/source/elements/sidebar.tsx +8 -21
  384. package/source/elements/tabs.tsx +2 -2
  385. package/source/elements/textarea.tsx +1 -1
  386. package/source/elements/toggle-group.tsx +2 -2
  387. package/source/elements/toggle.tsx +1 -1
  388. package/source/env.d.ts +1 -0
  389. package/source/head.ts +7 -0
  390. package/source/helpers/agent-skills.ts +59 -0
  391. package/source/helpers/agent-skills.unit.ts +56 -0
  392. package/source/helpers/article-search.tsx +192 -0
  393. package/source/helpers/article.ts +17 -0
  394. package/source/helpers/dependency-dir.ts +20 -0
  395. package/source/helpers/dependency-dir.unit.ts +39 -0
  396. package/source/helpers/head.ts +105 -0
  397. package/source/helpers/head.unit.ts +118 -0
  398. package/source/helpers/llms.ts +109 -0
  399. package/source/helpers/llms.unit.ts +115 -0
  400. package/source/helpers/nav-items.tsx +102 -0
  401. package/source/helpers/render-inline-markdown.ts +44 -0
  402. package/source/helpers/render-inline-markdown.unit.ts +30 -0
  403. package/source/helpers/search-highlight.ts +73 -0
  404. package/source/helpers/search-highlight.unit.ts +42 -0
  405. package/source/helpers/target-export.ts +28 -0
  406. package/source/helpers/target-export.unit.ts +37 -0
  407. package/source/helpers/theme.ts +40 -6
  408. package/source/helpers/theme.unit.ts +32 -0
  409. package/source/hooks/hydrated.ts +18 -0
  410. package/source/index.ts +1 -0
  411. package/source/models/config.ts +24 -0
  412. package/source/models/section.ts +3 -0
  413. package/source/plugins/vite-agents.ts +135 -0
  414. package/source/plugins/vite-agents.unit.ts +26 -0
  415. package/source/plugins/vite-livemark.ts +54 -95
  416. package/source/plugins/vite-robots.ts +18 -3
  417. package/source/plugins/vite-robots.unit.ts +7 -0
  418. package/source/program.ts +2 -0
  419. package/source/routes/$.tsx +27 -9
  420. package/source/routes/__root.tsx +51 -71
  421. package/source/settings.ts +1 -0
  422. package/source/styles/general.css +59 -11
  423. package/source/styles/markdown.css +13 -7
  424. package/source/styles/shape.css +14 -0
  425. package/source/vite.config.ts +2 -0
  426. package/.agents/skills/change-review/SKILL.md +0 -47
  427. package/.agents/skills/livemark-customization/SKILL.md +0 -118
@@ -1,6 +1,8 @@
1
+ import { useRef } from "react"
1
2
  import { MDXContent } from "@content-collections/mdx/react"
2
3
  import { Calendar, History, User } from "lucide-react"
3
4
  import type { ArticleView } from "../models/article.ts"
5
+ import { useSearchHighlight } from "../helpers/search-highlight.ts"
4
6
  import { TocContext } from "../helpers/toc-context.ts"
5
7
  import { Abbr } from "./Abbr.tsx"
6
8
  import { AnsiCode } from "./AnsiCode.tsx"
@@ -42,8 +44,10 @@ import { ZoomImage } from "./ZoomImage.tsx"
42
44
  import { Separator } from "../elements/separator.tsx"
43
45
 
44
46
  /** Full-page article layout: MDX content, table of contents, toolbar, prev/next, footer */
45
- export function Article(props: { article: ArticleView }) {
47
+ export function Article(props: { article: ArticleView; highlight?: string }) {
46
48
  const { article } = props
49
+ const prose = useRef<HTMLDivElement>(null)
50
+ useSearchHighlight(prose, article.path, props.highlight)
47
51
  const lastUpdated = formatDate(article.lastUpdated)
48
52
  const publishedDate = formatDate(article.date)
49
53
  const authors = article.author
@@ -56,7 +60,7 @@ export function Article(props: { article: ArticleView }) {
56
60
  return (
57
61
  <TocContext.Provider value={article.tocItems}>
58
62
  {article.toc !== false && (
59
- <MobileToc items={article.tocItems}>
63
+ <MobileToc items={article.tocItems} title={article.title}>
60
64
  <PageToolbar
61
65
  file={article.file}
62
66
  sourceUrl={article.sourceUrl}
@@ -67,7 +71,7 @@ export function Article(props: { article: ArticleView }) {
67
71
  )}
68
72
  <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
69
73
  <div className="flex-1 min-w-0 mx-auto max-w-3xl">
70
- <div className="prose dark:prose-invert max-w-none">
74
+ <div ref={prose} className="prose dark:prose-invert max-w-none">
71
75
  <h1 id="top">
72
76
  <a href="#top">{article.title}</a>
73
77
  </h1>
@@ -97,7 +101,7 @@ export function Article(props: { article: ArticleView }) {
97
101
  <img
98
102
  src={article.image}
99
103
  alt=""
100
- className="!mt-0 !mb-6 w-full rounded-xl border shadow-sm"
104
+ className="!mt-0 !mb-6 w-full rounded-card border shadow-sm"
101
105
  />
102
106
  )}
103
107
  <MDXContent
@@ -1,4 +1,14 @@
1
- /** Optional header banner. Empty by default; shadow via `.livemark/components/Banner.tsx` to render content. */
1
+ import { bannerHtml } from "livemark:virtual"
2
+
3
+ /** Announcement strip above the header, rendered from the `banner` config option. */
2
4
  export function Banner() {
3
- return null
5
+ if (!bannerHtml) return null
6
+ return (
7
+ <div className="bg-banner text-banner-foreground">
8
+ <div
9
+ className="min-w-0 px-4 py-2 text-center font-mono text-xs tracking-wide text-balance sm:px-6 [&_a]:underline [&_a]:underline-offset-4 [&_a:hover]:no-underline [&_code]:font-semibold"
10
+ dangerouslySetInnerHTML={{ __html: bannerHtml }}
11
+ />
12
+ </div>
13
+ )
4
14
  }
@@ -40,7 +40,7 @@ export function BlogIndex(props: { sectionPrefix: string }) {
40
40
 
41
41
  return (
42
42
  <TocContext.Provider value={tocItems}>
43
- <MobileToc items={tocItems}>
43
+ <MobileToc items={tocItems} title={section?.title ?? "Blog"}>
44
44
  <PageToolbar content={content} />
45
45
  </MobileToc>
46
46
  <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
@@ -25,20 +25,18 @@ const configs: Record<CalloutType, CalloutConfig> = {
25
25
  tip: {
26
26
  icon: Lightbulb,
27
27
  label: "Tip",
28
- classes:
29
- "border-green-500/30 bg-green-50/50 text-green-900 dark:bg-green-950/30 dark:text-green-200",
28
+ classes: "border-positive/30 bg-positive-tint/50 text-positive-tint-foreground",
30
29
  },
31
30
  warning: {
32
31
  icon: AlertTriangle,
33
32
  label: "Warning",
34
- classes:
35
- "border-yellow-500/30 bg-yellow-50/50 text-yellow-900 dark:bg-yellow-950/30 dark:text-yellow-200",
33
+ classes: "border-warning/30 bg-warning-tint/50 text-warning-tint-foreground",
36
34
  },
37
35
  danger: {
38
36
  icon: Flame,
39
37
  label: "Danger",
40
38
  classes:
41
- "border-red-500/30 bg-red-50/50 text-red-900 dark:bg-red-950/30 dark:text-red-200",
39
+ "border-destructive/30 bg-destructive-tint/50 text-destructive-tint-foreground",
42
40
  },
43
41
  }
44
42
 
@@ -54,7 +52,7 @@ export function Callout(props: {
54
52
  const title = props.title ?? config.label
55
53
 
56
54
  return (
57
- <aside className={cn("not-prose my-4 rounded-xl border px-4 py-3", config.classes)}>
55
+ <aside className={cn("not-prose my-4 rounded-card border px-4 py-3", config.classes)}>
58
56
  <div className="flex items-center gap-2 font-semibold text-sm mb-2">
59
57
  <Icon className="size-4 shrink-0" />
60
58
  <span>{title}</span>
@@ -37,7 +37,7 @@ export function ChangelogIndex(props: { sectionPrefix: string }) {
37
37
 
38
38
  return (
39
39
  <TocContext.Provider value={tocItems}>
40
- <MobileToc items={tocItems}>
40
+ <MobileToc items={tocItems} title={section?.title ?? "Changelog"}>
41
41
  <PageToolbar content={content} />
42
42
  </MobileToc>
43
43
  <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
@@ -0,0 +1,69 @@
1
+ import { CheckIcon } from "lucide-react"
2
+ import type { MouseEvent, ReactNode } from "react"
3
+ import { cn } from "cn"
4
+
5
+ export type Choice = {
6
+ id: string
7
+ label: ReactNode
8
+ icon?: ReactNode
9
+ href?: string
10
+ /** Indent level below the top one, for a nested choice like a sub-heading. */
11
+ depth?: number
12
+ }
13
+
14
+ /** The rows of a sheet that picks one of a list, the selected one tinted and ticked. */
15
+ export function ChoiceList(props: {
16
+ choices: Choice[]
17
+ selectedId?: string
18
+ onSelect?: (choice: Choice, event: MouseEvent<HTMLElement>) => void
19
+ }) {
20
+ return (
21
+ <ul className="-mx-2 flex flex-col gap-0.5">
22
+ {props.choices.map(choice => {
23
+ const selected = choice.id === props.selectedId
24
+ const className = cn(
25
+ "hover:bg-muted active:bg-muted flex h-10 w-full items-center gap-2.5 rounded-control px-2 text-left text-base outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-[18px] [&_svg]:shrink-0",
26
+ selected && "bg-sidebar-accent text-sidebar-accent-foreground font-medium",
27
+ )
28
+ const content = (
29
+ <>
30
+ {choice.icon}
31
+ <span
32
+ className={cn(
33
+ "flex-1 truncate",
34
+ choice.depth === 1 && "ps-4",
35
+ (choice.depth ?? 0) >= 2 && "ps-8",
36
+ )}
37
+ >
38
+ {choice.label}
39
+ </span>
40
+ {selected && <CheckIcon className="text-primary" />}
41
+ </>
42
+ )
43
+ return (
44
+ <li key={choice.id}>
45
+ {choice.href ? (
46
+ <a
47
+ href={choice.href}
48
+ aria-current={selected || undefined}
49
+ className={className}
50
+ onClick={event => props.onSelect?.(choice, event)}
51
+ >
52
+ {content}
53
+ </a>
54
+ ) : (
55
+ <button
56
+ type="button"
57
+ aria-pressed={selected}
58
+ className={className}
59
+ onClick={event => props.onSelect?.(choice, event)}
60
+ >
61
+ {content}
62
+ </button>
63
+ )}
64
+ </li>
65
+ )
66
+ })}
67
+ </ul>
68
+ )
69
+ }
@@ -28,7 +28,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
28
28
 
29
29
  return (
30
30
  <figure
31
- className="shiki not-prose my-4 relative rounded-xl border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar"
31
+ className="shiki not-prose my-4 relative rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar"
32
32
  style={{ ...style, backgroundColor: undefined }}
33
33
  >
34
34
  {title ? (
@@ -44,7 +44,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
44
44
  <TooltipTrigger
45
45
  onClick={handleWrapToggle}
46
46
  aria-label={wrap ? "Disable wrap" : "Enable wrap"}
47
- className={`-me-2 p-1.5 rounded-md hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
47
+ className={`-me-2 p-1.5 rounded-control hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
48
48
  >
49
49
  <WrapText className="size-3.5" />
50
50
  </TooltipTrigger>
@@ -54,7 +54,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
54
54
  <TooltipTrigger
55
55
  onClick={handleCopy}
56
56
  aria-label={copied ? "Copied" : "Copy code"}
57
- className="-me-2 p-1.5 rounded-md hover:text-foreground transition-colors"
57
+ className="-me-2 p-1.5 rounded-control hover:text-foreground transition-colors"
58
58
  >
59
59
  {copied ? (
60
60
  <Check className="size-3.5" />
@@ -66,12 +66,12 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
66
66
  </Tooltip>
67
67
  </div>
68
68
  ) : (
69
- <div className="empty:hidden absolute top-2 right-2 z-10 backdrop-blur-lg rounded-lg text-muted-foreground">
69
+ <div className="empty:hidden absolute top-2 right-2 z-10 backdrop-blur-lg rounded-control text-muted-foreground">
70
70
  <Tooltip>
71
71
  <TooltipTrigger
72
72
  onClick={handleWrapToggle}
73
73
  aria-label={wrap ? "Disable wrap" : "Enable wrap"}
74
- className={`p-1.5 rounded-md hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
74
+ className={`p-1.5 rounded-control hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
75
75
  >
76
76
  <WrapText className="size-3.5" />
77
77
  </TooltipTrigger>
@@ -81,7 +81,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
81
81
  <TooltipTrigger
82
82
  onClick={handleCopy}
83
83
  aria-label={copied ? "Copied" : "Copy code"}
84
- className="p-1.5 rounded-md hover:text-foreground transition-colors"
84
+ className="p-1.5 rounded-control hover:text-foreground transition-colors"
85
85
  >
86
86
  {copied ? (
87
87
  <Check className="size-3.5" />
@@ -47,7 +47,7 @@ export function CodeTabs(props: {
47
47
  }, [props.sync, names])
48
48
 
49
49
  return (
50
- <div className="not-prose my-4 rounded-xl border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
50
+ <div className="not-prose my-4 rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
51
51
  <div className="flex border-b">
52
52
  {names.map((name, i) => (
53
53
  <button
@@ -45,7 +45,7 @@ export function ContentTabs(props: { sync?: string; children: React.ReactNode })
45
45
  }, [props.sync, titles])
46
46
 
47
47
  return (
48
- <div className="not-prose my-4 rounded-xl border bg-card">
48
+ <div className="not-prose my-4 rounded-card border bg-card">
49
49
  <div className="flex border-b">
50
50
  {titles.map((title, i) => (
51
51
  <button
@@ -6,6 +6,7 @@ import {
6
6
  useMatch,
7
7
  useRouter,
8
8
  } from "@tanstack/react-router"
9
+ import { buttonVariants } from "../elements/button.tsx"
9
10
 
10
11
  export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
11
12
  const router = useRouter()
@@ -25,21 +26,18 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
25
26
  onClick={() => {
26
27
  router.invalidate()
27
28
  }}
28
- className="bg-primary text-primary-foreground px-4 py-2 text-sm font-medium hover:bg-primary/80 transition-colors"
29
+ className={buttonVariants({ size: "lg" })}
29
30
  >
30
31
  Try Again
31
32
  </button>
32
33
  {isRoot ? (
33
- <Link
34
- to="/"
35
- className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
36
- >
34
+ <Link to="/" className={buttonVariants({ variant: "outline", size: "lg" })}>
37
35
  Home
38
36
  </Link>
39
37
  ) : (
40
38
  <Link
41
39
  to="/"
42
- className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
40
+ className={buttonVariants({ variant: "outline", size: "lg" })}
43
41
  onClick={(e: any) => {
44
42
  e.preventDefault()
45
43
  window.history.back()
@@ -14,7 +14,7 @@ export function Details(props: { summary: string; children: React.ReactNode }) {
14
14
  <Collapsible
15
15
  open={open}
16
16
  onOpenChange={setOpen}
17
- className="not-prose my-4 rounded-xl border border-border bg-card px-4 py-3"
17
+ className="not-prose my-4 rounded-card border border-border bg-card px-4 py-3"
18
18
  >
19
19
  <CollapsibleTrigger className="flex w-full cursor-pointer items-center gap-2 text-sm font-semibold select-none">
20
20
  <ChevronRight
@@ -11,7 +11,7 @@ export function FileTree(props: { tree: string }) {
11
11
  const items: TreeItem[] = JSON.parse(props.tree)
12
12
 
13
13
  return (
14
- <div className="not-prose my-4 rounded-xl border border-border bg-card p-4 font-mono text-sm">
14
+ <div className="not-prose my-4 rounded-card border border-border bg-card p-4 font-mono text-sm">
15
15
  <TreeItems items={items} depth={0} />
16
16
  </div>
17
17
  )
@@ -0,0 +1,52 @@
1
+ import { config } from "livemark:virtual"
2
+ import { useLocation, useMatch } from "@tanstack/react-router"
3
+ import { useState } from "react"
4
+ import { cn } from "cn"
5
+ import { currentSection } from "../content/article.ts"
6
+ import { activeSiteLink } from "../helpers/section.ts"
7
+ import { HeaderMenu } from "./HeaderMenu.tsx"
8
+ import { HeaderNav } from "./HeaderNav.tsx"
9
+ import { Search } from "./Search.tsx"
10
+ import { SiteLink } from "./SiteLink.tsx"
11
+ import { SiteTitle } from "./SiteTitle.tsx"
12
+ import { Theme } from "./Theme.tsx"
13
+
14
+ /** Sticky site header: title, header sections and links, search and theme. `toggle`
15
+ * sits before the title from `lg`; `menu` opens the phone navigation */
16
+ export function Header(props: { toggle?: React.ReactNode; menu?: React.ReactNode }) {
17
+ const pathname = useLocation({ select: l => l.pathname })
18
+ const articleRoute = useMatch({ from: "/$", shouldThrow: false })
19
+ const activeSection = articleRoute
20
+ ? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
21
+ : undefined
22
+ const siteLinkTarget = activeSiteLink(pathname, config.sections ?? [])
23
+ const [compactSearch, setCompactSearch] = useState(false)
24
+
25
+ return (
26
+ <header className="sticky top-0 z-20 flex has-[[data-header-search]]:z-40 h-16 shrink-0 items-center border-b bg-background">
27
+ <div
28
+ className={cn(
29
+ "flex min-w-0 items-center self-stretch pl-4 lg:shrink-0",
30
+ props.toggle ? "pr-4" : "lg:w-[calc(var(--sidebar-width,16rem)+4rem)]",
31
+ )}
32
+ >
33
+ {props.toggle && <div className="hidden lg:flex">{props.toggle}</div>}
34
+ <SiteLink
35
+ to={siteLinkTarget}
36
+ className={cn("flex min-w-0 items-center gap-2", props.toggle && "lg:hidden")}
37
+ >
38
+ <SiteTitle />
39
+ </SiteLink>
40
+ </div>
41
+ <HeaderNav
42
+ activePrefix={activeSection?.prefix}
43
+ onCompactChange={setCompactSearch}
44
+ />
45
+ <div className="ml-auto flex shrink-0 items-center gap-1 pr-2 lg:pr-1">
46
+ <Search compact={compactSearch} />
47
+ <Theme />
48
+ <div className="lg:hidden">{props.menu ?? <HeaderMenu />}</div>
49
+ </div>
50
+ </header>
51
+ )
52
+ }
@@ -0,0 +1,41 @@
1
+ import { config } from "livemark:virtual"
2
+ import { useNavigate } from "@tanstack/react-router"
3
+ import { Menu } from "lucide-react"
4
+ import { Button } from "../elements/button.tsx"
5
+ import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
6
+ import { useNavItems } from "../helpers/nav-items.tsx"
7
+ import { prefixUrl } from "../helpers/prefix-url.ts"
8
+ import { MenuSheet } from "./MenuSheet.tsx"
9
+
10
+ /** Phone navigation for a `Header` without a sidebar: the header sections and links */
11
+ export function HeaderMenu() {
12
+ const items = useNavItems()
13
+ const navigate = useNavigate()
14
+
15
+ return (
16
+ <MenuSheet
17
+ title="Navigation"
18
+ items={items.map(item => ({
19
+ id: item.key,
20
+ label: item.suffix ? `${item.title} (${item.suffix})` : item.title,
21
+ icon: item.icon && <DynamicIcon name={item.icon} className="size-4 shrink-0" />,
22
+ active: item.active,
23
+ onSelect: () => {
24
+ if (item.href) window.location.assign(prefixUrl(item.href, config.base))
25
+ else if (item.target) navigate({ to: item.target })
26
+ },
27
+ }))}
28
+ trigger={
29
+ <Button
30
+ variant="ghost"
31
+ size="icon-sm"
32
+ title="Navigation"
33
+ aria-label="Navigation"
34
+ className="cursor-pointer"
35
+ />
36
+ }
37
+ >
38
+ <Menu />
39
+ </MenuSheet>
40
+ )
41
+ }
@@ -0,0 +1,145 @@
1
+ import { ChevronDown } from "lucide-react"
2
+ import { useLayoutEffect, useRef, useState } from "react"
3
+ import { cn } from "cn"
4
+ import { type NavItem, renderNavAnchor, useNavItems } from "../helpers/nav-items.tsx"
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ } from "../elements/dropdown-menu.tsx"
11
+
12
+ // The `lg:w-56` Search field minus its 36px icon-only form
13
+ const SEARCH_COMPACT_SAVING = 188
14
+
15
+ /** Header sections and links from `lg`: once they outgrow the bar, Search turns
16
+ * compact and the rest fold into a "More" menu */
17
+ export function HeaderNav(props: {
18
+ activePrefix?: string
19
+ onCompactChange: (compact: boolean) => void
20
+ }) {
21
+ const items = useNavItems(props.activePrefix)
22
+ const strip = useRef<HTMLElement>(null)
23
+ const measure = useRef<HTMLDivElement>(null)
24
+ const [visibleCount, setVisibleCount] = useState(items.length)
25
+ const onCompactChange = useRef(props.onCompactChange)
26
+ onCompactChange.current = props.onCompactChange
27
+
28
+ useLayoutEffect(() => {
29
+ let compact = false
30
+ const fit = () => {
31
+ if (!strip.current || !measure.current) return
32
+ const style = getComputedStyle(strip.current)
33
+ const width =
34
+ strip.current.clientWidth -
35
+ parseFloat(style.paddingLeft) -
36
+ parseFloat(style.paddingRight)
37
+ if (width <= 0) return
38
+ // Measured as if Search were full, so toggling it never feeds back into the fit
39
+ const room = width - (compact ? SEARCH_COMPACT_SAVING : 0)
40
+ const gap = parseFloat(getComputedStyle(measure.current).columnGap) || 0
41
+ const widths = [...measure.current.children].map(
42
+ child => child.getBoundingClientRect().width,
43
+ )
44
+ const more = widths.pop() ?? 0
45
+ const total = widths.reduce((sum, w) => sum + w, 0) + gap * (widths.length - 1)
46
+
47
+ compact = total > room
48
+ let count = widths.length
49
+ if (total > room + SEARCH_COMPACT_SAVING) {
50
+ let used = more
51
+ count = 0
52
+ for (const w of widths) {
53
+ if (used + gap + w > room + SEARCH_COMPACT_SAVING) break
54
+ used += gap + w
55
+ count++
56
+ }
57
+ }
58
+ onCompactChange.current(compact)
59
+ setVisibleCount(count)
60
+ }
61
+ fit()
62
+ const observer = new ResizeObserver(fit)
63
+ if (strip.current) observer.observe(strip.current)
64
+ if (measure.current) observer.observe(measure.current)
65
+ return () => observer.disconnect()
66
+ }, [])
67
+
68
+ const shown = items.slice(0, visibleCount)
69
+ const folded = items.slice(visibleCount)
70
+
71
+ return (
72
+ <nav
73
+ ref={strip}
74
+ className="relative hidden w-0 min-w-0 flex-1 items-center gap-6 self-stretch overflow-hidden px-6 text-sm lg:flex xl:gap-8"
75
+ >
76
+ {shown.map(item => (
77
+ <HeaderNavLink key={item.key} item={item} />
78
+ ))}
79
+ {folded.length > 0 && <HeaderNavMore items={folded} />}
80
+ <div
81
+ ref={measure}
82
+ aria-hidden
83
+ inert
84
+ className="invisible absolute left-0 flex gap-6 whitespace-nowrap xl:gap-8"
85
+ >
86
+ {items.map(item => (
87
+ <HeaderNavLink key={item.key} item={item} />
88
+ ))}
89
+ <HeaderNavMoreTrigger active={false} />
90
+ </div>
91
+ </nav>
92
+ )
93
+ }
94
+
95
+ function HeaderNavLink(props: { item: NavItem }) {
96
+ return renderNavAnchor(
97
+ props.item,
98
+ cn(
99
+ "inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap text-foreground",
100
+ props.item.active ? ACTIVE : IDLE,
101
+ ),
102
+ )
103
+ }
104
+
105
+ function HeaderNavMore(props: { items: NavItem[] }) {
106
+ return (
107
+ <DropdownMenu>
108
+ <DropdownMenuTrigger
109
+ render={<HeaderNavMoreTrigger active={props.items.some(item => item.active)} />}
110
+ />
111
+ <DropdownMenuContent align="end" className="w-auto">
112
+ {props.items.map(item => (
113
+ <DropdownMenuItem
114
+ key={item.key}
115
+ className={cn("cursor-pointer", item.active && "font-medium")}
116
+ render={renderNavAnchor(item)}
117
+ />
118
+ ))}
119
+ </DropdownMenuContent>
120
+ </DropdownMenu>
121
+ )
122
+ }
123
+
124
+ function HeaderNavMoreTrigger(
125
+ props: React.ComponentProps<"button"> & { active: boolean },
126
+ ) {
127
+ const { active, className, ...rest } = props
128
+ return (
129
+ <button
130
+ type="button"
131
+ {...rest}
132
+ className={cn(
133
+ "inline-flex shrink-0 cursor-pointer items-center gap-1 whitespace-nowrap text-foreground",
134
+ active ? ACTIVE : IDLE,
135
+ className,
136
+ )}
137
+ >
138
+ More
139
+ <ChevronDown className="size-3.5" />
140
+ </button>
141
+ )
142
+ }
143
+
144
+ const ACTIVE = "font-medium border-b-2 border-foreground pb-0.5 -mb-0.5"
145
+ const IDLE = "opacity-80 hover:opacity-100 transition-opacity"
@@ -0,0 +1,77 @@
1
+ import { useLayoutEffect, useRef, useState } from "react"
2
+ import { X } from "lucide-react"
3
+ import { SearchResultGroups, useArticleSearch } from "../helpers/article-search.tsx"
4
+ import { Command, CommandEmpty, CommandInput, CommandList } from "../elements/command.tsx"
5
+
6
+ /** Search field that takes over the header below `lg`, with results dropping under it */
7
+ export function HeaderSearch(props: { onClose: () => void }) {
8
+ const search = useArticleSearch()
9
+ const bar = useRef<HTMLDivElement>(null)
10
+ const [panelTop, setPanelTop] = useState<number>()
11
+
12
+ // Measured, not `top-16`: while the Banner is in view it pushes the header down.
13
+ useLayoutEffect(() => {
14
+ const measure = () => setPanelTop(bar.current?.getBoundingClientRect().bottom)
15
+ measure()
16
+ window.addEventListener("scroll", measure, { passive: true })
17
+ window.addEventListener("resize", measure)
18
+ return () => {
19
+ window.removeEventListener("scroll", measure)
20
+ window.removeEventListener("resize", measure)
21
+ }
22
+ }, [])
23
+
24
+ // `data-header-search` lifts the header over the Toc bar and BackToTop (Layout).
25
+ return (
26
+ <Command
27
+ ref={bar}
28
+ data-header-search
29
+ shouldFilter={false}
30
+ loop
31
+ onKeyDown={event => {
32
+ if (event.key === "Escape") props.onClose()
33
+ }}
34
+ className="absolute inset-0 z-10 overflow-visible rounded-none! bg-background p-0 lg:hidden"
35
+ >
36
+ <div className="flex h-full items-center gap-1 pl-1 pr-2">
37
+ <div className="flex-1 [&_[data-slot=command-input-wrapper]]:p-0 [&_[data-slot=input-group]]:border-transparent! [&_[data-slot=input-group]]:bg-transparent! [&_[data-slot=input-group]]:ring-0!">
38
+ <CommandInput
39
+ autoFocus
40
+ placeholder="Search docs..."
41
+ value={search.query}
42
+ onValueChange={search.searchArticles}
43
+ />
44
+ </div>
45
+ <button
46
+ type="button"
47
+ onClick={props.onClose}
48
+ aria-label="Close search"
49
+ title="Close search"
50
+ className="flex size-9 shrink-0 items-center justify-center rounded-control text-muted-foreground hover:bg-muted hover:text-foreground transition-colors cursor-pointer"
51
+ >
52
+ <X className="size-5" />
53
+ </button>
54
+ </div>
55
+ <div
56
+ style={{ top: panelTop }}
57
+ className="fixed inset-x-0 bottom-0 flex flex-col border-t bg-background p-1"
58
+ >
59
+ <CommandList className="max-h-none flex-1 overscroll-contain">
60
+ <CommandEmpty>
61
+ {search.query.trim()
62
+ ? "No results found."
63
+ : "Type to search across all contents."}
64
+ </CommandEmpty>
65
+ <SearchResultGroups
66
+ query={search.query}
67
+ grouped={search.grouped}
68
+ onSelect={path => {
69
+ props.onClose()
70
+ search.selectArticle(path)
71
+ }}
72
+ />
73
+ </CommandList>
74
+ </div>
75
+ </Command>
76
+ )
77
+ }
@@ -3,7 +3,7 @@ import { Badge } from "../elements/badge.tsx"
3
3
  /** Inline badge rendered from :badge text directives */
4
4
  export function InlineBadge(props: {
5
5
  label: string
6
- variant?: "default" | "secondary" | "destructive" | "outline"
6
+ variant?: "default" | "secondary" | "positive" | "warning" | "destructive" | "outline"
7
7
  }) {
8
8
  return <Badge variant={props.variant}>{props.label}</Badge>
9
9
  }
@@ -9,7 +9,7 @@ export function InlineToc(props: { maxLevel?: string }) {
9
9
  if (items.length === 0) return null
10
10
 
11
11
  return (
12
- <nav className="not-prose my-8 rounded-xl border border-border bg-card p-6">
12
+ <nav className="not-prose my-8 rounded-card border border-border bg-card p-6">
13
13
  <p className="text-base font-semibold mb-4">Table of Contents</p>
14
14
  <ul className="text-base space-y-2.5">
15
15
  {items.map(item => (