specra 0.1.13 → 0.2.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 (276) hide show
  1. package/LICENSE.MD +25 -4
  2. package/README.md +67 -58
  3. package/config/specra.config.schema.json +16 -0
  4. package/config/svelte-config.js +63 -0
  5. package/dist/api-parser.types.d.ts +59 -0
  6. package/dist/api-parser.types.js +5 -0
  7. package/dist/api.types.d.ts +137 -0
  8. package/dist/api.types.js +5 -0
  9. package/dist/category.d.ts +21 -0
  10. package/dist/category.js +48 -0
  11. package/dist/components/ConfigProvider.svelte +13 -0
  12. package/dist/components/ConfigProvider.svelte.d.ts +31 -0
  13. package/dist/components/docs/Accordion.svelte +18 -0
  14. package/dist/components/docs/Accordion.svelte.d.ts +10 -0
  15. package/dist/components/docs/AccordionItem.svelte +41 -0
  16. package/dist/components/docs/AccordionItem.svelte.d.ts +10 -0
  17. package/dist/components/docs/Badge.svelte +28 -0
  18. package/dist/components/docs/Badge.svelte.d.ts +9 -0
  19. package/dist/components/docs/Breadcrumb.svelte +80 -0
  20. package/dist/components/docs/Breadcrumb.svelte.d.ts +8 -0
  21. package/dist/components/docs/Callout.svelte +96 -0
  22. package/dist/components/docs/Callout.svelte.d.ts +10 -0
  23. package/dist/components/docs/Card.svelte +63 -0
  24. package/dist/components/docs/Card.svelte.d.ts +12 -0
  25. package/dist/components/docs/CardGrid.svelte +24 -0
  26. package/dist/components/docs/CardGrid.svelte.d.ts +8 -0
  27. package/dist/components/docs/CategoryIndex.svelte +110 -0
  28. package/dist/components/docs/CategoryIndex.svelte.d.ts +29 -0
  29. package/dist/components/docs/CodeBlock.svelte +172 -0
  30. package/dist/components/docs/CodeBlock.svelte.d.ts +8 -0
  31. package/dist/components/docs/Column.svelte +25 -0
  32. package/dist/components/docs/Column.svelte.d.ts +8 -0
  33. package/dist/components/docs/Columns.svelte +38 -0
  34. package/dist/components/docs/Columns.svelte.d.ts +13 -0
  35. package/dist/components/docs/DevModeBadge.svelte +15 -0
  36. package/dist/components/docs/DevModeBadge.svelte.d.ts +18 -0
  37. package/dist/components/docs/DocBadge.svelte +28 -0
  38. package/dist/components/docs/DocBadge.svelte.d.ts +9 -0
  39. package/dist/components/docs/DocLayout.svelte +107 -0
  40. package/dist/components/docs/DocLayout.svelte.d.ts +32 -0
  41. package/dist/components/docs/DocLoading.svelte +53 -0
  42. package/dist/components/docs/DocLoading.svelte.d.ts +18 -0
  43. package/dist/components/docs/DocMetadata.svelte +106 -0
  44. package/dist/components/docs/DocMetadata.svelte.d.ts +18 -0
  45. package/dist/components/docs/DocNavigation.svelte +56 -0
  46. package/dist/components/docs/DocNavigation.svelte.d.ts +12 -0
  47. package/dist/components/docs/DocTags.svelte +22 -0
  48. package/dist/components/docs/DocTags.svelte.d.ts +6 -0
  49. package/dist/components/docs/DraftBadge.svelte +10 -0
  50. package/dist/components/docs/DraftBadge.svelte.d.ts +18 -0
  51. package/dist/components/docs/Footer.svelte +72 -0
  52. package/dist/components/docs/Footer.svelte.d.ts +7 -0
  53. package/dist/components/docs/Frame.svelte +27 -0
  54. package/dist/components/docs/Frame.svelte.d.ts +9 -0
  55. package/dist/components/docs/Header.svelte +123 -0
  56. package/dist/components/docs/Header.svelte.d.ts +9 -0
  57. package/dist/components/docs/HeaderWithMenu.svelte +34 -0
  58. package/dist/components/docs/HeaderWithMenu.svelte.d.ts +17 -0
  59. package/dist/components/docs/HotReloadIndicator.svelte +44 -0
  60. package/dist/components/docs/HotReloadIndicator.svelte.d.ts +3 -0
  61. package/dist/components/docs/Icon.svelte +103 -0
  62. package/dist/components/docs/Icon.svelte.d.ts +11 -0
  63. package/dist/components/docs/Image.svelte +88 -0
  64. package/dist/components/docs/Image.svelte.d.ts +11 -0
  65. package/dist/components/docs/ImageCard.svelte +91 -0
  66. package/dist/components/docs/ImageCard.svelte.d.ts +12 -0
  67. package/dist/components/docs/ImageCardGrid.svelte +25 -0
  68. package/dist/components/docs/ImageCardGrid.svelte.d.ts +8 -0
  69. package/dist/components/docs/LayoutProviders.svelte +57 -0
  70. package/dist/components/docs/LayoutProviders.svelte.d.ts +9 -0
  71. package/dist/components/docs/Logo.svelte +25 -0
  72. package/dist/components/docs/Logo.svelte.d.ts +11 -0
  73. package/dist/components/docs/Math.svelte +54 -0
  74. package/dist/components/docs/Math.svelte.d.ts +7 -0
  75. package/dist/components/docs/MdxContent.svelte +41 -0
  76. package/dist/components/docs/MdxHotReload.svelte +78 -0
  77. package/dist/components/docs/MdxHotReload.svelte.d.ts +9 -0
  78. package/dist/components/docs/MdxLayout.svelte +16 -0
  79. package/dist/components/docs/MdxLayout.svelte.d.ts +6 -0
  80. package/dist/components/docs/Mermaid.svelte +88 -0
  81. package/dist/components/docs/Mermaid.svelte.d.ts +7 -0
  82. package/dist/components/docs/MobileDocLayout.svelte +211 -0
  83. package/dist/components/docs/MobileDocLayout.svelte.d.ts +35 -0
  84. package/dist/components/docs/MobileSidebar.svelte +122 -0
  85. package/dist/components/docs/MobileSidebar.svelte.d.ts +31 -0
  86. package/dist/components/docs/MobileSidebarWrapper.svelte +122 -0
  87. package/dist/components/docs/MobileSidebarWrapper.svelte.d.ts +32 -0
  88. package/dist/components/docs/NotFoundContent.svelte +40 -0
  89. package/dist/components/docs/NotFoundContent.svelte.d.ts +6 -0
  90. package/dist/components/docs/SearchHighlight.svelte +116 -0
  91. package/dist/components/docs/SearchHighlight.svelte.d.ts +3 -0
  92. package/dist/components/docs/SearchModal.svelte +239 -0
  93. package/dist/components/docs/SearchModal.svelte.d.ts +9 -0
  94. package/dist/components/docs/Sidebar.svelte +69 -0
  95. package/dist/components/docs/Sidebar.svelte.d.ts +31 -0
  96. package/dist/components/docs/SidebarMenuItems.svelte +344 -0
  97. package/dist/components/docs/SidebarMenuItems.svelte.d.ts +33 -0
  98. package/dist/components/docs/SidebarSkeleton.svelte +50 -0
  99. package/dist/components/docs/SidebarSkeleton.svelte.d.ts +18 -0
  100. package/dist/components/docs/SiteBanner.svelte +92 -0
  101. package/dist/components/docs/SiteBanner.svelte.d.ts +7 -0
  102. package/dist/components/docs/Step.svelte +44 -0
  103. package/dist/components/docs/Step.svelte.d.ts +8 -0
  104. package/dist/components/docs/Steps.svelte +15 -0
  105. package/dist/components/docs/Steps.svelte.d.ts +7 -0
  106. package/dist/components/docs/Tab.svelte +40 -0
  107. package/dist/components/docs/Tab.svelte.d.ts +8 -0
  108. package/dist/components/docs/TabGroups.svelte +183 -0
  109. package/dist/components/docs/TabGroups.svelte.d.ts +25 -0
  110. package/dist/components/docs/TableOfContents.svelte +100 -0
  111. package/dist/components/docs/TableOfContents.svelte.d.ts +9 -0
  112. package/dist/components/docs/Tabs.svelte +69 -0
  113. package/dist/components/docs/Tabs.svelte.d.ts +8 -0
  114. package/dist/components/docs/ThemeToggle.svelte +16 -0
  115. package/dist/components/docs/ThemeToggle.svelte.d.ts +18 -0
  116. package/dist/components/docs/Tooltip.svelte +44 -0
  117. package/dist/components/docs/Tooltip.svelte.d.ts +10 -0
  118. package/dist/components/docs/VersionSwitcher.svelte +95 -0
  119. package/dist/components/docs/VersionSwitcher.svelte.d.ts +7 -0
  120. package/dist/components/docs/Video.svelte +84 -0
  121. package/dist/components/docs/Video.svelte.d.ts +12 -0
  122. package/dist/components/docs/api/ApiEndpoint.svelte +61 -0
  123. package/dist/components/docs/api/ApiEndpoint.svelte.d.ts +11 -0
  124. package/dist/components/docs/api/ApiParams.svelte +80 -0
  125. package/dist/components/docs/api/ApiParams.svelte.d.ts +14 -0
  126. package/dist/components/docs/api/ApiPlayground.svelte +259 -0
  127. package/dist/components/docs/api/ApiPlayground.svelte.d.ts +16 -0
  128. package/dist/components/docs/api/ApiReference.svelte +278 -0
  129. package/dist/components/docs/api/ApiReference.svelte.d.ts +23 -0
  130. package/dist/components/docs/api/ApiResponse.svelte +66 -0
  131. package/dist/components/docs/api/ApiResponse.svelte.d.ts +9 -0
  132. package/dist/components/docs/api/index.d.ts +5 -0
  133. package/dist/components/docs/api/index.js +5 -0
  134. package/dist/components/docs/componentTextProps.d.ts +3 -0
  135. package/dist/components/docs/componentTextProps.js +61 -0
  136. package/dist/components/docs/index.d.ts +54 -0
  137. package/dist/components/docs/index.js +56 -0
  138. package/dist/components/global/VersionNotFound.svelte +48 -0
  139. package/dist/components/global/VersionNotFound.svelte.d.ts +7 -0
  140. package/dist/components/global/index.d.ts +1 -0
  141. package/dist/components/global/index.js +1 -0
  142. package/dist/components/index.d.ts +6 -822
  143. package/dist/components/index.js +11 -3854
  144. package/dist/components/ui/Badge.svelte +48 -0
  145. package/dist/components/ui/Badge.svelte.d.ts +15 -0
  146. package/dist/components/ui/Button.svelte +58 -0
  147. package/dist/components/ui/Button.svelte.d.ts +17 -0
  148. package/dist/components/ui/Dialog.svelte +16 -0
  149. package/dist/components/ui/Dialog.svelte.d.ts +9 -0
  150. package/dist/components/ui/DialogClose.svelte +16 -0
  151. package/dist/components/ui/DialogClose.svelte.d.ts +9 -0
  152. package/dist/components/ui/DialogContent.svelte +43 -0
  153. package/dist/components/ui/DialogContent.svelte.d.ts +10 -0
  154. package/dist/components/ui/DialogDescription.svelte +21 -0
  155. package/dist/components/ui/DialogDescription.svelte.d.ts +9 -0
  156. package/dist/components/ui/DialogFooter.svelte +20 -0
  157. package/dist/components/ui/DialogFooter.svelte.d.ts +9 -0
  158. package/dist/components/ui/DialogHeader.svelte +20 -0
  159. package/dist/components/ui/DialogHeader.svelte.d.ts +9 -0
  160. package/dist/components/ui/DialogTitle.svelte +21 -0
  161. package/dist/components/ui/DialogTitle.svelte.d.ts +9 -0
  162. package/dist/components/ui/Input.svelte +23 -0
  163. package/dist/components/ui/Input.svelte.d.ts +8 -0
  164. package/dist/components/ui/Textarea.svelte +19 -0
  165. package/dist/components/ui/Textarea.svelte.d.ts +7 -0
  166. package/dist/components/ui/index.d.ts +11 -0
  167. package/dist/components/ui/index.js +11 -0
  168. package/dist/config.d.ts +8 -0
  169. package/dist/config.js +9 -0
  170. package/dist/config.schema.json +471 -0
  171. package/dist/config.server.d.ts +46 -0
  172. package/dist/config.server.js +149 -0
  173. package/dist/{mdx-ColN3Cyg.d.mts → config.types.d.ts} +22 -75
  174. package/dist/config.types.js +39 -0
  175. package/dist/dev-utils.d.ts +29 -0
  176. package/dist/dev-utils.js +63 -0
  177. package/dist/index.d.ts +19 -4
  178. package/dist/index.js +25 -4861
  179. package/dist/mdx-cache.d.ts +41 -0
  180. package/dist/mdx-cache.js +160 -0
  181. package/dist/mdx-components.js +50 -1931
  182. package/dist/mdx-security.d.ts +76 -0
  183. package/dist/mdx-security.js +217 -0
  184. package/dist/mdx.d.ts +73 -0
  185. package/dist/mdx.js +1099 -0
  186. package/dist/middleware/index.d.ts +1 -0
  187. package/dist/middleware/index.js +2 -0
  188. package/dist/middleware/security.d.ts +22 -47
  189. package/dist/middleware/security.js +111 -137
  190. package/dist/parsers/base-parser.d.ts +14 -0
  191. package/dist/parsers/base-parser.js +1 -0
  192. package/dist/parsers/index.d.ts +16 -0
  193. package/dist/parsers/index.js +51 -0
  194. package/dist/parsers/openapi-parser.d.ts +18 -0
  195. package/dist/parsers/openapi-parser.js +209 -0
  196. package/dist/parsers/postman-parser.d.ts +20 -0
  197. package/dist/parsers/postman-parser.js +260 -0
  198. package/dist/parsers/specra-parser.d.ts +10 -0
  199. package/dist/parsers/specra-parser.js +18 -0
  200. package/dist/redirects.d.ts +12 -0
  201. package/dist/redirects.js +30 -0
  202. package/dist/remark-code-meta.d.ts +6 -0
  203. package/dist/remark-code-meta.js +21 -0
  204. package/dist/sidebar-utils.d.ts +59 -0
  205. package/dist/sidebar-utils.js +144 -0
  206. package/dist/stores/config.d.ts +20 -0
  207. package/dist/stores/config.js +45 -0
  208. package/dist/stores/index.d.ts +4 -0
  209. package/dist/stores/index.js +4 -0
  210. package/dist/stores/sidebar.d.ts +7 -0
  211. package/dist/stores/sidebar.js +12 -0
  212. package/dist/stores/tabs.d.ts +6 -0
  213. package/dist/stores/tabs.js +41 -0
  214. package/dist/stores/theme.d.ts +7 -0
  215. package/dist/stores/theme.js +75 -0
  216. package/dist/{styles.css → styles/globals.css} +136 -6
  217. package/dist/toc.d.ts +9 -0
  218. package/dist/toc.js +15 -0
  219. package/dist/utils.d.ts +13 -0
  220. package/dist/utils.js +30 -0
  221. package/package.json +47 -90
  222. package/dist/app/api/mdx-watch/route.d.mts +0 -10
  223. package/dist/app/api/mdx-watch/route.d.ts +0 -10
  224. package/dist/app/api/mdx-watch/route.js +0 -118
  225. package/dist/app/api/mdx-watch/route.js.map +0 -1
  226. package/dist/app/api/mdx-watch/route.mjs +0 -91
  227. package/dist/app/api/mdx-watch/route.mjs.map +0 -1
  228. package/dist/chunk-6S3EJVEO.mjs +0 -259
  229. package/dist/chunk-6S3EJVEO.mjs.map +0 -1
  230. package/dist/chunk-BE7EROIW.mjs +0 -212
  231. package/dist/chunk-BE7EROIW.mjs.map +0 -1
  232. package/dist/chunk-CWHRZHZO.mjs +0 -168
  233. package/dist/chunk-CWHRZHZO.mjs.map +0 -1
  234. package/dist/chunk-D5VDVYFY.mjs +0 -1325
  235. package/dist/chunk-D5VDVYFY.mjs.map +0 -1
  236. package/dist/chunk-WMCO2UX5.mjs +0 -585
  237. package/dist/chunk-WMCO2UX5.mjs.map +0 -1
  238. package/dist/chunk-XEMGCPZZ.mjs +0 -475
  239. package/dist/chunk-XEMGCPZZ.mjs.map +0 -1
  240. package/dist/components/index.d.mts +0 -822
  241. package/dist/components/index.js.map +0 -1
  242. package/dist/components/index.mjs +0 -3741
  243. package/dist/components/index.mjs.map +0 -1
  244. package/dist/index.d.mts +0 -4
  245. package/dist/index.js.map +0 -1
  246. package/dist/index.mjs +0 -1897
  247. package/dist/index.mjs.map +0 -1
  248. package/dist/layouts/index.d.mts +0 -34
  249. package/dist/layouts/index.d.ts +0 -34
  250. package/dist/layouts/index.js +0 -453
  251. package/dist/layouts/index.js.map +0 -1
  252. package/dist/layouts/index.mjs +0 -173
  253. package/dist/layouts/index.mjs.map +0 -1
  254. package/dist/lib/index.d.mts +0 -583
  255. package/dist/lib/index.d.ts +0 -583
  256. package/dist/lib/index.js +0 -1595
  257. package/dist/lib/index.js.map +0 -1
  258. package/dist/lib/index.mjs +0 -111
  259. package/dist/lib/index.mjs.map +0 -1
  260. package/dist/mdx-ColN3Cyg.d.ts +0 -352
  261. package/dist/mdx-components.d.mts +0 -86
  262. package/dist/mdx-components.d.ts +0 -86
  263. package/dist/mdx-components.js.map +0 -1
  264. package/dist/mdx-components.mjs +0 -206
  265. package/dist/mdx-components.mjs.map +0 -1
  266. package/dist/middleware/security.d.mts +0 -82
  267. package/dist/middleware/security.js.map +0 -1
  268. package/dist/middleware/security.mjs +0 -84
  269. package/dist/middleware/security.mjs.map +0 -1
  270. package/dist/styles.css.map +0 -1
  271. package/dist/styles.d.mts +0 -2
  272. package/dist/styles.d.ts +0 -2
  273. package/dist/styles.js +0 -2
  274. package/dist/styles.js.map +0 -1
  275. package/dist/styles.mjs +0 -1
  276. package/dist/styles.mjs.map +0 -1
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import { ZoomIn, X } from 'lucide-svelte';
3
+
4
+ interface Props {
5
+ src: string;
6
+ alt: string;
7
+ caption?: string;
8
+ width?: number;
9
+ height?: number;
10
+ zoom?: boolean;
11
+ }
12
+
13
+ let {
14
+ src,
15
+ alt,
16
+ caption,
17
+ width,
18
+ height,
19
+ zoom = true,
20
+ }: Props = $props();
21
+
22
+ let isZoomed = $state(false);
23
+
24
+ function handleKeydown(event: KeyboardEvent) {
25
+ if (event.key === 'Escape' && isZoomed) {
26
+ isZoomed = false;
27
+ }
28
+ }
29
+ </script>
30
+
31
+ <svelte:window onkeydown={handleKeydown} />
32
+
33
+ <figure class="my-6">
34
+ <div class="relative group rounded-xl border border-border overflow-hidden bg-muted/30">
35
+ <img
36
+ {src}
37
+ {alt}
38
+ width={width || 1200}
39
+ height={height || 675}
40
+ class="w-full h-auto"
41
+ loading="lazy"
42
+ />
43
+ {#if zoom}
44
+ <button
45
+ onclick={() => (isZoomed = true)}
46
+ class="absolute top-3 right-3 p-2 rounded-md bg-background/80 backdrop-blur-sm border border-border opacity-0 group-hover:opacity-100 transition-opacity hover:bg-background"
47
+ aria-label="Zoom image"
48
+ >
49
+ <ZoomIn class="h-4 w-4 text-foreground" />
50
+ </button>
51
+ {/if}
52
+ </div>
53
+ {#if caption}
54
+ <figcaption class="mt-2 text-center text-sm text-muted-foreground italic">
55
+ {caption}
56
+ </figcaption>
57
+ {/if}
58
+ </figure>
59
+
60
+ <!-- Zoom Modal -->
61
+ {#if isZoomed}
62
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
63
+ <div
64
+ class="fixed inset-0 z-50 bg-background/95 backdrop-blur-sm flex items-center justify-center p-4"
65
+ onclick={() => (isZoomed = false)}
66
+ onkeydown={(e) => { if (e.key === 'Escape') isZoomed = false; }}
67
+ >
68
+ <button
69
+ onclick={() => (isZoomed = false)}
70
+ class="absolute top-4 right-4 p-2 rounded-md bg-muted hover:bg-muted/80 transition-colors"
71
+ aria-label="Close"
72
+ >
73
+ <X class="h-5 w-5 text-foreground" />
74
+ </button>
75
+ <div class="max-w-7xl max-h-[90vh] overflow-auto">
76
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
77
+ <img
78
+ {src}
79
+ {alt}
80
+ width={width || 1920}
81
+ height={height || 1080}
82
+ class="w-full h-auto"
83
+ onclick={(e) => e.stopPropagation()}
84
+ onkeydown={(e) => { if (e.key === 'Enter') e.stopPropagation(); }}
85
+ />
86
+ </div>
87
+ </div>
88
+ {/if}
@@ -0,0 +1,11 @@
1
+ interface Props {
2
+ src: string;
3
+ alt: string;
4
+ caption?: string;
5
+ width?: number;
6
+ height?: number;
7
+ zoom?: boolean;
8
+ }
9
+ declare const Image: import("svelte").Component<Props, {}, "">;
10
+ type Image = ReturnType<typeof Image>;
11
+ export default Image;
@@ -0,0 +1,91 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ src: string;
4
+ alt: string;
5
+ title?: string;
6
+ description?: string;
7
+ href?: string;
8
+ external?: boolean;
9
+ aspectRatio?: 'square' | 'video' | 'portrait';
10
+ }
11
+
12
+ let {
13
+ src,
14
+ alt,
15
+ title,
16
+ description,
17
+ href,
18
+ external = false,
19
+ aspectRatio = 'video',
20
+ }: Props = $props();
21
+
22
+ const aspectRatios: Record<string, string> = {
23
+ square: 'aspect-square',
24
+ video: 'aspect-video',
25
+ portrait: 'aspect-[3/4]',
26
+ };
27
+
28
+ let aspectClass = $derived(aspectRatios[aspectRatio] || aspectRatios.video);
29
+ let hasInfo = $derived(!!title || !!description);
30
+ </script>
31
+
32
+ {#if href}
33
+ <a
34
+ {href}
35
+ class="image-card-link group block rounded-xl border border-border hover:border-primary/50 hover:shadow-lg transition-all overflow-hidden p-0"
36
+ target={external ? '_blank' : undefined}
37
+ rel={external ? 'noopener noreferrer' : undefined}
38
+ >
39
+ <div class="flex flex-col gap-0 p-0">
40
+ <div class="w-full {aspectClass} overflow-hidden {hasInfo ? 'rounded-t-xl' : 'rounded-xl'} bg-muted relative">
41
+ <img
42
+ {src}
43
+ {alt}
44
+ class="object-cover transition-transform duration-300 group-hover:scale-105 absolute inset-0 w-full h-full"
45
+ loading="lazy"
46
+ />
47
+ </div>
48
+ {#if hasInfo}
49
+ <div class="p-3 flex flex-col gap-1">
50
+ {#if title}
51
+ <h3 class="font-semibold text-foreground mb-0 no-underline group-hover:text-primary transition-colors">
52
+ {title}
53
+ </h3>
54
+ {/if}
55
+ {#if description}
56
+ <p class="text-sm text-muted-foreground line-clamp-2 no-underline mb-0">
57
+ {description}
58
+ </p>
59
+ {/if}
60
+ </div>
61
+ {/if}
62
+ </div>
63
+ </a>
64
+ {:else}
65
+ <div class="block rounded-xl border border-border overflow-hidden bg-card p-0">
66
+ <div class="flex flex-col gap-0 p-0">
67
+ <div class="w-full {aspectClass} overflow-hidden {hasInfo ? 'rounded-t-xl' : 'rounded-xl'} bg-muted relative">
68
+ <img
69
+ {src}
70
+ {alt}
71
+ class="object-cover absolute inset-0 w-full h-full"
72
+ loading="lazy"
73
+ />
74
+ </div>
75
+ {#if hasInfo}
76
+ <div class="p-3 flex flex-col gap-1">
77
+ {#if title}
78
+ <h3 class="font-semibold text-foreground mb-0 no-underline">
79
+ {title}
80
+ </h3>
81
+ {/if}
82
+ {#if description}
83
+ <p class="text-sm text-muted-foreground line-clamp-2 no-underline mb-0">
84
+ {description}
85
+ </p>
86
+ {/if}
87
+ </div>
88
+ {/if}
89
+ </div>
90
+ </div>
91
+ {/if}
@@ -0,0 +1,12 @@
1
+ interface Props {
2
+ src: string;
3
+ alt: string;
4
+ title?: string;
5
+ description?: string;
6
+ href?: string;
7
+ external?: boolean;
8
+ aspectRatio?: 'square' | 'video' | 'portrait';
9
+ }
10
+ declare const ImageCard: import("svelte").Component<Props, {}, "">;
11
+ type ImageCard = ReturnType<typeof ImageCard>;
12
+ export default ImageCard;
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ cols?: 1 | 2 | 3 | 4;
6
+ children?: Snippet;
7
+ }
8
+
9
+ let { cols = 3, children }: Props = $props();
10
+
11
+ const gridCols: Record<number, string> = {
12
+ 1: 'grid-cols-1',
13
+ 2: 'grid-cols-1 md:grid-cols-2',
14
+ 3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
15
+ 4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
16
+ };
17
+
18
+ let gridClass = $derived(gridCols[cols] || gridCols[3]);
19
+ </script>
20
+
21
+ <div class="grid {gridClass} gap-4 my-6">
22
+ {#if children}
23
+ {@render children()}
24
+ {/if}
25
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ cols?: 1 | 2 | 3 | 4;
4
+ children?: Snippet;
5
+ }
6
+ declare const ImageCardGrid: import("svelte").Component<Props, {}, "">;
7
+ type ImageCardGrid = ReturnType<typeof ImageCardGrid>;
8
+ export default ImageCardGrid;
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Root layout provider that initializes all Specra stores.
4
+ * Use this in your root +layout.svelte to set up config, theme, and tab state.
5
+ *
6
+ * In Svelte, this replaces:
7
+ * - React ConfigProvider
8
+ * - React TabProvider + TabSync
9
+ * - React SidebarStateProvider
10
+ * - React ThemeProvider (next-themes)
11
+ *
12
+ * Usage:
13
+ * ```svelte
14
+ * <LayoutProviders config={data.config}>
15
+ * <slot />
16
+ * </LayoutProviders>
17
+ * ```
18
+ */
19
+ import { onMount } from 'svelte'
20
+ import { setConfigContext } from '../../stores/config'
21
+ import { tabStore } from '../../stores/tabs'
22
+ import { themeStore } from '../../stores/theme'
23
+ import type { SpecraConfig } from '../../config.types'
24
+
25
+ let {
26
+ config,
27
+ currentPageTabGroup,
28
+ children
29
+ }: {
30
+ config: SpecraConfig
31
+ currentPageTabGroup?: string
32
+ children?: import('svelte').Snippet
33
+ } = $props()
34
+
35
+ // Set config in context for child components
36
+ const configCtx = setConfigContext(config)
37
+
38
+ // Update config context when prop changes
39
+ $effect(() => {
40
+ configCtx.set(config)
41
+ })
42
+
43
+ // Initialize tab store
44
+ $effect(() => {
45
+ const defaultTab = config.navigation?.tabGroups?.[0]?.id || ''
46
+ tabStore.initialize(defaultTab)
47
+ })
48
+
49
+ // Sync tab state when page tab group changes
50
+ $effect(() => {
51
+ if (currentPageTabGroup) {
52
+ tabStore.set(currentPageTabGroup)
53
+ }
54
+ })
55
+ </script>
56
+
57
+ {@render children?.()}
@@ -0,0 +1,9 @@
1
+ import type { SpecraConfig } from '../../config.types';
2
+ type $$ComponentProps = {
3
+ config: SpecraConfig;
4
+ currentPageTabGroup?: string;
5
+ children?: import('svelte').Snippet;
6
+ };
7
+ declare const LayoutProviders: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type LayoutProviders = ReturnType<typeof LayoutProviders>;
9
+ export default LayoutProviders;
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import { themeStore } from '../../stores/theme.js';
3
+
4
+ interface Props {
5
+ logo?: string | { light: string; dark: string };
6
+ alt?: string;
7
+ className?: string;
8
+ }
9
+
10
+ let { logo, alt = 'Logo', className = 'h-8 w-8 object-contain' }: Props = $props();
11
+
12
+ let currentSrc = $derived.by(() => {
13
+ if (!logo) return '';
14
+ if (typeof logo === 'string') return logo;
15
+ return $themeStore === 'dark' ? logo.dark : logo.light;
16
+ });
17
+ </script>
18
+
19
+ {#if logo}
20
+ <img
21
+ src={currentSrc}
22
+ {alt}
23
+ class={className}
24
+ />
25
+ {/if}
@@ -0,0 +1,11 @@
1
+ interface Props {
2
+ logo?: string | {
3
+ light: string;
4
+ dark: string;
5
+ };
6
+ alt?: string;
7
+ className?: string;
8
+ }
9
+ declare const Logo: import("svelte").Component<Props, {}, "">;
10
+ type Logo = ReturnType<typeof Logo>;
11
+ export default Logo;
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+
4
+ interface Props {
5
+ children: string;
6
+ block?: boolean;
7
+ }
8
+
9
+ let { children, block = false }: Props = $props();
10
+
11
+ let containerEl: HTMLSpanElement | HTMLDivElement;
12
+
13
+ async function renderMath(el: HTMLElement, expr: string, displayMode: boolean) {
14
+ try {
15
+ const katex = (await import('katex')).default;
16
+ katex.render(expr, el, {
17
+ throwOnError: false,
18
+ displayMode,
19
+ });
20
+ } catch (err) {
21
+ console.error('KaTeX rendering error:', err);
22
+ if (el) {
23
+ el.textContent = expr;
24
+ }
25
+ }
26
+ }
27
+
28
+ onMount(() => {
29
+ if (containerEl) {
30
+ renderMath(containerEl, children, block);
31
+ }
32
+ });
33
+
34
+ // Re-render when children or block changes
35
+ $effect(() => {
36
+ const expr = children;
37
+ const isBlock = block;
38
+ if (containerEl) {
39
+ renderMath(containerEl, expr, isBlock);
40
+ }
41
+ });
42
+ </script>
43
+
44
+ {#if block}
45
+ <div
46
+ bind:this={containerEl}
47
+ class="my-6 overflow-x-auto text-center"
48
+ ></div>
49
+ {:else}
50
+ <span
51
+ bind:this={containerEl}
52
+ class="inline-block"
53
+ ></span>
54
+ {/if}
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ children: string;
3
+ block?: boolean;
4
+ }
5
+ declare const Math: import("svelte").Component<Props, {}, "">;
6
+ type Math = ReturnType<typeof Math>;
7
+ export default Math;
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import MdxContent from './MdxContent.svelte';
3
+ import type { Component } from 'svelte';
4
+
5
+ interface MdxNode {
6
+ type: 'html' | 'component';
7
+ content?: string;
8
+ name?: string;
9
+ props?: Record<string, any>;
10
+ children?: MdxNode[];
11
+ }
12
+
13
+ interface Props {
14
+ nodes: MdxNode[];
15
+ components: Record<string, Component>;
16
+ }
17
+
18
+ let { nodes, components }: Props = $props();
19
+ </script>
20
+
21
+ {#each nodes as node}
22
+ {#if node.type === 'html'}
23
+ {@html node.content}
24
+ {:else if node.type === 'component' && node.name}
25
+ {@const Comp = components[node.name]}
26
+ {#if Comp}
27
+ {#if node.children && node.children.length > 0}
28
+ <svelte:component this={Comp} {...node.props}>
29
+ <MdxContent nodes={node.children} {components} />
30
+ </svelte:component>
31
+ {:else}
32
+ <svelte:component this={Comp} {...node.props} />
33
+ {/if}
34
+ {:else}
35
+ <!-- Unknown component: {node.name} -->
36
+ {#if node.children}
37
+ <MdxContent nodes={node.children} {components} />
38
+ {/if}
39
+ {/if}
40
+ {/if}
41
+ {/each}
@@ -0,0 +1,78 @@
1
+ <script lang="ts">
2
+ import { browser } from '$app/environment';
3
+ import { dev } from '$app/environment';
4
+ import { invalidateAll } from '$app/navigation';
5
+
6
+ interface Props {
7
+ /** Polling interval in ms for checking content changes */
8
+ pollInterval?: number;
9
+ /** Whether to automatically refresh on content change */
10
+ autoRefresh?: boolean;
11
+ }
12
+
13
+ let { pollInterval = 2000, autoRefresh = true }: Props = $props();
14
+
15
+ let lastContentHash = $state('');
16
+ let isWatching = $state(false);
17
+
18
+ $effect(() => {
19
+ if (!browser || !dev || !autoRefresh) return;
20
+
21
+ isWatching = true;
22
+ let intervalId: ReturnType<typeof setInterval>;
23
+
24
+ // Use Vite HMR if available
25
+ if (import.meta.hot) {
26
+ // Listen for custom mdx/md file changes via Vite plugin
27
+ import.meta.hot.on('specra:content-update', (data: { file: string; hash?: string }) => {
28
+ console.log(`[Specra] Content updated: ${data.file}`);
29
+ invalidateAll();
30
+ });
31
+
32
+ // Also listen for generic file changes that match doc patterns
33
+ import.meta.hot.on('vite:beforeUpdate', (payload: { updates: Array<{ path: string }> }) => {
34
+ const hasDocUpdate = payload.updates?.some(
35
+ (update) =>
36
+ update.path.includes('/docs/') ||
37
+ update.path.endsWith('.md') ||
38
+ update.path.endsWith('.svx') ||
39
+ update.path.endsWith('.mdx')
40
+ );
41
+ if (hasDocUpdate) {
42
+ console.log('[Specra] Doc file changed, refreshing...');
43
+ invalidateAll();
44
+ }
45
+ });
46
+ } else {
47
+ // Fallback: Poll the mdx-watch API endpoint
48
+ intervalId = setInterval(async () => {
49
+ try {
50
+ const res = await fetch('/api/mdx-watch');
51
+ if (res.ok) {
52
+ const data = await res.json();
53
+ const newHash = data.hash || data.timestamp || '';
54
+
55
+ if (lastContentHash && newHash !== lastContentHash) {
56
+ console.log('[Specra] Content change detected, refreshing...');
57
+ invalidateAll();
58
+ }
59
+ lastContentHash = newHash;
60
+ }
61
+ } catch {
62
+ // Silently ignore polling errors
63
+ }
64
+ }, pollInterval);
65
+ }
66
+
67
+ return () => {
68
+ isWatching = false;
69
+ if (intervalId) clearInterval(intervalId);
70
+ };
71
+ });
72
+ </script>
73
+
74
+ <!-- MdxHotReload is a side-effect only component, no visible output -->
75
+ {#if dev && isWatching}
76
+ <!-- Hidden marker for dev tools inspection -->
77
+ <div data-specra-hot-reload="active" class="hidden"></div>
78
+ {/if}
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ /** Polling interval in ms for checking content changes */
3
+ pollInterval?: number;
4
+ /** Whether to automatically refresh on content change */
5
+ autoRefresh?: boolean;
6
+ }
7
+ declare const MdxHotReload: import("svelte").Component<Props, {}, "">;
8
+ type MdxHotReload = ReturnType<typeof MdxHotReload>;
9
+ export default MdxHotReload;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ /**
3
+ * mdsvex layout component for documentation pages.
4
+ * This provides default styling for all markdown content elements.
5
+ *
6
+ * Usage in mdsvex config:
7
+ * ```js
8
+ * mdsvex({ layout: 'specra/components/MdxLayout' })
9
+ * ```
10
+ */
11
+ let { children }: { children?: import('svelte').Snippet } = $props()
12
+ </script>
13
+
14
+ <div class="prose prose-slate dark:prose-invert max-w-none prose-headings:scroll-mt-24 prose-headings:font-semibold prose-h1:text-4xl prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-4 prose-h3:text-2xl prose-h3:mt-8 prose-h3:mb-3 prose-p:text-base prose-p:leading-7 prose-p:text-muted-foreground prose-p:mb-4 prose-a:font-normal prose-a:transition-all prose-code:text-primary prose-code:bg-muted/50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:text-[13px] prose-code:font-mono prose-code:border prose-code:border-border/50 prose-code:before:content-none prose-code:after:content-none prose-pre:bg-transparent prose-pre:p-0 prose-ul:list-disc prose-ul:list-inside prose-ul:space-y-2 prose-ul:mb-4 prose-ol:list-decimal prose-ol:list-inside prose-ol:space-y-2 prose-ol:mb-4 prose-li:leading-7 prose-li:text-muted-foreground prose-strong:text-foreground prose-strong:font-semibold">
15
+ {@render children?.()}
16
+ </div>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ children?: import('svelte').Snippet;
3
+ };
4
+ declare const MdxLayout: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type MdxLayout = ReturnType<typeof MdxLayout>;
6
+ export default MdxLayout;
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+
4
+ interface Props {
5
+ chart: string;
6
+ caption?: string;
7
+ }
8
+
9
+ let { chart, caption }: Props = $props();
10
+
11
+ let containerEl: HTMLDivElement;
12
+ let error = $state<string | null>(null);
13
+
14
+ async function renderChart() {
15
+ if (!containerEl) return;
16
+
17
+ try {
18
+ const mermaid = (await import('mermaid')).default;
19
+
20
+ mermaid.initialize({
21
+ startOnLoad: false,
22
+ theme: document.documentElement.classList.contains('dark') ? 'dark' : 'default',
23
+ securityLevel: 'loose',
24
+ fontFamily: 'inherit',
25
+ });
26
+
27
+ const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
28
+ const { svg } = await mermaid.render(id, chart);
29
+ containerEl.innerHTML = svg;
30
+ error = null;
31
+ } catch (err) {
32
+ console.error('Mermaid rendering error:', err);
33
+ error = err instanceof Error ? err.message : 'Failed to render diagram';
34
+ }
35
+ }
36
+
37
+ let observer: MutationObserver | null = null;
38
+
39
+ onMount(() => {
40
+ renderChart();
41
+
42
+ // Re-render on theme change
43
+ observer = new MutationObserver((mutations) => {
44
+ mutations.forEach((mutation) => {
45
+ if (mutation.attributeName === 'class') {
46
+ renderChart();
47
+ }
48
+ });
49
+ });
50
+
51
+ observer.observe(document.documentElement, { attributes: true });
52
+ });
53
+
54
+ onDestroy(() => {
55
+ if (observer) {
56
+ observer.disconnect();
57
+ observer = null;
58
+ }
59
+ });
60
+
61
+ // Re-render when chart prop changes
62
+ $effect(() => {
63
+ const _chart = chart;
64
+ if (containerEl) {
65
+ renderChart();
66
+ }
67
+ });
68
+ </script>
69
+
70
+ {#if error}
71
+ <div class="my-6 p-4 rounded-xl border border-red-500/50 bg-red-500/10">
72
+ <p class="text-sm text-red-600 dark:text-red-400 font-mono">
73
+ Mermaid Error: {error}
74
+ </p>
75
+ </div>
76
+ {:else}
77
+ <figure class="my-6">
78
+ <div
79
+ bind:this={containerEl}
80
+ class="flex justify-center items-center p-6 rounded-xl border border-border bg-muted/30 overflow-x-auto"
81
+ ></div>
82
+ {#if caption}
83
+ <figcaption class="mt-2 text-center text-sm text-muted-foreground italic">
84
+ {caption}
85
+ </figcaption>
86
+ {/if}
87
+ </figure>
88
+ {/if}
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ chart: string;
3
+ caption?: string;
4
+ }
5
+ declare const Mermaid: import("svelte").Component<Props, {}, "">;
6
+ type Mermaid = ReturnType<typeof Mermaid>;
7
+ export default Mermaid;