@thomasfosterau/components 0.0.0-stage → 0.1.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 (340) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +817 -2
  3. package/dist/canvas/VectorCanvas.svelte +239 -0
  4. package/dist/canvas/VectorCanvas.svelte.d.ts +87 -0
  5. package/dist/canvas/history.d.ts +105 -0
  6. package/dist/canvas/history.js +147 -0
  7. package/dist/canvas/index.d.ts +15 -0
  8. package/dist/canvas/index.js +14 -0
  9. package/dist/canvas/types.d.ts +91 -0
  10. package/dist/canvas/types.js +13 -0
  11. package/dist/canvas/utils.d.ts +66 -0
  12. package/dist/canvas/utils.js +114 -0
  13. package/dist/charts/Chart.svelte +300 -0
  14. package/dist/charts/Chart.svelte.d.ts +50 -0
  15. package/dist/charts/index.d.ts +11 -0
  16. package/dist/charts/index.js +10 -0
  17. package/dist/charts/types.d.ts +28 -0
  18. package/dist/charts/types.js +6 -0
  19. package/dist/citations/BookCitation.svelte +53 -0
  20. package/dist/citations/BookCitation.svelte.d.ts +41 -0
  21. package/dist/citations/ChapterCitation.svelte +38 -0
  22. package/dist/citations/ChapterCitation.svelte.d.ts +50 -0
  23. package/dist/citations/Citation.svelte +47 -0
  24. package/dist/citations/Citation.svelte.d.ts +24 -0
  25. package/dist/citations/CreditList.svelte +69 -0
  26. package/dist/citations/CreditList.svelte.d.ts +47 -0
  27. package/dist/citations/DOILink.svelte +28 -0
  28. package/dist/citations/DOILink.svelte.d.ts +19 -0
  29. package/dist/citations/Footnote.svelte +39 -0
  30. package/dist/citations/Footnote.svelte.d.ts +17 -0
  31. package/dist/citations/FootnoteList.svelte +36 -0
  32. package/dist/citations/FootnoteList.svelte.d.ts +21 -0
  33. package/dist/citations/ISBNLink.svelte +29 -0
  34. package/dist/citations/ISBNLink.svelte.d.ts +19 -0
  35. package/dist/citations/ISSNLink.svelte +29 -0
  36. package/dist/citations/ISSNLink.svelte.d.ts +19 -0
  37. package/dist/citations/Publisher.svelte +34 -0
  38. package/dist/citations/Publisher.svelte.d.ts +14 -0
  39. package/dist/citations/ReferenceList.svelte +53 -0
  40. package/dist/citations/ReferenceList.svelte.d.ts +21 -0
  41. package/dist/citations/ReportCitation.svelte +41 -0
  42. package/dist/citations/ReportCitation.svelte.d.ts +36 -0
  43. package/dist/citations/ScholarlyArticleCitation.svelte +64 -0
  44. package/dist/citations/ScholarlyArticleCitation.svelte.d.ts +41 -0
  45. package/dist/citations/ThesisCitation.svelte +35 -0
  46. package/dist/citations/ThesisCitation.svelte.d.ts +29 -0
  47. package/dist/citations/bibliography.svelte.d.ts +55 -0
  48. package/dist/citations/bibliography.svelte.js +78 -0
  49. package/dist/citations/citation.d.ts +71 -0
  50. package/dist/citations/citation.js +121 -0
  51. package/dist/citations/csl.d.ts +102 -0
  52. package/dist/citations/csl.js +20 -0
  53. package/dist/citations/index.d.ts +31 -0
  54. package/dist/citations/index.js +36 -0
  55. package/dist/citations/list-format.d.ts +34 -0
  56. package/dist/citations/list-format.js +38 -0
  57. package/dist/context.svelte.d.ts +46 -0
  58. package/dist/context.svelte.js +51 -0
  59. package/dist/data/DataGrid.svelte +150 -0
  60. package/dist/data/DataGrid.svelte.d.ts +118 -0
  61. package/dist/data/DescriptionList.svelte +47 -0
  62. package/dist/data/DescriptionList.svelte.d.ts +26 -0
  63. package/dist/data/DescriptionRow.svelte +55 -0
  64. package/dist/data/DescriptionRow.svelte.d.ts +21 -0
  65. package/dist/data/DiffText.svelte +29 -0
  66. package/dist/data/DiffText.svelte.d.ts +19 -0
  67. package/dist/data/ItemList.svelte +80 -0
  68. package/dist/data/ItemList.svelte.d.ts +52 -0
  69. package/dist/data/LinkListItem.svelte +46 -0
  70. package/dist/data/LinkListItem.svelte.d.ts +21 -0
  71. package/dist/data/Pagination.svelte +136 -0
  72. package/dist/data/Pagination.svelte.d.ts +71 -0
  73. package/dist/data/PaginationFooter.svelte +41 -0
  74. package/dist/data/PaginationFooter.svelte.d.ts +59 -0
  75. package/dist/data/ResultCount.svelte +27 -0
  76. package/dist/data/ResultCount.svelte.d.ts +18 -0
  77. package/dist/data/Table.svelte +145 -0
  78. package/dist/data/Table.svelte.d.ts +75 -0
  79. package/dist/data/TreeList.svelte +41 -0
  80. package/dist/data/TreeList.svelte.d.ts +17 -0
  81. package/dist/data/TreeListItem.svelte +29 -0
  82. package/dist/data/TreeListItem.svelte.d.ts +10 -0
  83. package/dist/data/description-context.d.ts +33 -0
  84. package/dist/data/description-context.js +35 -0
  85. package/dist/data/diff.d.ts +14 -0
  86. package/dist/data/diff.js +10 -0
  87. package/dist/data/index.d.ts +35 -0
  88. package/dist/data/index.js +35 -0
  89. package/dist/data/list-item.d.ts +30 -0
  90. package/dist/data/list-item.js +19 -0
  91. package/dist/data/pagination.d.ts +81 -0
  92. package/dist/data/pagination.js +79 -0
  93. package/dist/data/table-types.d.ts +64 -0
  94. package/dist/data/table-types.js +35 -0
  95. package/dist/feedback/ConfirmDialog.svelte +79 -0
  96. package/dist/feedback/ConfirmDialog.svelte.d.ts +82 -0
  97. package/dist/feedback/Dialog.svelte +77 -0
  98. package/dist/feedback/Dialog.svelte.d.ts +37 -0
  99. package/dist/feedback/LoadingBoundary.svelte +61 -0
  100. package/dist/feedback/LoadingBoundary.svelte.d.ts +34 -0
  101. package/dist/feedback/ProgressBar.svelte +46 -0
  102. package/dist/feedback/ProgressBar.svelte.d.ts +28 -0
  103. package/dist/feedback/SegmentBar.svelte +83 -0
  104. package/dist/feedback/SegmentBar.svelte.d.ts +36 -0
  105. package/dist/feedback/StatTile.svelte +77 -0
  106. package/dist/feedback/StatTile.svelte.d.ts +49 -0
  107. package/dist/feedback/Stats.svelte +18 -0
  108. package/dist/feedback/Stats.svelte.d.ts +16 -0
  109. package/dist/feedback/Timeline.svelte +86 -0
  110. package/dist/feedback/Timeline.svelte.d.ts +52 -0
  111. package/dist/feedback/Toaster.svelte +78 -0
  112. package/dist/feedback/Toaster.svelte.d.ts +32 -0
  113. package/dist/feedback/index.d.ts +31 -0
  114. package/dist/feedback/index.js +27 -0
  115. package/dist/feedback/sizes.d.ts +27 -0
  116. package/dist/feedback/sizes.js +41 -0
  117. package/dist/feedback/toaster.d.ts +48 -0
  118. package/dist/feedback/toaster.js +29 -0
  119. package/dist/fields/Checkbox.svelte +33 -0
  120. package/dist/fields/Checkbox.svelte.d.ts +24 -0
  121. package/dist/fields/CheckboxGroup.svelte +41 -0
  122. package/dist/fields/CheckboxGroup.svelte.d.ts +40 -0
  123. package/dist/fields/Combobox.svelte +222 -0
  124. package/dist/fields/Combobox.svelte.d.ts +98 -0
  125. package/dist/fields/DateInput.svelte +31 -0
  126. package/dist/fields/DateInput.svelte.d.ts +29 -0
  127. package/dist/fields/FieldErrors.svelte +30 -0
  128. package/dist/fields/FieldErrors.svelte.d.ts +11 -0
  129. package/dist/fields/Fieldset.svelte +48 -0
  130. package/dist/fields/Fieldset.svelte.d.ts +26 -0
  131. package/dist/fields/FileInput.svelte +67 -0
  132. package/dist/fields/FileInput.svelte.d.ts +33 -0
  133. package/dist/fields/FormField.svelte +54 -0
  134. package/dist/fields/FormField.svelte.d.ts +29 -0
  135. package/dist/fields/ImageInput.svelte +87 -0
  136. package/dist/fields/ImageInput.svelte.d.ts +39 -0
  137. package/dist/fields/PushToggle.svelte +93 -0
  138. package/dist/fields/PushToggle.svelte.d.ts +55 -0
  139. package/dist/fields/RadioGroup.svelte +85 -0
  140. package/dist/fields/RadioGroup.svelte.d.ts +41 -0
  141. package/dist/fields/SearchInput.svelte +57 -0
  142. package/dist/fields/SearchInput.svelte.d.ts +33 -0
  143. package/dist/fields/Select.svelte +36 -0
  144. package/dist/fields/Select.svelte.d.ts +29 -0
  145. package/dist/fields/TextInput.svelte +26 -0
  146. package/dist/fields/TextInput.svelte.d.ts +30 -0
  147. package/dist/fields/Textarea.svelte +22 -0
  148. package/dist/fields/Textarea.svelte.d.ts +25 -0
  149. package/dist/fields/Toggle.svelte +72 -0
  150. package/dist/fields/Toggle.svelte.d.ts +32 -0
  151. package/dist/fields/VideoInput.svelte +35 -0
  152. package/dist/fields/VideoInput.svelte.d.ts +40 -0
  153. package/dist/fields/field-types.d.ts +51 -0
  154. package/dist/fields/field-types.js +60 -0
  155. package/dist/fields/index.d.ts +34 -0
  156. package/dist/fields/index.js +34 -0
  157. package/dist/formatting/DateTime.svelte +35 -0
  158. package/dist/formatting/DateTime.svelte.d.ts +26 -0
  159. package/dist/formatting/FormatList.svelte +31 -0
  160. package/dist/formatting/FormatList.svelte.d.ts +46 -0
  161. package/dist/formatting/Highlight.svelte +47 -0
  162. package/dist/formatting/Highlight.svelte.d.ts +15 -0
  163. package/dist/formatting/Number.svelte +35 -0
  164. package/dist/formatting/Number.svelte.d.ts +43 -0
  165. package/dist/formatting/RelativeTime.svelte +69 -0
  166. package/dist/formatting/RelativeTime.svelte.d.ts +26 -0
  167. package/dist/formatting/Year.svelte +39 -0
  168. package/dist/formatting/Year.svelte.d.ts +21 -0
  169. package/dist/formatting/index.d.ts +12 -0
  170. package/dist/formatting/index.js +14 -0
  171. package/dist/forms/EntityForm.svelte +177 -0
  172. package/dist/forms/EntityForm.svelte.d.ts +57 -0
  173. package/dist/forms/FormActions.svelte +29 -0
  174. package/dist/forms/FormActions.svelte.d.ts +37 -0
  175. package/dist/forms/FormErrors.svelte +21 -0
  176. package/dist/forms/FormErrors.svelte.d.ts +16 -0
  177. package/dist/forms/HiddenField.svelte +22 -0
  178. package/dist/forms/HiddenField.svelte.d.ts +11 -0
  179. package/dist/forms/SearchForm.svelte +43 -0
  180. package/dist/forms/SearchForm.svelte.d.ts +36 -0
  181. package/dist/forms/entity-form.d.ts +12 -0
  182. package/dist/forms/entity-form.js +12 -0
  183. package/dist/forms/index.d.ts +14 -0
  184. package/dist/forms/index.js +14 -0
  185. package/dist/hotkeys/HotkeyBadge.svelte +39 -0
  186. package/dist/hotkeys/HotkeyBadge.svelte.d.ts +25 -0
  187. package/dist/hotkeys/HotkeyHelpDialog.svelte +68 -0
  188. package/dist/hotkeys/HotkeyHelpDialog.svelte.d.ts +35 -0
  189. package/dist/hotkeys/HotkeyProvider.svelte +92 -0
  190. package/dist/hotkeys/HotkeyProvider.svelte.d.ts +4 -0
  191. package/dist/hotkeys/context.svelte.d.ts +47 -0
  192. package/dist/hotkeys/context.svelte.js +72 -0
  193. package/dist/hotkeys/index.d.ts +23 -0
  194. package/dist/hotkeys/index.js +22 -0
  195. package/dist/hotkeys/match.d.ts +65 -0
  196. package/dist/hotkeys/match.js +133 -0
  197. package/dist/hotkeys/registry.svelte.d.ts +55 -0
  198. package/dist/hotkeys/registry.svelte.js +110 -0
  199. package/dist/hotkeys/types.d.ts +72 -0
  200. package/dist/hotkeys/types.js +18 -0
  201. package/dist/index.d.ts +36 -0
  202. package/dist/index.js +36 -0
  203. package/dist/infobox/Infobox.svelte +79 -0
  204. package/dist/infobox/Infobox.svelte.d.ts +26 -0
  205. package/dist/infobox/InfoboxHeader.svelte +27 -0
  206. package/dist/infobox/InfoboxHeader.svelte.d.ts +11 -0
  207. package/dist/infobox/InfoboxImage.svelte +38 -0
  208. package/dist/infobox/InfoboxImage.svelte.d.ts +21 -0
  209. package/dist/infobox/InfoboxRow.svelte +50 -0
  210. package/dist/infobox/InfoboxRow.svelte.d.ts +25 -0
  211. package/dist/infobox/InfoboxSection.svelte +24 -0
  212. package/dist/infobox/InfoboxSection.svelte.d.ts +12 -0
  213. package/dist/infobox/index.d.ts +21 -0
  214. package/dist/infobox/index.js +20 -0
  215. package/dist/infobox/types.d.ts +86 -0
  216. package/dist/infobox/types.js +27 -0
  217. package/dist/layout/Breadcrumbs.svelte +52 -0
  218. package/dist/layout/Breadcrumbs.svelte.d.ts +18 -0
  219. package/dist/layout/DetailLayout.svelte +50 -0
  220. package/dist/layout/DetailLayout.svelte.d.ts +19 -0
  221. package/dist/layout/FilterBar.svelte +40 -0
  222. package/dist/layout/FilterBar.svelte.d.ts +12 -0
  223. package/dist/layout/Footer.svelte +32 -0
  224. package/dist/layout/Footer.svelte.d.ts +12 -0
  225. package/dist/layout/Main.svelte +30 -0
  226. package/dist/layout/Main.svelte.d.ts +15 -0
  227. package/dist/layout/Navbar.svelte +68 -0
  228. package/dist/layout/Navbar.svelte.d.ts +31 -0
  229. package/dist/layout/PageHeader.svelte +77 -0
  230. package/dist/layout/PageHeader.svelte.d.ts +33 -0
  231. package/dist/layout/Section.svelte +72 -0
  232. package/dist/layout/Section.svelte.d.ts +40 -0
  233. package/dist/layout/Sidebar.svelte +54 -0
  234. package/dist/layout/Sidebar.svelte.d.ts +27 -0
  235. package/dist/layout/Tabs.svelte +85 -0
  236. package/dist/layout/Tabs.svelte.d.ts +42 -0
  237. package/dist/layout/index.d.ts +29 -0
  238. package/dist/layout/index.js +29 -0
  239. package/dist/layout/types.d.ts +52 -0
  240. package/dist/layout/types.js +27 -0
  241. package/dist/links/ContextLinks.svelte +28 -0
  242. package/dist/links/ContextLinks.svelte.d.ts +20 -0
  243. package/dist/links/EmailLink.svelte +26 -0
  244. package/dist/links/EmailLink.svelte.d.ts +20 -0
  245. package/dist/links/index.d.ts +7 -0
  246. package/dist/links/index.js +7 -0
  247. package/dist/media/Figure.svelte +26 -0
  248. package/dist/media/Figure.svelte.d.ts +12 -0
  249. package/dist/media/Image.svelte +76 -0
  250. package/dist/media/Image.svelte.d.ts +41 -0
  251. package/dist/media/Video.svelte +65 -0
  252. package/dist/media/Video.svelte.d.ts +29 -0
  253. package/dist/media/image-dimensions.d.ts +28 -0
  254. package/dist/media/image-dimensions.js +34 -0
  255. package/dist/media/index.d.ts +10 -0
  256. package/dist/media/index.js +10 -0
  257. package/dist/search/CommandPalette.svelte +257 -0
  258. package/dist/search/CommandPalette.svelte.d.ts +59 -0
  259. package/dist/search/index.d.ts +13 -0
  260. package/dist/search/index.js +13 -0
  261. package/dist/search/types.d.ts +37 -0
  262. package/dist/search/types.js +20 -0
  263. package/dist/seo/Head.svelte +190 -0
  264. package/dist/seo/Head.svelte.d.ts +86 -0
  265. package/dist/seo/JsonLd.svelte +30 -0
  266. package/dist/seo/JsonLd.svelte.d.ts +20 -0
  267. package/dist/seo/index.d.ts +18 -0
  268. package/dist/seo/index.js +17 -0
  269. package/dist/seo/json-ld.d.ts +33 -0
  270. package/dist/seo/json-ld.js +29 -0
  271. package/dist/seo/og-image.d.ts +71 -0
  272. package/dist/seo/og-image.js +81 -0
  273. package/dist/seo/types.d.ts +43 -0
  274. package/dist/seo/types.js +6 -0
  275. package/dist/theme.css +161 -0
  276. package/dist/tips/PopoverTip.svelte +80 -0
  277. package/dist/tips/PopoverTip.svelte.d.ts +33 -0
  278. package/dist/tips/Tip.svelte +91 -0
  279. package/dist/tips/Tip.svelte.d.ts +25 -0
  280. package/dist/tips/index.d.ts +15 -0
  281. package/dist/tips/index.js +14 -0
  282. package/dist/tips/tip-stack.d.ts +25 -0
  283. package/dist/tips/tip-stack.js +37 -0
  284. package/dist/tips/tip-store.svelte.d.ts +74 -0
  285. package/dist/tips/tip-store.svelte.js +158 -0
  286. package/dist/tips/types.d.ts +46 -0
  287. package/dist/tips/types.js +6 -0
  288. package/dist/ui/Alert.svelte +61 -0
  289. package/dist/ui/Alert.svelte.d.ts +37 -0
  290. package/dist/ui/Badge.svelte +39 -0
  291. package/dist/ui/Badge.svelte.d.ts +32 -0
  292. package/dist/ui/Button.svelte +43 -0
  293. package/dist/ui/Button.svelte.d.ts +29 -0
  294. package/dist/ui/Card.svelte +49 -0
  295. package/dist/ui/Card.svelte.d.ts +44 -0
  296. package/dist/ui/Chip.svelte +58 -0
  297. package/dist/ui/Chip.svelte.d.ts +21 -0
  298. package/dist/ui/ConfirmButton.svelte +75 -0
  299. package/dist/ui/ConfirmButton.svelte.d.ts +48 -0
  300. package/dist/ui/Disclosure.svelte +39 -0
  301. package/dist/ui/Disclosure.svelte.d.ts +22 -0
  302. package/dist/ui/EmptyState.svelte +37 -0
  303. package/dist/ui/EmptyState.svelte.d.ts +22 -0
  304. package/dist/ui/ExternalLink.svelte +38 -0
  305. package/dist/ui/ExternalLink.svelte.d.ts +34 -0
  306. package/dist/ui/Heading.svelte +58 -0
  307. package/dist/ui/Heading.svelte.d.ts +22 -0
  308. package/dist/ui/Link.svelte +34 -0
  309. package/dist/ui/Link.svelte.d.ts +20 -0
  310. package/dist/ui/LinkButton.svelte +51 -0
  311. package/dist/ui/LinkButton.svelte.d.ts +33 -0
  312. package/dist/ui/Menu.svelte +54 -0
  313. package/dist/ui/Menu.svelte.d.ts +34 -0
  314. package/dist/ui/MenuItem.svelte +75 -0
  315. package/dist/ui/MenuItem.svelte.d.ts +21 -0
  316. package/dist/ui/MenuSeparator.svelte +26 -0
  317. package/dist/ui/MenuSeparator.svelte.d.ts +8 -0
  318. package/dist/ui/Popover.svelte +63 -0
  319. package/dist/ui/Popover.svelte.d.ts +40 -0
  320. package/dist/ui/ShareButton.svelte +64 -0
  321. package/dist/ui/ShareButton.svelte.d.ts +39 -0
  322. package/dist/ui/Skeleton.svelte +43 -0
  323. package/dist/ui/Skeleton.svelte.d.ts +22 -0
  324. package/dist/ui/ThemeToggle.svelte +65 -0
  325. package/dist/ui/ThemeToggle.svelte.d.ts +24 -0
  326. package/dist/ui/Tooltip.svelte +54 -0
  327. package/dist/ui/Tooltip.svelte.d.ts +35 -0
  328. package/dist/ui/VisuallyHidden.svelte +23 -0
  329. package/dist/ui/VisuallyHidden.svelte.d.ts +10 -0
  330. package/dist/ui/colour-token.d.ts +41 -0
  331. package/dist/ui/colour-token.js +49 -0
  332. package/dist/ui/index.d.ts +44 -0
  333. package/dist/ui/index.js +44 -0
  334. package/dist/ui/menu-context.d.ts +22 -0
  335. package/dist/ui/menu-context.js +26 -0
  336. package/dist/ui/slug.d.ts +19 -0
  337. package/dist/ui/slug.js +25 -0
  338. package/dist/ui/tones.d.ts +68 -0
  339. package/dist/ui/tones.js +150 -0
  340. package/package.json +101 -3
@@ -0,0 +1,59 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type CommandGroup, type CommandItem } from './types.ts';
3
+ interface Props {
4
+ /**
5
+ * Whether the palette is shown. Bindable. Two-way bound to the hotkey
6
+ * registry's `paletteOpen` when a registry is mounted above.
7
+ */
8
+ open?: boolean | undefined;
9
+ /**
10
+ * Names the command menu, and titles the dialog.
11
+ * @defaultValue `'Command palette'`
12
+ */
13
+ label?: string | undefined;
14
+ /**
15
+ * Placeholder in the search field.
16
+ * @defaultValue `'Type a command or search…'`
17
+ */
18
+ placeholder?: string | undefined;
19
+ /**
20
+ * Announced while an `onSearch` request is in flight.
21
+ * @defaultValue `'Searching…'`
22
+ */
23
+ loadingLabel?: string | undefined;
24
+ /**
25
+ * Shown when a settled query matched nothing.
26
+ * @defaultValue `'No results'`
27
+ */
28
+ emptyLabel?: string | undefined;
29
+ /**
30
+ * Accessible name of the dialog's close button.
31
+ * @defaultValue `'Close'`
32
+ */
33
+ closeLabel?: string | undefined;
34
+ /**
35
+ * The sections, in render order. Rows name one by `id`.
36
+ * @defaultValue the exported `defaultCommandGroups`
37
+ */
38
+ groups?: readonly CommandGroup[] | undefined;
39
+ /** Rows that are always available, registry or no registry. */
40
+ items?: readonly CommandItem[] | undefined;
41
+ /**
42
+ * Fetch rows for a query. Debounced by `debounceMs`; `signal` aborts when
43
+ * a newer query supersedes this one, so pass it to `fetch`.
44
+ */
45
+ onSearch?: ((query: string, signal: AbortSignal) => Promise<readonly CommandItem[]>) | undefined;
46
+ /**
47
+ * Quiet period before a search starts, in milliseconds.
48
+ * @defaultValue `150`
49
+ */
50
+ debounceMs?: number | undefined;
51
+ /** Called when a row is activated, before its own `run` / `href`. */
52
+ onSelect?: ((item: CommandItem) => void) | undefined;
53
+ /** Replace a row's contents. */
54
+ item?: Snippet<[CommandItem]> | undefined;
55
+ }
56
+ /** The ⌘K / Ctrl+K command palette: bits-ui’s `Command` inside this library’s `Dialog`. */
57
+ declare const CommandPalette: import("svelte").Component<Props, {}, "open">;
58
+ type CommandPalette = ReturnType<typeof CommandPalette>;
59
+ export default CommandPalette;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * search — the command palette.
3
+ *
4
+ * One component, because a palette is one thing: a dialog, a query box, and
5
+ * rows from wherever the app has them. It composes `feedback/Dialog` and
6
+ * bits-ui's `Command`, lists the hotkey registry's palette actions when a
7
+ * registry is mounted, and works from `items` / `onSearch` alone when none
8
+ * is.
9
+ *
10
+ * @module
11
+ */
12
+ export { default as CommandPalette } from './CommandPalette.svelte';
13
+ export { defaultCommandGroups, type CommandGroup, type CommandItem } from './types.ts';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * search — the command palette.
3
+ *
4
+ * One component, because a palette is one thing: a dialog, a query box, and
5
+ * rows from wherever the app has them. It composes `feedback/Dialog` and
6
+ * bits-ui's `Command`, lists the hotkey registry's palette actions when a
7
+ * registry is mounted, and works from `items` / `onSearch` alone when none
8
+ * is.
9
+ *
10
+ * @module
11
+ */
12
+ export { default as CommandPalette } from './CommandPalette.svelte';
13
+ export { defaultCommandGroups } from "./types.js";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The row and section shapes the command palette renders.
3
+ *
4
+ * A {@link CommandItem} is deliberately not a `CommandAction`: registry
5
+ * actions always run something, whereas a palette row can just as well be a
6
+ * search result that navigates, or a row whose whole behaviour is the
7
+ * caller's `onSelect`. The palette projects registry actions into this shape
8
+ * internally, so one row renderer serves both sources.
9
+ *
10
+ * @module
11
+ */
12
+ export interface CommandItem {
13
+ /** Stable id. Unique within its group; it is what bits-ui ranks and selects on. */
14
+ id: string;
15
+ /** The row's text — what the reader searches for and what is announced. */
16
+ label: string;
17
+ /** Which {@link CommandGroup} the row belongs in, by `id`. */
18
+ group: string;
19
+ /** Muted trailing text: a path, a date, a parent name. */
20
+ hint?: string | undefined;
21
+ /** A short chip on the right — a type, a status. */
22
+ badge?: string | undefined;
23
+ /** What activating the row does. Runs after the palette closes. */
24
+ run?: (() => void | Promise<void>) | undefined;
25
+ /** Where activating the row goes. Resolved through the components context. */
26
+ href?: string | undefined;
27
+ }
28
+ /** A palette section: an id rows point at, and the heading it renders. */
29
+ export interface CommandGroup {
30
+ id: string;
31
+ label: string;
32
+ }
33
+ /**
34
+ * The sections a palette shows when given none: the two group ids the hotkey
35
+ * registry uses (`actions`, `navigation`), then whatever `onSearch` returns.
36
+ */
37
+ export declare const defaultCommandGroups: readonly CommandGroup[];
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The row and section shapes the command palette renders.
3
+ *
4
+ * A {@link CommandItem} is deliberately not a `CommandAction`: registry
5
+ * actions always run something, whereas a palette row can just as well be a
6
+ * search result that navigates, or a row whose whole behaviour is the
7
+ * caller's `onSelect`. The palette projects registry actions into this shape
8
+ * internally, so one row renderer serves both sources.
9
+ *
10
+ * @module
11
+ */
12
+ /**
13
+ * The sections a palette shows when given none: the two group ids the hotkey
14
+ * registry uses (`actions`, `navigation`), then whatever `onSearch` returns.
15
+ */
16
+ export const defaultCommandGroups = [
17
+ { id: 'actions', label: 'Actions' },
18
+ { id: 'navigation', label: 'Go to' },
19
+ { id: 'results', label: 'Results' },
20
+ ];
@@ -0,0 +1,190 @@
1
+ <!-- @component
2
+ Per-page `<head>` metadata: title, description, canonical URL, `hreflang` alternates, Open Graph and Twitter / X tags, `article:*` and pagination links.
3
+ -->
4
+ <script lang="ts">/**
5
+ * Per-page `<head>` metadata: the title (with an optional site-name
6
+ * suffix), description, keywords, robots directive, canonical URL,
7
+ * `hreflang` alternates, an optional feed link, Open Graph and Twitter / X
8
+ * card tags, the `article:*` family, and `prev` / `next` pagination links.
9
+ *
10
+ * **Canonical on the production origin.** The explicit `canonical` wins;
11
+ * otherwise the page self-references `pathname` resolved through
12
+ * `canonicalHref` (the query is never part of it), so canonical and
13
+ * `og:url` are never empty and a preview host never becomes canonical.
14
+ * `canonicalHref` defaults to the components context's `href` — an app
15
+ * whose `href` returns relative paths passes its own absolute resolver
16
+ * here. `pathname` defaults to `window.location.pathname`, which does not
17
+ * exist during SSR: a SvelteKit app passes `pathname={page.url.pathname}`.
18
+ *
19
+ * **Share image, in precedence order:** an explicit `image`, then a
20
+ * per-page card built from `shareCard` by {@link ogImageUrl}, then
21
+ * `defaultImage` — so every share renders a card when a default is set.
22
+ *
23
+ * **`og:type`** is derived from the Schema.org `type` when `openGraphType`
24
+ * is not given, and the `article:*` tags are emitted only for `article`.
25
+ * **`og:locale`** comes from `ogLocale`, else the context locale with `-`
26
+ * replaced by `_` (`en-AU` → `en_AU`).
27
+ *
28
+ * Every string is a prop: `siteName` has no default (a library cannot know
29
+ * the site), `feed.title` defaults to `'Latest changes'`, and the share
30
+ * card's alt text defaults to `"<title> — <siteName>"` (the title alone without a `siteName`) via `shareCardAlt`.
31
+ *
32
+ * Renders only into `<svelte:head>`; there is no markup in the body.
33
+ */
34
+ import { getComponentsContext } from "../context.svelte.js";
35
+ import { OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, ogImageUrl } from "./og-image.js";
36
+ let { title, siteName = undefined, titleSeparator = " | ", description = undefined, keywords = undefined, type = undefined, openGraphType = undefined, canonical = undefined, pathname = undefined, canonicalHref = undefined, alternates = undefined, locales = undefined, ogLocale = undefined, feed = undefined, robots = undefined, image = undefined, shareCard = undefined, shareCardPath = undefined, shareCardAlt = undefined, defaultImage = undefined, published = undefined, modified = undefined, section = undefined, tags = undefined, twitterCard = "summary_large_image", twitterSite = undefined, twitterCreator = undefined, prevUrl = undefined, nextUrl = undefined } = $props();
37
+ const context = getComponentsContext();
38
+ /** Schema.org type → Open Graph type, so `og:type` is never empty. */
39
+ const schemaToOpenGraph = {
40
+ WebPage: "website",
41
+ WebSite: "website",
42
+ CollectionPage: "website",
43
+ Article: "article",
44
+ BlogPosting: "article",
45
+ ProfilePage: "profile"
46
+ };
47
+ const resolveHref = $derived(canonicalHref ?? context.href);
48
+ const resolvedTitle = $derived.by(() => {
49
+ const trimmed = title.trim();
50
+ if (trimmed === "") return siteName ?? "";
51
+ return siteName === undefined || siteName === "" ? trimmed : `${trimmed}${titleSeparator}${siteName}`;
52
+ });
53
+ const resolvedDescription = $derived(description === null || description === undefined || description.trim() === "" ? undefined : description);
54
+ const resolvedCanonical = $derived.by(() => {
55
+ if (canonical !== undefined) return canonical;
56
+ const path = pathname ?? (typeof window === "undefined" ? undefined : window.location.pathname);
57
+ return path === undefined ? undefined : resolveHref(path);
58
+ });
59
+ const resolvedAlternates = $derived.by(() => {
60
+ if (alternates !== undefined) return alternates;
61
+ if (resolvedCanonical === undefined || locales === undefined || locales.length === 0) return [];
62
+ const href = resolvedCanonical;
63
+ return [...locales, "x-default"].map((lang) => ({
64
+ href,
65
+ lang
66
+ }));
67
+ });
68
+ const resolvedOgType = $derived(openGraphType ?? (type === undefined ? "website" : schemaToOpenGraph[type]));
69
+ const resolvedOgLocale = $derived.by(() => {
70
+ if (ogLocale !== undefined) return ogLocale;
71
+ const locale = context.locale();
72
+ const first = typeof locale === "string" ? locale : locale?.[0];
73
+ return first === undefined ? undefined : first.replace(/-/g, "_");
74
+ });
75
+ const cardImage = $derived.by(() => {
76
+ if (image !== undefined || shareCard === undefined) return undefined;
77
+ const url = ogImageUrl(shareCard, {
78
+ href: resolveHref,
79
+ basePath: shareCardPath
80
+ });
81
+ if (url === undefined) return undefined;
82
+ const alt = shareCardAlt?.(shareCard) ?? (siteName === undefined || siteName === "" ? shareCard.title.trim() : `${shareCard.title.trim()} — ${siteName}`);
83
+ return {
84
+ url,
85
+ alt,
86
+ width: OG_IMAGE_WIDTH,
87
+ height: OG_IMAGE_HEIGHT,
88
+ type: "image/png"
89
+ };
90
+ });
91
+ const resolvedImage = $derived(image ?? cardImage ?? defaultImage);
92
+ const robotsContent = $derived(`${robots?.index ?? true ? "index" : "noindex"}, ${robots?.follow ?? true ? "follow" : "nofollow"}`);
93
+ const resolvedSection = $derived(section?.trim() === "" ? undefined : section);
94
+ </script>
95
+
96
+ {#snippet articleMeta()}
97
+ {#if published !== undefined}
98
+ <meta property="article:published_time" content={published.toISOString()} />
99
+ {/if}
100
+ {#if modified !== undefined}
101
+ <meta property="article:modified_time" content={modified.toISOString()} />
102
+ {/if}
103
+ {#if resolvedSection !== undefined}
104
+ <meta property="article:section" content={resolvedSection} />
105
+ {/if}
106
+ {#each tags ?? [] as tag}
107
+ <meta property="article:tag" content={tag} />
108
+ {/each}
109
+ {/snippet}
110
+
111
+ <svelte:head>
112
+ <title>{resolvedTitle}</title>
113
+ {#if resolvedDescription !== undefined}
114
+ <meta name="description" content={resolvedDescription} />
115
+ {/if}
116
+ {#if keywords !== undefined && keywords.length > 0}
117
+ <meta name="keywords" content={keywords.join(', ')} />
118
+ {/if}
119
+ {#if resolvedCanonical !== undefined}
120
+ <link rel="canonical" href={resolvedCanonical} />
121
+ {/if}
122
+ {#if feed !== undefined}
123
+ <link
124
+ rel="alternate"
125
+ type={feed.type ?? 'application/feed+json'}
126
+ href={resolveHref(feed.path)}
127
+ title={feed.title ?? 'Latest changes'}
128
+ />
129
+ {/if}
130
+ <meta name="robots" content={robotsContent} />
131
+
132
+ <meta property="og:title" content={resolvedTitle} />
133
+ {#if resolvedCanonical !== undefined}
134
+ <meta property="og:url" content={resolvedCanonical} />
135
+ {/if}
136
+ {#if siteName !== undefined && siteName !== ''}
137
+ <meta property="og:site_name" content={siteName} />
138
+ {/if}
139
+ <meta property="og:type" content={resolvedOgType} />
140
+ {#if resolvedOgLocale !== undefined}
141
+ <meta property="og:locale" content={resolvedOgLocale} />
142
+ {/if}
143
+ {#if resolvedDescription !== undefined}
144
+ <meta property="og:description" content={resolvedDescription} />
145
+ {/if}
146
+
147
+ <meta name="twitter:card" content={twitterCard} />
148
+ {#if twitterSite !== undefined}
149
+ <meta name="twitter:site" content={twitterSite} />
150
+ {/if}
151
+ {#if twitterCreator !== undefined}
152
+ <meta name="twitter:creator" content={twitterCreator} />
153
+ {/if}
154
+ <meta name="twitter:title" content={resolvedTitle} />
155
+ {#if resolvedDescription !== undefined}
156
+ <meta name="twitter:description" content={resolvedDescription} />
157
+ {/if}
158
+
159
+ {#if resolvedImage !== undefined}
160
+ <meta property="og:image" content={resolvedImage.url} />
161
+ <meta name="twitter:image" content={resolvedImage.url} />
162
+ {#if resolvedImage.alt !== undefined && resolvedImage.alt !== ''}
163
+ <meta property="og:image:alt" content={resolvedImage.alt} />
164
+ <meta name="twitter:image:alt" content={resolvedImage.alt} />
165
+ {/if}
166
+ {#if resolvedImage.width !== undefined}
167
+ <meta property="og:image:width" content={String(resolvedImage.width)} />
168
+ {/if}
169
+ {#if resolvedImage.height !== undefined}
170
+ <meta property="og:image:height" content={String(resolvedImage.height)} />
171
+ {/if}
172
+ {#if resolvedImage.type !== undefined}
173
+ <meta property="og:image:type" content={resolvedImage.type} />
174
+ {/if}
175
+ {/if}
176
+
177
+ {#if resolvedOgType === 'article'}
178
+ {@render articleMeta()}
179
+ {/if}
180
+
181
+ {#each resolvedAlternates as alternate}
182
+ <link rel="alternate" href={alternate.href} hreflang={alternate.lang} />
183
+ {/each}
184
+ {#if prevUrl !== undefined}
185
+ <link rel="prev" href={prevUrl} />
186
+ {/if}
187
+ {#if nextUrl !== undefined}
188
+ <link rel="next" href={nextUrl} />
189
+ {/if}
190
+ </svelte:head>
@@ -0,0 +1,86 @@
1
+ import { type OgImageCard } from './og-image.ts';
2
+ import type { AlternateLink, FeedLink, HeadImage, OpenGraphType, Robots, SchemaPageType, TwitterCard } from './types.ts';
3
+ interface Props {
4
+ /** The page title. Blank renders `siteName` alone. */
5
+ title: string;
6
+ /** Appended to every title as `"<title><titleSeparator><siteName>"`, and emitted as `og:site_name`. */
7
+ siteName?: string | undefined;
8
+ /**
9
+ * Between the title and `siteName`.
10
+ * @defaultValue `' | '`
11
+ */
12
+ titleSeparator?: string | undefined;
13
+ /** Meta and Open Graph description. */
14
+ description?: string | null | undefined;
15
+ /** Meta keywords. */
16
+ keywords?: readonly string[] | undefined;
17
+ /** The Schema.org page type — only used to derive `og:type`. */
18
+ type?: SchemaPageType | undefined;
19
+ /** An explicit `og:type`; wins over the one derived from `type`. */
20
+ openGraphType?: OpenGraphType | undefined;
21
+ /** An explicit canonical URL. Wins over `pathname`. */
22
+ canonical?: string | undefined;
23
+ /** The current path, resolved through `canonicalHref` into the canonical URL. */
24
+ pathname?: string | undefined;
25
+ /**
26
+ * Resolves an app-relative path to an absolute URL on the production origin.
27
+ * @defaultValue the components context’s `href`
28
+ */
29
+ canonicalHref?: ((path: string) => string) | undefined;
30
+ /** Explicit `hreflang` alternates. Wins over `locales`. */
31
+ alternates?: readonly AlternateLink[] | undefined;
32
+ /**
33
+ * The site's locales. Each, plus `x-default`, becomes an `hreflang`
34
+ * alternate pointing at the canonical URL — the right signal for a site
35
+ * whose UI chrome is localised but whose URLs are not. A site with
36
+ * per-locale URLs passes `alternates` instead.
37
+ */
38
+ locales?: readonly string[] | undefined;
39
+ /** `og:locale`, in Open Graph's `language_TERRITORY` form. */
40
+ ogLocale?: string | undefined;
41
+ /** An RSS, Atom or JSON Feed link. */
42
+ feed?: FeedLink | undefined;
43
+ /** The robots directive. */
44
+ robots?: Robots | undefined;
45
+ /** An explicit share image. Wins over `shareCard` and `defaultImage`. */
46
+ image?: HeadImage | undefined;
47
+ /** Build a per-page share card URL with {@link ogImageUrl}. */
48
+ shareCard?: OgImageCard | undefined;
49
+ /**
50
+ * Where the share-card renderer is mounted.
51
+ * @defaultValue `/og`
52
+ */
53
+ shareCardPath?: string | undefined;
54
+ /**
55
+ * The share card's alt text.
56
+ * @defaultValue `"<card title> — <siteName>"`, or the card title alone when `siteName` is unset or empty
57
+ */
58
+ shareCardAlt?: ((card: OgImageCard) => string) | undefined;
59
+ /** The share image when neither `image` nor `shareCard` gives one. */
60
+ defaultImage?: HeadImage | undefined;
61
+ /** `article:published_time`. */
62
+ published?: Date | undefined;
63
+ /** `article:modified_time`. */
64
+ modified?: Date | undefined;
65
+ /** `article:section`. Blank is treated as absent. */
66
+ section?: string | undefined;
67
+ /** `article:tag`, one per entry. */
68
+ tags?: readonly string[] | undefined;
69
+ /**
70
+ * The Twitter / X card type.
71
+ * @defaultValue `'summary_large_image'`
72
+ */
73
+ twitterCard?: TwitterCard | undefined;
74
+ /** The site’s Twitter / X handle. */
75
+ twitterSite?: string | undefined;
76
+ /** The author’s Twitter / X handle. */
77
+ twitterCreator?: string | undefined;
78
+ /** The previous page in a paginated series (`rel="prev"`). */
79
+ prevUrl?: string | undefined;
80
+ /** The next page in a paginated series (`rel="next"`). */
81
+ nextUrl?: string | undefined;
82
+ }
83
+ /** Per-page `<head>` metadata: title, description, canonical URL, `hreflang` alternates, Open Graph and Twitter / X tags, `article:*` and pagination links. */
84
+ declare const Head: import("svelte").Component<Props, {}, "">;
85
+ type Head = ReturnType<typeof Head>;
86
+ export default Head;
@@ -0,0 +1,30 @@
1
+ <!-- @component
2
+ One Schema.org JSON-LD node as an XSS-safe `<script type="application/ld+json">` block in `<svelte:head>`.
3
+ -->
4
+ <script lang="ts">/**
5
+ * Emits one Schema.org JSON-LD graph node as a
6
+ * `<script type="application/ld+json">` block in `<svelte:head>`.
7
+ *
8
+ * The JSON goes through {@link serializeJsonLd}, which escapes `<`, `>`,
9
+ * `&`, U+2028 and U+2029, so authored content can neither close the script
10
+ * element nor open an HTML comment inside it and inject markup. `data` of
11
+ * `null` / `undefined` renders nothing, so a builder that
12
+ * may have nothing to emit (a degenerate breadcrumb trail) can be wired
13
+ * without a call-site `{#if}`.
14
+ */
15
+ import { serializeJsonLd } from "./json-ld.js";
16
+ let { data } = $props();
17
+ // The closing tag is split so neither the Svelte compiler nor an HTML
18
+ // parser reading this file sees it as the end of the component's own
19
+ // `<script>`.
20
+ const open = "<script type=\"application/ld+json\">";
21
+ const close = "<" + "/script>";
22
+ const block = $derived(data === null || data === undefined ? undefined : open + serializeJsonLd(data) + close);
23
+ </script>
24
+
25
+ <svelte:head>
26
+ {#if block !== undefined}
27
+ <!-- Escaped by serializeJsonLd: `<`, `>` and `&` cannot appear unescaped. -->
28
+ {@html block}
29
+ {/if}
30
+ </svelte:head>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Emits one Schema.org JSON-LD graph node as a
3
+ * `<script type="application/ld+json">` block in `<svelte:head>`.
4
+ *
5
+ * The JSON goes through {@link serializeJsonLd}, which escapes `<`, `>`,
6
+ * `&`, U+2028 and U+2029, so authored content can neither close the script
7
+ * element nor open an HTML comment inside it and inject markup. `data` of
8
+ * `null` / `undefined` renders nothing, so a builder that
9
+ * may have nothing to emit (a degenerate breadcrumb trail) can be wired
10
+ * without a call-site `{#if}`.
11
+ */
12
+ import { type JsonLdObject } from './json-ld.ts';
13
+ interface Props {
14
+ /** The node to serialise; nothing is emitted for `null` or `undefined`. */
15
+ data: JsonLdObject | null | undefined;
16
+ }
17
+ /** One Schema.org JSON-LD node as an XSS-safe `<script type="application/ld+json">` block in `<svelte:head>`. */
18
+ declare const JsonLd: import("svelte").Component<Props, {}, "">;
19
+ type JsonLd = ReturnType<typeof JsonLd>;
20
+ export default JsonLd;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@thomasfosterau/components/seo` — per-page `<head>` metadata and
3
+ * structured data. A separate entry point: it renders only into
4
+ * `<svelte:head>`, and a consumer that manages its own head never loads it.
5
+ *
6
+ * `<Head>` emits the title, canonical, `hreflang`, Open Graph and Twitter /
7
+ * X tags; `<JsonLd>` emits one XSS-safe JSON-LD block; `serializeJsonLd` is
8
+ * the escaping serialiser underneath it; `ogImageUrl` builds the
9
+ * content-addressed share-card URL `<Head>` emits, with the param names and
10
+ * caps a card renderer reads back.
11
+ *
12
+ * @module
13
+ */
14
+ export { default as Head } from './Head.svelte';
15
+ export { default as JsonLd } from './JsonLd.svelte';
16
+ export { serializeJsonLd, type JsonLdObject } from './json-ld.ts';
17
+ export { OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, OG_SUBTITLE_MAX_LENGTH, OG_SUBTITLE_PARAM, OG_TITLE_MAX_LENGTH, OG_TITLE_PARAM, OG_VERSION_PARAM, ogImageUrl, type OgImageCard, type OgImageUrlOptions, } from './og-image.ts';
18
+ export type { AlternateLink, FeedLink, HeadImage, OpenGraphType, Robots, SchemaPageType, TwitterCard, } from './types.ts';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@thomasfosterau/components/seo` — per-page `<head>` metadata and
3
+ * structured data. A separate entry point: it renders only into
4
+ * `<svelte:head>`, and a consumer that manages its own head never loads it.
5
+ *
6
+ * `<Head>` emits the title, canonical, `hreflang`, Open Graph and Twitter /
7
+ * X tags; `<JsonLd>` emits one XSS-safe JSON-LD block; `serializeJsonLd` is
8
+ * the escaping serialiser underneath it; `ogImageUrl` builds the
9
+ * content-addressed share-card URL `<Head>` emits, with the param names and
10
+ * caps a card renderer reads back.
11
+ *
12
+ * @module
13
+ */
14
+ export { default as Head } from './Head.svelte';
15
+ export { default as JsonLd } from './JsonLd.svelte';
16
+ export { serializeJsonLd } from "./json-ld.js";
17
+ export { OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, OG_SUBTITLE_MAX_LENGTH, OG_SUBTITLE_PARAM, OG_TITLE_MAX_LENGTH, OG_TITLE_PARAM, OG_VERSION_PARAM, ogImageUrl, } from "./og-image.js";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Serialising Schema.org JSON-LD for a `<script type="application/ld+json">`
3
+ * block.
4
+ *
5
+ * `JSON.stringify` already escapes `"` inside strings. What it leaves is the
6
+ * HTML parser: an authored title containing `</script>` would close the
7
+ * embedding script element and inject whatever follows. `<`, `>` and `&` are
8
+ * therefore replaced with their `\u` escapes — JSON parsers read them
9
+ * identically, so the structured-data consumer sees the original text — and so
10
+ * are U+2028 / U+2029, which are legal in JSON strings but line terminators in
11
+ * JavaScript (OWASP / Google's JSON-in-HTML guidance).
12
+ *
13
+ * @module
14
+ */
15
+ /**
16
+ * A JSON-LD graph node with its top-level `@context`.
17
+ *
18
+ * Deliberately loose: the library does not take `schema-dts` (or any
19
+ * vocabulary) as a dependency. `WithContext<Thing>` from `schema-dts` (a type
20
+ * alias, so it satisfies the index signature) is assignable to it, so an app
21
+ * that type-checks its builders against the real vocabulary can hand the
22
+ * result straight to {@link serializeJsonLd} or `<JsonLd>`.
23
+ */
24
+ export type JsonLdObject = {
25
+ readonly '@context': unknown;
26
+ readonly [property: string]: unknown;
27
+ };
28
+ /**
29
+ * Serialise a JSON-LD object to a string safe to embed in a
30
+ * `<script type="application/ld+json">` block. The output is valid JSON that
31
+ * round-trips through `JSON.parse` to the input.
32
+ */
33
+ export declare function serializeJsonLd(data: JsonLdObject): string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Serialising Schema.org JSON-LD for a `<script type="application/ld+json">`
3
+ * block.
4
+ *
5
+ * `JSON.stringify` already escapes `"` inside strings. What it leaves is the
6
+ * HTML parser: an authored title containing `</script>` would close the
7
+ * embedding script element and inject whatever follows. `<`, `>` and `&` are
8
+ * therefore replaced with their `\u` escapes — JSON parsers read them
9
+ * identically, so the structured-data consumer sees the original text — and so
10
+ * are U+2028 / U+2029, which are legal in JSON strings but line terminators in
11
+ * JavaScript (OWASP / Google's JSON-in-HTML guidance).
12
+ *
13
+ * @module
14
+ */
15
+ const LINE_SEPARATOR = new RegExp(String.fromCharCode(0x2028), 'g');
16
+ const PARAGRAPH_SEPARATOR = new RegExp(String.fromCharCode(0x2029), 'g');
17
+ /**
18
+ * Serialise a JSON-LD object to a string safe to embed in a
19
+ * `<script type="application/ld+json">` block. The output is valid JSON that
20
+ * round-trips through `JSON.parse` to the input.
21
+ */
22
+ export function serializeJsonLd(data) {
23
+ return JSON.stringify(data)
24
+ .replace(/</g, '\\u003C')
25
+ .replace(/>/g, '\\u003E')
26
+ .replace(/&/g, '\\u0026')
27
+ .replace(LINE_SEPARATOR, '\\u2028')
28
+ .replace(PARAGRAPH_SEPARATOR, '\\u2029');
29
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The content-addressed Open Graph share-image URL builder.
3
+ *
4
+ * Shared by `<Head>` (which emits the URL as `og:image`) and whatever renders
5
+ * the card (an app route or Worker reading the same query params), so the
6
+ * producer and the consumer of the URL can never disagree on the param names
7
+ * or the length caps — import the constants below on both sides.
8
+ *
9
+ * The URL is **content-addressed**: the title and description ride in the
10
+ * query, and a `v` token derived from the content's last-modified instant
11
+ * changes the whole URL whenever the content changes. A new URL is how the
12
+ * image cache-busts — link-preview scrapers (Facebook, LinkedIn, federated
13
+ * servers) cache aggressively and only re-fetch a URL they have not seen, so
14
+ * the version token is load-bearing, not cosmetic.
15
+ *
16
+ * @module
17
+ */
18
+ /** Query param carrying the card title. */
19
+ export declare const OG_TITLE_PARAM = "t";
20
+ /** Query param carrying the card's second line (the page description). */
21
+ export declare const OG_SUBTITLE_PARAM = "d";
22
+ /** Query param carrying the cache-busting version token. */
23
+ export declare const OG_VERSION_PARAM = "v";
24
+ /**
25
+ * Length caps on the title / description carried in the URL. A renderer
26
+ * should re-apply them defensively; here they keep the emitted URL short.
27
+ */
28
+ export declare const OG_TITLE_MAX_LENGTH = 120;
29
+ /** Longest description, in characters, carried in a share-card URL; see `OG_TITLE_MAX_LENGTH`. */
30
+ export declare const OG_SUBTITLE_MAX_LENGTH = 200;
31
+ /** The rendered card's size — the 1.91:1 Open Graph standard. */
32
+ export declare const OG_IMAGE_WIDTH = 1200;
33
+ /** Share-card height in pixels; see `OG_IMAGE_WIDTH`. */
34
+ export declare const OG_IMAGE_HEIGHT = 630;
35
+ /** What a per-page share card is built from. */
36
+ export interface OgImageCard {
37
+ /** The page title — the card headline. A blank title builds no URL. */
38
+ readonly title: string;
39
+ /** Optional second line (the page description). */
40
+ readonly description?: string | null | undefined;
41
+ /**
42
+ * A stable, opaque identity for the page (a content UUID, a slug), used only
43
+ * to build the readable `<slug>.png` path segment. Never rendered.
44
+ */
45
+ readonly id: string;
46
+ /**
47
+ * The content's last-modified instant, or anything that changes when the
48
+ * card should. Drives the version token; absent or unparseable, the URL is
49
+ * keyed by title + description alone.
50
+ */
51
+ readonly updatedAt?: Date | string | number | null | undefined;
52
+ }
53
+ /** How `ogImageUrl` turns a card into an absolute URL: the href resolver and the renderer's mount path. */
54
+ export interface OgImageUrlOptions {
55
+ /**
56
+ * Resolves the card's path to the href emitted — the production origin, so
57
+ * the URL is absolute (`og:image` must be). `<Head>` passes its
58
+ * `canonicalHref`, which defaults to the components context's `href`.
59
+ * Defaults to the identity.
60
+ */
61
+ readonly href?: ((path: string) => string) | undefined;
62
+ /** The path the card renderer is mounted at. Default `/og`. */
63
+ readonly basePath?: string | undefined;
64
+ }
65
+ /**
66
+ * Build the `og:image` URL for a page's share card: `<basePath>/<slug>.png`
67
+ * resolved through `href`, with the title, description and version token in
68
+ * the query. Returns `undefined` for a blank title, so the caller falls back
69
+ * to its default share image.
70
+ */
71
+ export declare function ogImageUrl(card: OgImageCard, options?: OgImageUrlOptions): string | undefined;