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.
- package/LICENSE.MD +25 -4
- package/README.md +67 -58
- package/config/specra.config.schema.json +16 -0
- package/config/svelte-config.js +63 -0
- package/dist/api-parser.types.d.ts +59 -0
- package/dist/api-parser.types.js +5 -0
- package/dist/api.types.d.ts +137 -0
- package/dist/api.types.js +5 -0
- package/dist/category.d.ts +21 -0
- package/dist/category.js +48 -0
- package/dist/components/ConfigProvider.svelte +13 -0
- package/dist/components/ConfigProvider.svelte.d.ts +31 -0
- package/dist/components/docs/Accordion.svelte +18 -0
- package/dist/components/docs/Accordion.svelte.d.ts +10 -0
- package/dist/components/docs/AccordionItem.svelte +41 -0
- package/dist/components/docs/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/docs/Badge.svelte +28 -0
- package/dist/components/docs/Badge.svelte.d.ts +9 -0
- package/dist/components/docs/Breadcrumb.svelte +80 -0
- package/dist/components/docs/Breadcrumb.svelte.d.ts +8 -0
- package/dist/components/docs/Callout.svelte +96 -0
- package/dist/components/docs/Callout.svelte.d.ts +10 -0
- package/dist/components/docs/Card.svelte +63 -0
- package/dist/components/docs/Card.svelte.d.ts +12 -0
- package/dist/components/docs/CardGrid.svelte +24 -0
- package/dist/components/docs/CardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/CategoryIndex.svelte +110 -0
- package/dist/components/docs/CategoryIndex.svelte.d.ts +29 -0
- package/dist/components/docs/CodeBlock.svelte +172 -0
- package/dist/components/docs/CodeBlock.svelte.d.ts +8 -0
- package/dist/components/docs/Column.svelte +25 -0
- package/dist/components/docs/Column.svelte.d.ts +8 -0
- package/dist/components/docs/Columns.svelte +38 -0
- package/dist/components/docs/Columns.svelte.d.ts +13 -0
- package/dist/components/docs/DevModeBadge.svelte +15 -0
- package/dist/components/docs/DevModeBadge.svelte.d.ts +18 -0
- package/dist/components/docs/DocBadge.svelte +28 -0
- package/dist/components/docs/DocBadge.svelte.d.ts +9 -0
- package/dist/components/docs/DocLayout.svelte +107 -0
- package/dist/components/docs/DocLayout.svelte.d.ts +32 -0
- package/dist/components/docs/DocLoading.svelte +53 -0
- package/dist/components/docs/DocLoading.svelte.d.ts +18 -0
- package/dist/components/docs/DocMetadata.svelte +106 -0
- package/dist/components/docs/DocMetadata.svelte.d.ts +18 -0
- package/dist/components/docs/DocNavigation.svelte +56 -0
- package/dist/components/docs/DocNavigation.svelte.d.ts +12 -0
- package/dist/components/docs/DocTags.svelte +22 -0
- package/dist/components/docs/DocTags.svelte.d.ts +6 -0
- package/dist/components/docs/DraftBadge.svelte +10 -0
- package/dist/components/docs/DraftBadge.svelte.d.ts +18 -0
- package/dist/components/docs/Footer.svelte +72 -0
- package/dist/components/docs/Footer.svelte.d.ts +7 -0
- package/dist/components/docs/Frame.svelte +27 -0
- package/dist/components/docs/Frame.svelte.d.ts +9 -0
- package/dist/components/docs/Header.svelte +123 -0
- package/dist/components/docs/Header.svelte.d.ts +9 -0
- package/dist/components/docs/HeaderWithMenu.svelte +34 -0
- package/dist/components/docs/HeaderWithMenu.svelte.d.ts +17 -0
- package/dist/components/docs/HotReloadIndicator.svelte +44 -0
- package/dist/components/docs/HotReloadIndicator.svelte.d.ts +3 -0
- package/dist/components/docs/Icon.svelte +103 -0
- package/dist/components/docs/Icon.svelte.d.ts +11 -0
- package/dist/components/docs/Image.svelte +88 -0
- package/dist/components/docs/Image.svelte.d.ts +11 -0
- package/dist/components/docs/ImageCard.svelte +91 -0
- package/dist/components/docs/ImageCard.svelte.d.ts +12 -0
- package/dist/components/docs/ImageCardGrid.svelte +25 -0
- package/dist/components/docs/ImageCardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/LayoutProviders.svelte +57 -0
- package/dist/components/docs/LayoutProviders.svelte.d.ts +9 -0
- package/dist/components/docs/Logo.svelte +25 -0
- package/dist/components/docs/Logo.svelte.d.ts +11 -0
- package/dist/components/docs/Math.svelte +54 -0
- package/dist/components/docs/Math.svelte.d.ts +7 -0
- package/dist/components/docs/MdxContent.svelte +41 -0
- package/dist/components/docs/MdxHotReload.svelte +78 -0
- package/dist/components/docs/MdxHotReload.svelte.d.ts +9 -0
- package/dist/components/docs/MdxLayout.svelte +16 -0
- package/dist/components/docs/MdxLayout.svelte.d.ts +6 -0
- package/dist/components/docs/Mermaid.svelte +88 -0
- package/dist/components/docs/Mermaid.svelte.d.ts +7 -0
- package/dist/components/docs/MobileDocLayout.svelte +211 -0
- package/dist/components/docs/MobileDocLayout.svelte.d.ts +35 -0
- package/dist/components/docs/MobileSidebar.svelte +122 -0
- package/dist/components/docs/MobileSidebar.svelte.d.ts +31 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte +122 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte.d.ts +32 -0
- package/dist/components/docs/NotFoundContent.svelte +40 -0
- package/dist/components/docs/NotFoundContent.svelte.d.ts +6 -0
- package/dist/components/docs/SearchHighlight.svelte +116 -0
- package/dist/components/docs/SearchHighlight.svelte.d.ts +3 -0
- package/dist/components/docs/SearchModal.svelte +239 -0
- package/dist/components/docs/SearchModal.svelte.d.ts +9 -0
- package/dist/components/docs/Sidebar.svelte +69 -0
- package/dist/components/docs/Sidebar.svelte.d.ts +31 -0
- package/dist/components/docs/SidebarMenuItems.svelte +344 -0
- package/dist/components/docs/SidebarMenuItems.svelte.d.ts +33 -0
- package/dist/components/docs/SidebarSkeleton.svelte +50 -0
- package/dist/components/docs/SidebarSkeleton.svelte.d.ts +18 -0
- package/dist/components/docs/SiteBanner.svelte +92 -0
- package/dist/components/docs/SiteBanner.svelte.d.ts +7 -0
- package/dist/components/docs/Step.svelte +44 -0
- package/dist/components/docs/Step.svelte.d.ts +8 -0
- package/dist/components/docs/Steps.svelte +15 -0
- package/dist/components/docs/Steps.svelte.d.ts +7 -0
- package/dist/components/docs/Tab.svelte +40 -0
- package/dist/components/docs/Tab.svelte.d.ts +8 -0
- package/dist/components/docs/TabGroups.svelte +183 -0
- package/dist/components/docs/TabGroups.svelte.d.ts +25 -0
- package/dist/components/docs/TableOfContents.svelte +100 -0
- package/dist/components/docs/TableOfContents.svelte.d.ts +9 -0
- package/dist/components/docs/Tabs.svelte +69 -0
- package/dist/components/docs/Tabs.svelte.d.ts +8 -0
- package/dist/components/docs/ThemeToggle.svelte +16 -0
- package/dist/components/docs/ThemeToggle.svelte.d.ts +18 -0
- package/dist/components/docs/Tooltip.svelte +44 -0
- package/dist/components/docs/Tooltip.svelte.d.ts +10 -0
- package/dist/components/docs/VersionSwitcher.svelte +95 -0
- package/dist/components/docs/VersionSwitcher.svelte.d.ts +7 -0
- package/dist/components/docs/Video.svelte +84 -0
- package/dist/components/docs/Video.svelte.d.ts +12 -0
- package/dist/components/docs/api/ApiEndpoint.svelte +61 -0
- package/dist/components/docs/api/ApiEndpoint.svelte.d.ts +11 -0
- package/dist/components/docs/api/ApiParams.svelte +80 -0
- package/dist/components/docs/api/ApiParams.svelte.d.ts +14 -0
- package/dist/components/docs/api/ApiPlayground.svelte +259 -0
- package/dist/components/docs/api/ApiPlayground.svelte.d.ts +16 -0
- package/dist/components/docs/api/ApiReference.svelte +278 -0
- package/dist/components/docs/api/ApiReference.svelte.d.ts +23 -0
- package/dist/components/docs/api/ApiResponse.svelte +66 -0
- package/dist/components/docs/api/ApiResponse.svelte.d.ts +9 -0
- package/dist/components/docs/api/index.d.ts +5 -0
- package/dist/components/docs/api/index.js +5 -0
- package/dist/components/docs/componentTextProps.d.ts +3 -0
- package/dist/components/docs/componentTextProps.js +61 -0
- package/dist/components/docs/index.d.ts +54 -0
- package/dist/components/docs/index.js +56 -0
- package/dist/components/global/VersionNotFound.svelte +48 -0
- package/dist/components/global/VersionNotFound.svelte.d.ts +7 -0
- package/dist/components/global/index.d.ts +1 -0
- package/dist/components/global/index.js +1 -0
- package/dist/components/index.d.ts +6 -822
- package/dist/components/index.js +11 -3854
- package/dist/components/ui/Badge.svelte +48 -0
- package/dist/components/ui/Badge.svelte.d.ts +15 -0
- package/dist/components/ui/Button.svelte +58 -0
- package/dist/components/ui/Button.svelte.d.ts +17 -0
- package/dist/components/ui/Dialog.svelte +16 -0
- package/dist/components/ui/Dialog.svelte.d.ts +9 -0
- package/dist/components/ui/DialogClose.svelte +16 -0
- package/dist/components/ui/DialogClose.svelte.d.ts +9 -0
- package/dist/components/ui/DialogContent.svelte +43 -0
- package/dist/components/ui/DialogContent.svelte.d.ts +10 -0
- package/dist/components/ui/DialogDescription.svelte +21 -0
- package/dist/components/ui/DialogDescription.svelte.d.ts +9 -0
- package/dist/components/ui/DialogFooter.svelte +20 -0
- package/dist/components/ui/DialogFooter.svelte.d.ts +9 -0
- package/dist/components/ui/DialogHeader.svelte +20 -0
- package/dist/components/ui/DialogHeader.svelte.d.ts +9 -0
- package/dist/components/ui/DialogTitle.svelte +21 -0
- package/dist/components/ui/DialogTitle.svelte.d.ts +9 -0
- package/dist/components/ui/Input.svelte +23 -0
- package/dist/components/ui/Input.svelte.d.ts +8 -0
- package/dist/components/ui/Textarea.svelte +19 -0
- package/dist/components/ui/Textarea.svelte.d.ts +7 -0
- package/dist/components/ui/index.d.ts +11 -0
- package/dist/components/ui/index.js +11 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +9 -0
- package/dist/config.schema.json +471 -0
- package/dist/config.server.d.ts +46 -0
- package/dist/config.server.js +149 -0
- package/dist/{mdx-ColN3Cyg.d.mts → config.types.d.ts} +22 -75
- package/dist/config.types.js +39 -0
- package/dist/dev-utils.d.ts +29 -0
- package/dist/dev-utils.js +63 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +25 -4861
- package/dist/mdx-cache.d.ts +41 -0
- package/dist/mdx-cache.js +160 -0
- package/dist/mdx-components.js +50 -1931
- package/dist/mdx-security.d.ts +76 -0
- package/dist/mdx-security.js +217 -0
- package/dist/mdx.d.ts +73 -0
- package/dist/mdx.js +1099 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/security.d.ts +22 -47
- package/dist/middleware/security.js +111 -137
- package/dist/parsers/base-parser.d.ts +14 -0
- package/dist/parsers/base-parser.js +1 -0
- package/dist/parsers/index.d.ts +16 -0
- package/dist/parsers/index.js +51 -0
- package/dist/parsers/openapi-parser.d.ts +18 -0
- package/dist/parsers/openapi-parser.js +209 -0
- package/dist/parsers/postman-parser.d.ts +20 -0
- package/dist/parsers/postman-parser.js +260 -0
- package/dist/parsers/specra-parser.d.ts +10 -0
- package/dist/parsers/specra-parser.js +18 -0
- package/dist/redirects.d.ts +12 -0
- package/dist/redirects.js +30 -0
- package/dist/remark-code-meta.d.ts +6 -0
- package/dist/remark-code-meta.js +21 -0
- package/dist/sidebar-utils.d.ts +59 -0
- package/dist/sidebar-utils.js +144 -0
- package/dist/stores/config.d.ts +20 -0
- package/dist/stores/config.js +45 -0
- package/dist/stores/index.d.ts +4 -0
- package/dist/stores/index.js +4 -0
- package/dist/stores/sidebar.d.ts +7 -0
- package/dist/stores/sidebar.js +12 -0
- package/dist/stores/tabs.d.ts +6 -0
- package/dist/stores/tabs.js +41 -0
- package/dist/stores/theme.d.ts +7 -0
- package/dist/stores/theme.js +75 -0
- package/dist/{styles.css → styles/globals.css} +136 -6
- package/dist/toc.d.ts +9 -0
- package/dist/toc.js +15 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +30 -0
- package/package.json +47 -90
- package/dist/app/api/mdx-watch/route.d.mts +0 -10
- package/dist/app/api/mdx-watch/route.d.ts +0 -10
- package/dist/app/api/mdx-watch/route.js +0 -118
- package/dist/app/api/mdx-watch/route.js.map +0 -1
- package/dist/app/api/mdx-watch/route.mjs +0 -91
- package/dist/app/api/mdx-watch/route.mjs.map +0 -1
- package/dist/chunk-6S3EJVEO.mjs +0 -259
- package/dist/chunk-6S3EJVEO.mjs.map +0 -1
- package/dist/chunk-BE7EROIW.mjs +0 -212
- package/dist/chunk-BE7EROIW.mjs.map +0 -1
- package/dist/chunk-CWHRZHZO.mjs +0 -168
- package/dist/chunk-CWHRZHZO.mjs.map +0 -1
- package/dist/chunk-D5VDVYFY.mjs +0 -1325
- package/dist/chunk-D5VDVYFY.mjs.map +0 -1
- package/dist/chunk-WMCO2UX5.mjs +0 -585
- package/dist/chunk-WMCO2UX5.mjs.map +0 -1
- package/dist/chunk-XEMGCPZZ.mjs +0 -475
- package/dist/chunk-XEMGCPZZ.mjs.map +0 -1
- package/dist/components/index.d.mts +0 -822
- package/dist/components/index.js.map +0 -1
- package/dist/components/index.mjs +0 -3741
- package/dist/components/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -4
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1897
- package/dist/index.mjs.map +0 -1
- package/dist/layouts/index.d.mts +0 -34
- package/dist/layouts/index.d.ts +0 -34
- package/dist/layouts/index.js +0 -453
- package/dist/layouts/index.js.map +0 -1
- package/dist/layouts/index.mjs +0 -173
- package/dist/layouts/index.mjs.map +0 -1
- package/dist/lib/index.d.mts +0 -583
- package/dist/lib/index.d.ts +0 -583
- package/dist/lib/index.js +0 -1595
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/index.mjs +0 -111
- package/dist/lib/index.mjs.map +0 -1
- package/dist/mdx-ColN3Cyg.d.ts +0 -352
- package/dist/mdx-components.d.mts +0 -86
- package/dist/mdx-components.d.ts +0 -86
- package/dist/mdx-components.js.map +0 -1
- package/dist/mdx-components.mjs +0 -206
- package/dist/mdx-components.mjs.map +0 -1
- package/dist/middleware/security.d.mts +0 -82
- package/dist/middleware/security.js.map +0 -1
- package/dist/middleware/security.mjs +0 -84
- package/dist/middleware/security.mjs.map +0 -1
- package/dist/styles.css.map +0 -1
- package/dist/styles.d.mts +0 -2
- package/dist/styles.d.ts +0 -2
- package/dist/styles.js +0 -2
- package/dist/styles.js.map +0 -1
- package/dist/styles.mjs +0 -1
- 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,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,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,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}
|