@fuzdev/fuz_ui 0.169.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/Alert.svelte +108 -0
  4. package/dist/Alert.svelte.d.ts +16 -0
  5. package/dist/Alert.svelte.d.ts.map +1 -0
  6. package/dist/ApiDeclarationList.svelte +35 -0
  7. package/dist/ApiDeclarationList.svelte.d.ts +9 -0
  8. package/dist/ApiDeclarationList.svelte.d.ts.map +1 -0
  9. package/dist/ApiIndex.svelte +65 -0
  10. package/dist/ApiIndex.svelte.d.ts +23 -0
  11. package/dist/ApiIndex.svelte.d.ts.map +1 -0
  12. package/dist/ApiModule.svelte +124 -0
  13. package/dist/ApiModule.svelte.d.ts +22 -0
  14. package/dist/ApiModule.svelte.d.ts.map +1 -0
  15. package/dist/Breadcrumb.svelte +83 -0
  16. package/dist/Breadcrumb.svelte.d.ts +23 -0
  17. package/dist/Breadcrumb.svelte.d.ts.map +1 -0
  18. package/dist/Card.svelte +157 -0
  19. package/dist/Card.svelte.d.ts +13 -0
  20. package/dist/Card.svelte.d.ts.map +1 -0
  21. package/dist/ColorSchemeInput.svelte +65 -0
  22. package/dist/ColorSchemeInput.svelte.d.ts +11 -0
  23. package/dist/ColorSchemeInput.svelte.d.ts.map +1 -0
  24. package/dist/Contextmenu.svelte +30 -0
  25. package/dist/Contextmenu.svelte.d.ts +32 -0
  26. package/dist/Contextmenu.svelte.d.ts.map +1 -0
  27. package/dist/ContextmenuEntry.svelte +74 -0
  28. package/dist/ContextmenuEntry.svelte.d.ts +12 -0
  29. package/dist/ContextmenuEntry.svelte.d.ts.map +1 -0
  30. package/dist/ContextmenuLinkEntry.svelte +112 -0
  31. package/dist/ContextmenuLinkEntry.svelte.d.ts +12 -0
  32. package/dist/ContextmenuLinkEntry.svelte.d.ts.map +1 -0
  33. package/dist/ContextmenuRoot.svelte +372 -0
  34. package/dist/ContextmenuRoot.svelte.d.ts +71 -0
  35. package/dist/ContextmenuRoot.svelte.d.ts.map +1 -0
  36. package/dist/ContextmenuRootForSafariCompatibility.svelte +541 -0
  37. package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts +79 -0
  38. package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts.map +1 -0
  39. package/dist/ContextmenuSeparator.svelte +16 -0
  40. package/dist/ContextmenuSeparator.svelte.d.ts +4 -0
  41. package/dist/ContextmenuSeparator.svelte.d.ts.map +1 -0
  42. package/dist/ContextmenuSubmenu.svelte +116 -0
  43. package/dist/ContextmenuSubmenu.svelte.d.ts +10 -0
  44. package/dist/ContextmenuSubmenu.svelte.d.ts.map +1 -0
  45. package/dist/ContextmenuTextEntry.svelte +21 -0
  46. package/dist/ContextmenuTextEntry.svelte.d.ts +10 -0
  47. package/dist/ContextmenuTextEntry.svelte.d.ts.map +1 -0
  48. package/dist/CopyToClipboard.svelte +81 -0
  49. package/dist/CopyToClipboard.svelte.d.ts +18 -0
  50. package/dist/CopyToClipboard.svelte.d.ts.map +1 -0
  51. package/dist/DeclarationDetail.svelte +340 -0
  52. package/dist/DeclarationDetail.svelte.d.ts +8 -0
  53. package/dist/DeclarationDetail.svelte.d.ts.map +1 -0
  54. package/dist/DeclarationLink.svelte +50 -0
  55. package/dist/DeclarationLink.svelte.d.ts +8 -0
  56. package/dist/DeclarationLink.svelte.d.ts.map +1 -0
  57. package/dist/Details.svelte +51 -0
  58. package/dist/Details.svelte.d.ts +20 -0
  59. package/dist/Details.svelte.d.ts.map +1 -0
  60. package/dist/Dialog.svelte +217 -0
  61. package/dist/Dialog.svelte.d.ts +30 -0
  62. package/dist/Dialog.svelte.d.ts.map +1 -0
  63. package/dist/Dialogs.svelte +28 -0
  64. package/dist/Dialogs.svelte.d.ts +11 -0
  65. package/dist/Dialogs.svelte.d.ts.map +1 -0
  66. package/dist/Docs.svelte +179 -0
  67. package/dist/Docs.svelte.d.ts +13 -0
  68. package/dist/Docs.svelte.d.ts.map +1 -0
  69. package/dist/DocsContent.svelte +40 -0
  70. package/dist/DocsContent.svelte.d.ts +14 -0
  71. package/dist/DocsContent.svelte.d.ts.map +1 -0
  72. package/dist/DocsFooter.svelte +64 -0
  73. package/dist/DocsFooter.svelte.d.ts +15 -0
  74. package/dist/DocsFooter.svelte.d.ts.map +1 -0
  75. package/dist/DocsLink.svelte +41 -0
  76. package/dist/DocsLink.svelte.d.ts +12 -0
  77. package/dist/DocsLink.svelte.d.ts.map +1 -0
  78. package/dist/DocsList.svelte +44 -0
  79. package/dist/DocsList.svelte.d.ts +11 -0
  80. package/dist/DocsList.svelte.d.ts.map +1 -0
  81. package/dist/DocsMenu.svelte +55 -0
  82. package/dist/DocsMenu.svelte.d.ts +11 -0
  83. package/dist/DocsMenu.svelte.d.ts.map +1 -0
  84. package/dist/DocsMenuHeader.svelte +15 -0
  85. package/dist/DocsMenuHeader.svelte.d.ts +9 -0
  86. package/dist/DocsMenuHeader.svelte.d.ts.map +1 -0
  87. package/dist/DocsModulesList.svelte +32 -0
  88. package/dist/DocsModulesList.svelte.d.ts +7 -0
  89. package/dist/DocsModulesList.svelte.d.ts.map +1 -0
  90. package/dist/DocsPageLinks.svelte +61 -0
  91. package/dist/DocsPageLinks.svelte.d.ts +8 -0
  92. package/dist/DocsPageLinks.svelte.d.ts.map +1 -0
  93. package/dist/DocsPrimaryNav.svelte +93 -0
  94. package/dist/DocsPrimaryNav.svelte.d.ts +11 -0
  95. package/dist/DocsPrimaryNav.svelte.d.ts.map +1 -0
  96. package/dist/DocsSearch.svelte +48 -0
  97. package/dist/DocsSearch.svelte.d.ts +11 -0
  98. package/dist/DocsSearch.svelte.d.ts.map +1 -0
  99. package/dist/DocsSecondaryNav.svelte +63 -0
  100. package/dist/DocsSecondaryNav.svelte.d.ts +9 -0
  101. package/dist/DocsSecondaryNav.svelte.d.ts.map +1 -0
  102. package/dist/DocsTertiaryNav.svelte +118 -0
  103. package/dist/DocsTertiaryNav.svelte.d.ts +10 -0
  104. package/dist/DocsTertiaryNav.svelte.d.ts.map +1 -0
  105. package/dist/EcosystemLinks.svelte +53 -0
  106. package/dist/EcosystemLinks.svelte.d.ts +7 -0
  107. package/dist/EcosystemLinks.svelte.d.ts.map +1 -0
  108. package/dist/EcosystemLinksPanel.svelte +22 -0
  109. package/dist/EcosystemLinksPanel.svelte.d.ts +8 -0
  110. package/dist/EcosystemLinksPanel.svelte.d.ts.map +1 -0
  111. package/dist/GithubLink.svelte +75 -0
  112. package/dist/GithubLink.svelte.d.ts +14 -0
  113. package/dist/GithubLink.svelte.d.ts.map +1 -0
  114. package/dist/Glyph.svelte +28 -0
  115. package/dist/Glyph.svelte.d.ts +9 -0
  116. package/dist/Glyph.svelte.d.ts.map +1 -0
  117. package/dist/Hashlink.svelte +41 -0
  118. package/dist/Hashlink.svelte.d.ts +8 -0
  119. package/dist/Hashlink.svelte.d.ts.map +1 -0
  120. package/dist/HiddenPersonalLinks.svelte +6 -0
  121. package/dist/HiddenPersonalLinks.svelte.d.ts +27 -0
  122. package/dist/HiddenPersonalLinks.svelte.d.ts.map +1 -0
  123. package/dist/HueInput.svelte +127 -0
  124. package/dist/HueInput.svelte.d.ts +11 -0
  125. package/dist/HueInput.svelte.d.ts.map +1 -0
  126. package/dist/ImgOrSvg.svelte +58 -0
  127. package/dist/ImgOrSvg.svelte.d.ts +25 -0
  128. package/dist/ImgOrSvg.svelte.d.ts.map +1 -0
  129. package/dist/LibraryDetail.svelte +297 -0
  130. package/dist/LibraryDetail.svelte.d.ts +15 -0
  131. package/dist/LibraryDetail.svelte.d.ts.map +1 -0
  132. package/dist/LibrarySummary.svelte +151 -0
  133. package/dist/LibrarySummary.svelte.d.ts +16 -0
  134. package/dist/LibrarySummary.svelte.d.ts.map +1 -0
  135. package/dist/MdnLink.svelte +40 -0
  136. package/dist/MdnLink.svelte.d.ts +8 -0
  137. package/dist/MdnLink.svelte.d.ts.map +1 -0
  138. package/dist/Mdz.svelte +30 -0
  139. package/dist/Mdz.svelte.d.ts +10 -0
  140. package/dist/Mdz.svelte.d.ts.map +1 -0
  141. package/dist/MdzNodeView.svelte +93 -0
  142. package/dist/MdzNodeView.svelte.d.ts +9 -0
  143. package/dist/MdzNodeView.svelte.d.ts.map +1 -0
  144. package/dist/ModuleLink.svelte +48 -0
  145. package/dist/ModuleLink.svelte.d.ts +8 -0
  146. package/dist/ModuleLink.svelte.d.ts.map +1 -0
  147. package/dist/PasteFromClipboard.svelte +35 -0
  148. package/dist/PasteFromClipboard.svelte.d.ts +9 -0
  149. package/dist/PasteFromClipboard.svelte.d.ts.map +1 -0
  150. package/dist/PendingAnimation.svelte +62 -0
  151. package/dist/PendingAnimation.svelte.d.ts +13 -0
  152. package/dist/PendingAnimation.svelte.d.ts.map +1 -0
  153. package/dist/PendingButton.svelte +75 -0
  154. package/dist/PendingButton.svelte.d.ts +17 -0
  155. package/dist/PendingButton.svelte.d.ts.map +1 -0
  156. package/dist/ProjectLinks.svelte +54 -0
  157. package/dist/ProjectLinks.svelte.d.ts +19 -0
  158. package/dist/ProjectLinks.svelte.d.ts.map +1 -0
  159. package/dist/Redirect.svelte +44 -0
  160. package/dist/Redirect.svelte.d.ts +23 -0
  161. package/dist/Redirect.svelte.d.ts.map +1 -0
  162. package/dist/Spiders.svelte +57 -0
  163. package/dist/Spiders.svelte.d.ts +9 -0
  164. package/dist/Spiders.svelte.d.ts.map +1 -0
  165. package/dist/Svg.svelte +99 -0
  166. package/dist/Svg.svelte.d.ts +54 -0
  167. package/dist/Svg.svelte.d.ts.map +1 -0
  168. package/dist/Teleport.svelte +48 -0
  169. package/dist/Teleport.svelte.d.ts +15 -0
  170. package/dist/Teleport.svelte.d.ts.map +1 -0
  171. package/dist/ThemeInput.svelte +75 -0
  172. package/dist/ThemeInput.svelte.d.ts +15 -0
  173. package/dist/ThemeInput.svelte.d.ts.map +1 -0
  174. package/dist/Themed.svelte +101 -0
  175. package/dist/Themed.svelte.d.ts +24 -0
  176. package/dist/Themed.svelte.d.ts.map +1 -0
  177. package/dist/TomeContent.svelte +67 -0
  178. package/dist/TomeContent.svelte.d.ts +12 -0
  179. package/dist/TomeContent.svelte.d.ts.map +1 -0
  180. package/dist/TomeHeader.svelte +56 -0
  181. package/dist/TomeHeader.svelte.d.ts +4 -0
  182. package/dist/TomeHeader.svelte.d.ts.map +1 -0
  183. package/dist/TomeLink.svelte +29 -0
  184. package/dist/TomeLink.svelte.d.ts +10 -0
  185. package/dist/TomeLink.svelte.d.ts.map +1 -0
  186. package/dist/TomeSection.svelte +65 -0
  187. package/dist/TomeSection.svelte.d.ts +24 -0
  188. package/dist/TomeSection.svelte.d.ts.map +1 -0
  189. package/dist/TomeSectionHeader.svelte +90 -0
  190. package/dist/TomeSectionHeader.svelte.d.ts +13 -0
  191. package/dist/TomeSectionHeader.svelte.d.ts.map +1 -0
  192. package/dist/TypeLink.svelte +19 -0
  193. package/dist/TypeLink.svelte.d.ts +7 -0
  194. package/dist/TypeLink.svelte.d.ts.map +1 -0
  195. package/dist/alert.d.ts +7 -0
  196. package/dist/alert.d.ts.map +1 -0
  197. package/dist/alert.js +6 -0
  198. package/dist/api_search.svelte.d.ts +16 -0
  199. package/dist/api_search.svelte.d.ts.map +1 -0
  200. package/dist/api_search.svelte.js +61 -0
  201. package/dist/constants.d.ts +2 -0
  202. package/dist/constants.d.ts.map +1 -0
  203. package/dist/constants.js +3 -0
  204. package/dist/context_helpers.d.ts +17 -0
  205. package/dist/context_helpers.d.ts.map +1 -0
  206. package/dist/context_helpers.js +19 -0
  207. package/dist/contextmenu_helpers.d.ts +16 -0
  208. package/dist/contextmenu_helpers.d.ts.map +1 -0
  209. package/dist/contextmenu_helpers.js +39 -0
  210. package/dist/contextmenu_state.svelte.d.ts +152 -0
  211. package/dist/contextmenu_state.svelte.d.ts.map +1 -0
  212. package/dist/contextmenu_state.svelte.js +424 -0
  213. package/dist/csp.d.ts +160 -0
  214. package/dist/csp.d.ts.map +1 -0
  215. package/dist/csp.js +354 -0
  216. package/dist/csp_of_ryanatkn.d.ts +6 -0
  217. package/dist/csp_of_ryanatkn.d.ts.map +1 -0
  218. package/dist/csp_of_ryanatkn.js +14 -0
  219. package/dist/declaration.svelte.d.ts +84 -0
  220. package/dist/declaration.svelte.d.ts.map +1 -0
  221. package/dist/declaration.svelte.js +66 -0
  222. package/dist/declaration_contextmenu.d.ts +4 -0
  223. package/dist/declaration_contextmenu.d.ts.map +1 -0
  224. package/dist/declaration_contextmenu.js +14 -0
  225. package/dist/dialog.d.ts +24 -0
  226. package/dist/dialog.d.ts.map +1 -0
  227. package/dist/dialog.js +12 -0
  228. package/dist/dimensions.svelte.d.ts +5 -0
  229. package/dist/dimensions.svelte.d.ts.map +1 -0
  230. package/dist/dimensions.svelte.js +4 -0
  231. package/dist/docs_helpers.svelte.d.ts +48 -0
  232. package/dist/docs_helpers.svelte.d.ts.map +1 -0
  233. package/dist/docs_helpers.svelte.js +99 -0
  234. package/dist/helpers.d.ts +2 -0
  235. package/dist/helpers.d.ts.map +1 -0
  236. package/dist/helpers.js +16 -0
  237. package/dist/intersect.svelte.d.ts +47 -0
  238. package/dist/intersect.svelte.d.ts.map +1 -0
  239. package/dist/intersect.svelte.js +92 -0
  240. package/dist/library.svelte.d.ts +197 -0
  241. package/dist/library.svelte.d.ts.map +1 -0
  242. package/dist/library.svelte.js +130 -0
  243. package/dist/library_gen.d.ts +34 -0
  244. package/dist/library_gen.d.ts.map +1 -0
  245. package/dist/library_gen.js +123 -0
  246. package/dist/library_gen_helpers.d.ts +85 -0
  247. package/dist/library_gen_helpers.d.ts.map +1 -0
  248. package/dist/library_gen_helpers.js +188 -0
  249. package/dist/library_helpers.d.ts +54 -0
  250. package/dist/library_helpers.d.ts.map +1 -0
  251. package/dist/library_helpers.js +102 -0
  252. package/dist/logos.d.ts +134 -0
  253. package/dist/logos.d.ts.map +1 -0
  254. package/dist/logos.js +281 -0
  255. package/dist/mdz.d.ts +106 -0
  256. package/dist/mdz.d.ts.map +1 -0
  257. package/dist/mdz.js +1481 -0
  258. package/dist/mdz_components.d.ts +37 -0
  259. package/dist/mdz_components.d.ts.map +1 -0
  260. package/dist/mdz_components.js +12 -0
  261. package/dist/module.svelte.d.ts +47 -0
  262. package/dist/module.svelte.d.ts.map +1 -0
  263. package/dist/module.svelte.js +56 -0
  264. package/dist/module_contextmenu.d.ts +4 -0
  265. package/dist/module_contextmenu.d.ts.map +1 -0
  266. package/dist/module_contextmenu.js +14 -0
  267. package/dist/module_helpers.d.ts +69 -0
  268. package/dist/module_helpers.d.ts.map +1 -0
  269. package/dist/module_helpers.js +87 -0
  270. package/dist/rune_helpers.svelte.d.ts +6 -0
  271. package/dist/rune_helpers.svelte.d.ts.map +1 -0
  272. package/dist/rune_helpers.svelte.js +10 -0
  273. package/dist/storage.d.ts +13 -0
  274. package/dist/storage.d.ts.map +1 -0
  275. package/dist/storage.js +43 -0
  276. package/dist/svelte_helpers.d.ts +37 -0
  277. package/dist/svelte_helpers.d.ts.map +1 -0
  278. package/dist/svelte_helpers.js +245 -0
  279. package/dist/themer.svelte.d.ts +24 -0
  280. package/dist/themer.svelte.d.ts.map +1 -0
  281. package/dist/themer.svelte.js +43 -0
  282. package/dist/tome.d.ts +80 -0
  283. package/dist/tome.d.ts.map +1 -0
  284. package/dist/tome.js +27 -0
  285. package/dist/ts_helpers.d.ts +110 -0
  286. package/dist/ts_helpers.d.ts.map +1 -0
  287. package/dist/ts_helpers.js +533 -0
  288. package/dist/tsdoc_helpers.d.ts +98 -0
  289. package/dist/tsdoc_helpers.d.ts.map +1 -0
  290. package/dist/tsdoc_helpers.js +221 -0
  291. package/package.json +128 -0
  292. package/src/lib/alert.ts +14 -0
  293. package/src/lib/api_search.svelte.ts +85 -0
  294. package/src/lib/constants.ts +3 -0
  295. package/src/lib/context_helpers.ts +47 -0
  296. package/src/lib/contextmenu_helpers.ts +63 -0
  297. package/src/lib/contextmenu_state.svelte.ts +515 -0
  298. package/src/lib/csp.ts +576 -0
  299. package/src/lib/csp_of_ryanatkn.ts +16 -0
  300. package/src/lib/declaration.svelte.ts +102 -0
  301. package/src/lib/declaration_contextmenu.ts +22 -0
  302. package/src/lib/dialog.ts +35 -0
  303. package/src/lib/dimensions.svelte.ts +4 -0
  304. package/src/lib/docs_helpers.svelte.ts +149 -0
  305. package/src/lib/helpers.ts +10 -0
  306. package/src/lib/intersect.svelte.ts +152 -0
  307. package/src/lib/library.svelte.ts +162 -0
  308. package/src/lib/library_gen.ts +160 -0
  309. package/src/lib/library_gen_helpers.ts +262 -0
  310. package/src/lib/library_helpers.ts +123 -0
  311. package/src/lib/logos.ts +302 -0
  312. package/src/lib/mdz.ts +1819 -0
  313. package/src/lib/mdz_components.ts +34 -0
  314. package/src/lib/module.svelte.ts +78 -0
  315. package/src/lib/module_contextmenu.ts +20 -0
  316. package/src/lib/module_helpers.ts +113 -0
  317. package/src/lib/rune_helpers.svelte.ts +10 -0
  318. package/src/lib/storage.ts +48 -0
  319. package/src/lib/svelte_helpers.ts +303 -0
  320. package/src/lib/themer.svelte.ts +68 -0
  321. package/src/lib/tome.ts +38 -0
  322. package/src/lib/ts_helpers.ts +662 -0
  323. package/src/lib/tsdoc_helpers.ts +259 -0
@@ -0,0 +1,22 @@
1
+ import type {Declaration} from './declaration.svelte.js';
2
+ import type {ContextmenuParams} from './contextmenu_state.svelte.js';
3
+
4
+ // TODO @many expand contextmenus for docs
5
+
6
+ export const create_declaration_contextmenu = (
7
+ declaration: Declaration,
8
+ ): Array<ContextmenuParams> => {
9
+ const entries: Array<ContextmenuParams> = [];
10
+
11
+ // View source on GitHub
12
+ if (declaration.url_github) {
13
+ entries.push({
14
+ snippet: 'link',
15
+ props: {
16
+ href: declaration.url_github,
17
+ },
18
+ });
19
+ }
20
+
21
+ return entries;
22
+ };
@@ -0,0 +1,35 @@
1
+ import type {ComponentProps, Component} from 'svelte';
2
+
3
+ import type Dialog from './Dialog.svelte';
4
+
5
+ /**
6
+ * This helper function is needed to construct `DialogParams` with type safety.
7
+ * It uses TypeScript's inferred generics for functions,
8
+ * which do not work for plain objects as of v5.0.4.
9
+ * * `ContextmenuParams` uses a similar strategy.
10
+ */
11
+ export const to_dialog_params = <T extends Component<any>>(
12
+ Component: T,
13
+ props: ComponentProps<T>,
14
+ dialog_props?: Partial<ComponentProps<typeof Dialog>>,
15
+ ): DialogParams<T> => ({
16
+ Component,
17
+ props,
18
+ dialog_props,
19
+ });
20
+
21
+ /**
22
+ * This pattern is based on:
23
+ * https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/src/01-props/09-svelte-component/Component.svelte
24
+ * The main limitation is that the generic cannot be inferred automatically,
25
+ * so we use `to_dialog_params` to construct instances in most cases.
26
+ * Definining `DialogParams` with no concrete `T` lacks typechecking for `props`.
27
+ */
28
+ export interface DialogParams<T extends Component<any> = Component<any>> {
29
+ Component: T;
30
+ props: ComponentProps<T>;
31
+ dialog_props?: Partial<ComponentProps<typeof Dialog>> | undefined;
32
+ }
33
+
34
+ export type DialogLayout = 'centered' | 'page';
35
+ export const dialog_layouts: Array<DialogLayout> = ['centered', 'page'];
@@ -0,0 +1,4 @@
1
+ export class Dimensions {
2
+ width: number = $state(0);
3
+ height: number = $state(0);
4
+ }
@@ -0,0 +1,149 @@
1
+ import {resolve} from '$app/paths';
2
+ import {SvelteMap, SvelteSet} from 'svelte/reactivity';
3
+ import {ensure_end, ensure_start} from '@fuzdev/fuz_util/string.js';
4
+
5
+ import {create_context} from './context_helpers.js';
6
+
7
+ /**
8
+ * Convert a string to a URL-safe fragment identifier, preserving case for API declarations.
9
+ * Only transforms spaces and special characters, keeping valid identifier characters intact.
10
+ * Used for hash anchors in documentation.
11
+ * @param str - The string to convert to a fragment
12
+ * @returns A URL-safe fragment identifier
13
+ */
14
+ export const docs_slugify = (str: string): string => {
15
+ return (
16
+ str
17
+ .trim()
18
+ // Replace spaces and multiple hyphens with single hyphen
19
+ .replace(/\s+/g, '-')
20
+ .replace(/-+/g, '-')
21
+ // Remove invalid characters (keep letters, digits, _, $, -)
22
+ .replace(/[^\w$-]/g, '')
23
+ // Remove leading/trailing hyphens
24
+ .replace(/^-+|-+$/g, '')
25
+ );
26
+ };
27
+
28
+ export const DOCS_PATH_DEFAULT = '/docs';
29
+ export const DOCS_PATH = resolve('/docs');
30
+ export const DOCS_API_PATH = DOCS_PATH + '/api';
31
+
32
+ export const to_docs_path_info = (
33
+ path_slug: string,
34
+ pathname: string,
35
+ root_path = DOCS_PATH_DEFAULT,
36
+ ): {path: string; path_is_selected: boolean; path_segment: string | undefined} => {
37
+ const path_segment = pathname.split('/').at(-1);
38
+ const path = resolve((ensure_end(ensure_start(root_path, '/'), '/') + path_slug) as any);
39
+ const path_is_selected = path_segment === path_slug; // messy but works
40
+ return {path, path_is_selected, path_segment};
41
+ };
42
+
43
+ export const docs_links_context = create_context<DocsLinks>();
44
+
45
+ export type DocsLinkTag = 'h2' | 'h3' | 'h4';
46
+
47
+ export interface DocsLinkInfo {
48
+ id: string;
49
+ text: string;
50
+ fragment: string;
51
+ tag: DocsLinkTag | undefined;
52
+ depth: number;
53
+ order: number;
54
+ parent_id: string | undefined;
55
+ }
56
+
57
+ export class DocsLinks {
58
+ readonly root_path: string;
59
+
60
+ readonly links: SvelteMap<string, DocsLinkInfo> = new SvelteMap();
61
+
62
+ // Maps compound keys (pathname#fragment) to their original order
63
+ // This preserves order across component remounts
64
+ #fragment_to_order: Map<string, number> = new Map();
65
+
66
+ // Counter for generating unique IDs
67
+ #next_id = 0;
68
+
69
+ // Counter for generating section IDs (page-scoped, not module-scoped)
70
+ #section_counter = 0;
71
+
72
+ // Counter for generating link order values (page-scoped, not module-scoped)
73
+ // This ensures consistent ordering between SSR and client hydration
74
+ #order_counter = 0;
75
+
76
+ docs_links = $derived.by(() => {
77
+ // Build parent-child map
78
+ const children_map: Map<string | undefined, Array<DocsLinkInfo>> = new Map();
79
+
80
+ for (const link of this.links.values()) {
81
+ if (!children_map.has(link.parent_id)) {
82
+ children_map.set(link.parent_id, []);
83
+ }
84
+ children_map.get(link.parent_id)!.push(link);
85
+ }
86
+
87
+ // Sort siblings by order
88
+ for (const siblings of children_map.values()) {
89
+ siblings.sort((a, b) => a.order - b.order);
90
+ }
91
+
92
+ // Flatten tree with depth-first traversal
93
+ const result: Array<DocsLinkInfo> = [];
94
+ const traverse = (parent_id: string | undefined) => {
95
+ const children = children_map.get(parent_id) || [];
96
+ for (const child of children) {
97
+ result.push(child);
98
+ traverse(child.id); // recursively add children
99
+ }
100
+ };
101
+
102
+ traverse(undefined); // start with root nodes (no parent)
103
+ return result;
104
+ });
105
+
106
+ readonly fragments_onscreen: SvelteSet<string> = new SvelteSet();
107
+
108
+ constructor(root_path = DOCS_PATH_DEFAULT) {
109
+ this.root_path = root_path;
110
+ }
111
+
112
+ add(
113
+ fragment: string,
114
+ text: string,
115
+ pathname: string,
116
+ tag?: DocsLinkTag,
117
+ depth = 1,
118
+ parent_id?: string,
119
+ explicit_id?: string,
120
+ ): string {
121
+ // Use explicit ID if provided, otherwise generate one
122
+ const id = explicit_id ?? 'docs_link_' + this.#next_id++;
123
+
124
+ // Use compound key (pathname#fragment) to preserve order across remounts
125
+ const page_fragment_key = `${pathname}#${fragment}`;
126
+ const existing_order = this.#fragment_to_order.get(page_fragment_key);
127
+ const order = existing_order ?? this.#order_counter++;
128
+
129
+ // Store order for new fragments only
130
+ if (existing_order === undefined) {
131
+ this.#fragment_to_order.set(page_fragment_key, order);
132
+ }
133
+
134
+ this.links.set(id, {id, fragment, text, tag, depth, order, parent_id});
135
+ return id;
136
+ }
137
+
138
+ remove(id: string): void {
139
+ this.links.delete(id);
140
+ }
141
+
142
+ /**
143
+ * Generate a unique section ID for the current page render.
144
+ * This counter is instance-scoped, ensuring SSR/client consistency.
145
+ */
146
+ generate_section_id(): string {
147
+ return `section_${this.#section_counter++}`;
148
+ }
149
+ }
@@ -0,0 +1,10 @@
1
+ export const render_value_to_string = (value: unknown): string => {
2
+ if (value === null) return 'null';
3
+ if (value === undefined) return 'undefined';
4
+ const type = typeof value;
5
+ if (type === 'string') return `'${value}'`; // eslint-disable-line @typescript-eslint/no-base-to-string
6
+ if (type === 'number' || type === 'boolean') return value + ''; // eslint-disable-line @typescript-eslint/no-base-to-string
7
+ if (Array.isArray(value)) return `[${value.map(render_value_to_string).join(', ')}]`;
8
+ if (type === 'object') return JSON.stringify(value);
9
+ return value + ''; // eslint-disable-line @typescript-eslint/no-base-to-string
10
+ };
@@ -0,0 +1,152 @@
1
+ import type {Attachment} from 'svelte/attachments';
2
+ import {deep_equal} from '@fuzdev/fuz_util/deep_equal.js';
3
+
4
+ export interface IntersectParams {
5
+ /**
6
+ * Called when the element enters or leaves the viewport until disconnected.
7
+ */
8
+ onintersect?: OnIntersect;
9
+ /**
10
+ * Called when the attachment's observer is disconnected,
11
+ * either by the user calling disconnect or the attachment being destroyed.
12
+ */
13
+ ondisconnect?: OnDisconnect;
14
+ /**
15
+ * A value of `1` disconnects after `el` enters and leaves the viewport one time,
16
+ * similar to 'once' for an event.
17
+ * `0` disables and `undefined` or a negative number like `-1` never disconnects.
18
+ */
19
+ count?: number;
20
+ /**
21
+ * Same as the `options` param to
22
+ * [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options)
23
+ */
24
+ options?: IntersectionObserverInit;
25
+ }
26
+
27
+ export type IntersectParamsOrCallback = OnIntersect | IntersectParams;
28
+
29
+ /**
30
+ * Creates an attachment that observes element viewport intersection.
31
+ * Uses the lazy function pattern to optimize reactivity:
32
+ * callbacks can update without recreating the observer, preserving state.
33
+ * @param get_params Function that returns callback, params object, or nullish to disable
34
+ */
35
+ export const intersect =
36
+ (
37
+ get_params: () => IntersectParamsOrCallback | null | undefined,
38
+ ): Attachment<HTMLElement | SVGElement> =>
39
+ (el) => {
40
+ // State that persists across callback changes
41
+ let intersecting = false;
42
+ let intersections = 0;
43
+ let observer: IntersectionObserver | null = null;
44
+ let current_options: IntersectionObserverInit | undefined = undefined;
45
+
46
+ // Current callbacks - updated reactively without recreating observer
47
+ let current_onintersect: OnIntersect | undefined;
48
+ let current_ondisconnect: OnDisconnect | undefined;
49
+ let current_count: number | undefined;
50
+
51
+ const disconnect = (): void => {
52
+ if (!observer) return;
53
+ const observer_to_disconnect = observer;
54
+ observer.disconnect();
55
+ if (current_ondisconnect) {
56
+ current_ondisconnect({intersecting, intersections, el, observer: observer_to_disconnect});
57
+ }
58
+ observer = null;
59
+ };
60
+
61
+ $effect(() => {
62
+ const params = get_params();
63
+
64
+ if (params == null) {
65
+ // Params disabled, disconnect if active
66
+ current_onintersect = undefined;
67
+ current_ondisconnect = undefined;
68
+ current_count = undefined;
69
+ if (observer) {
70
+ disconnect();
71
+ }
72
+ return;
73
+ }
74
+
75
+ // Parse params and update current callbacks
76
+ let options: IntersectionObserverInit | undefined;
77
+ if (typeof params === 'function') {
78
+ current_onintersect = params;
79
+ current_ondisconnect = undefined;
80
+ current_count = undefined;
81
+ options = undefined;
82
+ } else {
83
+ current_onintersect = params.onintersect;
84
+ current_ondisconnect = params.ondisconnect;
85
+ current_count = params.count;
86
+ options = params.options;
87
+ }
88
+
89
+ // Check if count is 0 (disabled)
90
+ if (current_count === 0) {
91
+ if (observer) {
92
+ disconnect();
93
+ }
94
+ return;
95
+ }
96
+
97
+ // Check if options changed (requires observer recreation)
98
+ const options_changed = !deep_equal(current_options, options);
99
+
100
+ if (options_changed || !observer) {
101
+ // Disconnect old observer if exists
102
+ if (observer) {
103
+ disconnect();
104
+ }
105
+
106
+ // Reset state on observer recreation
107
+ intersecting = false;
108
+ intersections = 0;
109
+ current_options = options;
110
+
111
+ // Create new observer
112
+ observer = new IntersectionObserver((entries) => {
113
+ intersecting = entries[0]!.isIntersecting;
114
+ if (current_onintersect && observer) {
115
+ current_onintersect({intersecting, intersections, el, observer, disconnect});
116
+ }
117
+ if (intersecting) {
118
+ intersections++;
119
+ } else {
120
+ if (current_count && current_count > 0 && intersections >= current_count) {
121
+ disconnect();
122
+ }
123
+ }
124
+ }, options);
125
+ observer.observe(el);
126
+ }
127
+ // If only callbacks changed, observer stays alive and uses new callbacks from closure
128
+ });
129
+
130
+ return disconnect;
131
+ };
132
+
133
+ // TODO how to forward generic `el` type?
134
+ export type OnIntersect = (state: IntersectState) => void;
135
+
136
+ export interface IntersectState {
137
+ intersecting: boolean;
138
+ intersections: number;
139
+ el: HTMLElement | SVGElement;
140
+ observer: IntersectionObserver;
141
+ disconnect: () => void;
142
+ }
143
+
144
+ // TODO how to forward generic `el` type?
145
+ export type OnDisconnect = (state: DisconnectState) => void;
146
+
147
+ export interface DisconnectState {
148
+ intersecting: boolean;
149
+ intersections: number;
150
+ el: HTMLElement | SVGElement;
151
+ observer: IntersectionObserver;
152
+ }
@@ -0,0 +1,162 @@
1
+ import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
2
+
3
+ import {create_context} from './context_helpers.js';
4
+ import {Declaration} from './declaration.svelte.js';
5
+ import {Module} from './module.svelte.js';
6
+
7
+ /**
8
+ * Rich runtime representation of a library.
9
+ *
10
+ * Wraps `LibraryJson` with computed properties and provides the root
11
+ * of the API documentation hierarchy: Library → modules → declarations
12
+ *
13
+ * @see module.svelte.ts for Module class
14
+ * @see declaration.svelte.ts for Declaration class
15
+ */
16
+ export class Library {
17
+ readonly library_json: LibraryJson = $state.raw()!;
18
+
19
+ package_json = $derived(this.library_json.package_json);
20
+ source_json = $derived(this.library_json.source_json);
21
+
22
+ name = $derived(this.library_json.name);
23
+ repo_name = $derived(this.library_json.repo_name);
24
+ repo_url = $derived(this.library_json.repo_url);
25
+ owner_name = $derived(this.library_json.owner_name);
26
+ homepage_url = $derived(this.library_json.homepage_url);
27
+ logo_url = $derived(this.library_json.logo_url);
28
+ logo_alt = $derived(this.library_json.logo_alt);
29
+ npm_url = $derived(this.library_json.npm_url);
30
+ changelog_url = $derived(this.library_json.changelog_url);
31
+ published = $derived(this.library_json.published);
32
+
33
+ /**
34
+ * Organization URL (e.g., 'https://github.com/ryanatkn').
35
+ */
36
+ org_url = $derived(
37
+ this.repo_url && this.repo_name
38
+ ? this.repo_url.endsWith('/' + this.repo_name)
39
+ ? this.repo_url.slice(0, -this.repo_name.length - 1)
40
+ : null
41
+ : null,
42
+ );
43
+
44
+ /**
45
+ * All modules as rich Module instances.
46
+ */
47
+ modules = $derived(
48
+ this.source_json.modules
49
+ ? this.source_json.modules.map((module_json) => new Module(this, module_json))
50
+ : [],
51
+ );
52
+
53
+ /**
54
+ * All modules sorted alphabetically by path.
55
+ */
56
+ modules_sorted = $derived([...this.modules].sort((a, b) => a.path.localeCompare(b.path)));
57
+
58
+ /**
59
+ * All declarations across all modules as a flat array.
60
+ */
61
+ declarations = $derived(this.modules.flatMap((module) => module.declarations));
62
+
63
+ /**
64
+ * Declaration lookup map by name. Provides O(1) lookup.
65
+ */
66
+ declaration_map = $derived(new Map(this.declarations.map((d) => [d.name, d])));
67
+
68
+ constructor(library_json: LibraryJson) {
69
+ this.library_json = library_json;
70
+ }
71
+
72
+ /**
73
+ * Look up a declaration by name.
74
+ */
75
+ lookup_declaration(name: string): Declaration | undefined {
76
+ return this.declaration_map.get(name);
77
+ }
78
+
79
+ /**
80
+ * Check if a declaration exists.
81
+ */
82
+ has_declaration(name: string): boolean {
83
+ return this.declaration_map.has(name);
84
+ }
85
+
86
+ /**
87
+ * Look up a module by its canonical path.
88
+ */
89
+ lookup_module(path: string): Module | undefined {
90
+ return this.modules.find((m) => m.path === path);
91
+ }
92
+
93
+ /**
94
+ * Search declarations by query string with multi-term AND logic.
95
+ */
96
+ search_declarations(query: string): Array<Declaration> {
97
+ return library_search_declarations(this.declarations, query);
98
+ }
99
+ }
100
+
101
+ export const library_context = create_context<Library>();
102
+
103
+ /**
104
+ * Search declarations by query string with multi-term AND logic.
105
+ */
106
+ const library_search_declarations = (
107
+ declarations: Array<Declaration>,
108
+ query: string,
109
+ ): Array<Declaration> => {
110
+ if (!query.trim()) return [];
111
+
112
+ const terms = query.trim().toLowerCase().split(/\s+/);
113
+
114
+ const results = declarations
115
+ .map((declaration) => {
116
+ let total_score = 0;
117
+
118
+ for (const term of terms) {
119
+ const name_score = library_score_match(declaration.name, term);
120
+ const kind_score = library_score_match(declaration.kind, term);
121
+ const module_score = library_score_match(declaration.module_path, term);
122
+
123
+ const term_best = Math.max(name_score, kind_score, module_score);
124
+
125
+ if (term_best === 0) {
126
+ return null;
127
+ }
128
+
129
+ total_score += term_best;
130
+ }
131
+
132
+ return {declaration, score: total_score};
133
+ })
134
+ .filter((r) => r !== null)
135
+ .sort((a, b) => b.score - a.score || a.declaration.name.localeCompare(b.declaration.name))
136
+ .map((r) => r.declaration);
137
+
138
+ return results;
139
+ };
140
+
141
+ /**
142
+ * Score a search term match against a field value.
143
+ */
144
+ const library_score_match = (field_value: string, term: string): number => {
145
+ const field_lower = field_value.toLowerCase();
146
+
147
+ if (field_lower === term) return 100;
148
+ if (field_lower.startsWith(term)) return 80;
149
+ if (field_lower.includes(term)) return 60;
150
+
151
+ let term_idx = 0;
152
+ for (const char of field_lower) {
153
+ if (char === term[term_idx]) {
154
+ term_idx++;
155
+ if (term_idx === term.length) {
156
+ return 40;
157
+ }
158
+ }
159
+ }
160
+
161
+ return 0;
162
+ };
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Library metadata generator helper.
3
+ *
4
+ * Generates package_json and source_json with rich metadata:
5
+ * - JSDoc/TSDoc comments with full tag support
6
+ * - Full type signatures
7
+ * - Source code locations
8
+ * - Parameter information with descriptions and defaults
9
+ * - Return value documentation
10
+ * - Usage examples
11
+ * - Dependency graphs
12
+ * - Svelte component props
13
+ *
14
+ * @see @fuzdev/fuz_util/source_json.js for type definitions
15
+ * @see src/lib/library_gen_helpers.ts for buildtime-only helpers
16
+ * @see src/lib/tsdoc_helpers.ts for JSDoc/TSDoc parsing
17
+ * @see src/lib/ts_helpers.ts for TypeScript analysis
18
+ * @see src/lib/svelte_helpers.ts for Svelte component analysis
19
+ */
20
+
21
+ import type {Gen} from '@ryanatkn/gro';
22
+ import {load_package_json} from '@ryanatkn/gro/package_json.js';
23
+ import type {SourceJson} from '@fuzdev/fuz_util/source_json.js';
24
+
25
+ import {ts_create_program, type ReExportInfo} from './ts_helpers.js';
26
+ import {module_extract_path, module_is_svelte} from './module_helpers.js';
27
+ import {
28
+ library_gen_collect_source_files,
29
+ library_gen_sort_modules,
30
+ library_gen_validate_no_duplicates,
31
+ library_gen_generate_ts,
32
+ library_gen_analyze_svelte_file,
33
+ library_gen_analyze_typescript_file,
34
+ } from './library_gen_helpers.js';
35
+
36
+ export interface LibraryGenOptions {
37
+ filename?: string;
38
+ }
39
+
40
+ /**
41
+ * Creates a Gen object for generating library metadata with full TypeScript analysis.
42
+ *
43
+ * Usage in a `.gen.ts` file:
44
+ * ```ts
45
+ * import {library_gen} from '@ryanatkn/fuz/library_gen.js';
46
+ * export const gen = library_gen();
47
+ * ```
48
+ */
49
+ export const library_gen = (options?: LibraryGenOptions): Gen => {
50
+ return {
51
+ generate: async ({log, filer}) => {
52
+ log.info('generating library metadata with full TypeScript analysis...');
53
+
54
+ // Ensure filer is initialized
55
+ await filer.init();
56
+
57
+ // Read package.json
58
+ const package_json = await load_package_json();
59
+
60
+ // Create TypeScript program
61
+ const program = ts_create_program(log);
62
+ if (!program) {
63
+ throw new Error('Failed to create TypeScript program - cannot generate library metadata');
64
+ }
65
+
66
+ const checker = program.getTypeChecker();
67
+
68
+ // Collect source files from filer
69
+ const source_disknodes = library_gen_collect_source_files(filer.files, log);
70
+
71
+ // Build source_json with array-based modules
72
+ // Phase 1: Analyze all modules and collect re-exports
73
+ const source_json: SourceJson = {
74
+ name: package_json.name,
75
+ version: package_json.version,
76
+ modules: [],
77
+ };
78
+
79
+ // Collect all re-exports: Map<declaration_name, Set<re_exporting_module_path>>
80
+ // The Set tracks which modules re-export each declaration
81
+ const all_re_exports: Array<{re_exporting_module: string; re_export: ReExportInfo}> = [];
82
+
83
+ for (const disknode of source_disknodes) {
84
+ const source_id = disknode.id;
85
+ const module_path = module_extract_path(source_id);
86
+ const is_svelte = module_is_svelte(module_path);
87
+
88
+ // Handle Svelte files separately (before trying to get TypeScript source file)
89
+ if (is_svelte) {
90
+ const mod = library_gen_analyze_svelte_file(disknode, module_path, checker);
91
+ source_json.modules!.push(mod);
92
+ } else {
93
+ // For TypeScript/JS files, get the source file from the program
94
+ const source_file = program.getSourceFile(source_id);
95
+ if (!source_file) {
96
+ log.warn(`Could not get source file: ${source_id}`);
97
+ continue;
98
+ }
99
+
100
+ // May throw, which we want to see
101
+ const {module: mod, re_exports} = library_gen_analyze_typescript_file(
102
+ disknode,
103
+ source_file,
104
+ module_path,
105
+ checker,
106
+ );
107
+ source_json.modules!.push(mod);
108
+
109
+ // Collect re-exports for post-processing
110
+ for (const re_export of re_exports) {
111
+ all_re_exports.push({re_exporting_module: module_path, re_export});
112
+ }
113
+ }
114
+ }
115
+
116
+ // Phase 2: Build also_exported_from arrays from re-export data
117
+ // Group re-exports by original module and declaration name
118
+ const re_export_map: Map<string, Map<string, Array<string>>> = new Map();
119
+ for (const {re_exporting_module, re_export} of all_re_exports) {
120
+ const {name, original_module} = re_export;
121
+ if (!re_export_map.has(original_module)) {
122
+ re_export_map.set(original_module, new Map());
123
+ }
124
+ const module_map = re_export_map.get(original_module)!;
125
+ if (!module_map.has(name)) {
126
+ module_map.set(name, []);
127
+ }
128
+ module_map.get(name)!.push(re_exporting_module);
129
+ }
130
+
131
+ // Merge into original declarations
132
+ for (const mod of source_json.modules ?? []) {
133
+ const module_re_exports = re_export_map.get(mod.path);
134
+ if (!module_re_exports) continue;
135
+
136
+ for (const declaration of mod.declarations ?? []) {
137
+ const re_exporters = module_re_exports.get(declaration.name);
138
+ if (re_exporters?.length) {
139
+ declaration.also_exported_from = re_exporters.sort();
140
+ }
141
+ }
142
+ }
143
+
144
+ // Sort modules alphabetically for deterministic output and cleaner diffs
145
+ source_json.modules = source_json.modules
146
+ ? library_gen_sort_modules(source_json.modules)
147
+ : undefined;
148
+
149
+ // Validate no duplicate declaration names across modules
150
+ library_gen_validate_no_duplicates(source_json, log);
151
+
152
+ log.info('library metadata generation complete');
153
+
154
+ return {
155
+ content: library_gen_generate_ts(package_json, source_json),
156
+ ...(options?.filename && {filename: options.filename}),
157
+ };
158
+ },
159
+ };
160
+ };