svelte-docsmith 0.2.0 → 0.3.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/README.md +57 -25
- package/dist/clipboard.svelte.d.ts +1 -1
- package/dist/clipboard.svelte.js +15 -8
- package/dist/config.d.ts +36 -6
- package/dist/config.js +33 -7
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -0
- package/dist/markdown-layout.svelte +14 -1
- package/dist/markdown-layout.svelte.d.ts +2 -0
- package/dist/preprocess.d.ts +4 -5
- package/dist/preprocess.js +4 -5
- package/dist/search/context.svelte.d.ts +7 -0
- package/dist/search/context.svelte.js +20 -0
- package/dist/search/create-search.d.ts +14 -0
- package/dist/search/create-search.js +56 -0
- package/dist/search/snippet.d.ts +7 -0
- package/dist/search/snippet.js +43 -0
- package/dist/search.d.ts +10 -0
- package/dist/search.js +4 -0
- package/dist/theme.css +25 -0
- package/dist/types.d.ts +0 -6
- package/dist/ui/badge.svelte +10 -1
- package/dist/ui/badge.svelte.d.ts +2 -0
- package/dist/ui/callout.svelte +9 -9
- package/dist/ui/callout.svelte.d.ts +3 -3
- package/dist/ui/layouts/docs-header.svelte +5 -11
- package/dist/ui/layouts/docs-header.svelte.d.ts +0 -6
- package/dist/ui/layouts/docs-mobile-header.svelte +40 -8
- package/dist/ui/layouts/docs-mobile-header.svelte.d.ts +5 -4
- package/dist/ui/layouts/docs-shell.svelte +49 -5
- package/dist/ui/layouts/docs-shell.svelte.d.ts +16 -1
- package/dist/ui/layouts/error-page.svelte +82 -0
- package/dist/ui/layouts/error-page.svelte.d.ts +25 -0
- package/dist/ui/layouts/seo-head.svelte +65 -0
- package/dist/ui/layouts/seo-head.svelte.d.ts +11 -0
- package/dist/ui/markdown/h2.svelte +7 -3
- package/dist/ui/markdown/h3.svelte +7 -3
- package/dist/ui/markdown/pre.svelte +1 -1
- package/dist/ui/prop.svelte +71 -36
- package/dist/ui/prop.svelte.d.ts +1 -1
- package/dist/ui/props-table.svelte +10 -62
- package/dist/ui/search-trigger.svelte +31 -0
- package/dist/ui/search-trigger.svelte.d.ts +6 -0
- package/dist/ui/search.svelte +167 -0
- package/dist/ui/search.svelte.d.ts +13 -0
- package/dist/ui/shadcn/accordion/accordion.svelte.d.ts +1 -1
- package/dist/ui/shadcn/command/command-dialog.svelte +42 -0
- package/dist/ui/shadcn/command/command-dialog.svelte.d.ts +14 -0
- package/dist/ui/shadcn/command/command-empty.svelte +17 -0
- package/dist/ui/shadcn/command/command-empty.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-group.svelte +33 -0
- package/dist/ui/shadcn/command/command-group.svelte.d.ts +7 -0
- package/dist/ui/shadcn/command/command-input.svelte +36 -0
- package/dist/ui/shadcn/command/command-input.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-item.svelte +27 -0
- package/dist/ui/shadcn/command/command-item.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-link-item.svelte +20 -0
- package/dist/ui/shadcn/command/command-link-item.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-list.svelte +20 -0
- package/dist/ui/shadcn/command/command-list.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-loading.svelte +7 -0
- package/dist/ui/shadcn/command/command-loading.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-separator.svelte +17 -0
- package/dist/ui/shadcn/command/command-separator.svelte.d.ts +4 -0
- package/dist/ui/shadcn/command/command-shortcut.svelte +23 -0
- package/dist/ui/shadcn/command/command-shortcut.svelte.d.ts +5 -0
- package/dist/ui/shadcn/command/command.svelte +28 -0
- package/dist/ui/shadcn/command/command.svelte.d.ts +8 -0
- package/dist/ui/shadcn/command/index.d.ts +12 -0
- package/dist/ui/shadcn/command/index.js +14 -0
- package/dist/ui/shadcn/dialog/dialog-close.svelte +11 -0
- package/dist/ui/shadcn/dialog/dialog-close.svelte.d.ts +4 -0
- package/dist/ui/shadcn/dialog/dialog-content.svelte +48 -0
- package/dist/ui/shadcn/dialog/dialog-content.svelte.d.ts +13 -0
- package/dist/ui/shadcn/dialog/dialog-description.svelte +20 -0
- package/dist/ui/shadcn/dialog/dialog-description.svelte.d.ts +4 -0
- package/dist/ui/shadcn/dialog/dialog-footer.svelte +32 -0
- package/dist/ui/shadcn/dialog/dialog-footer.svelte.d.ts +8 -0
- package/dist/ui/shadcn/dialog/dialog-header.svelte +20 -0
- package/dist/ui/shadcn/dialog/dialog-header.svelte.d.ts +5 -0
- package/dist/ui/shadcn/dialog/dialog-overlay.svelte +20 -0
- package/dist/ui/shadcn/dialog/dialog-overlay.svelte.d.ts +4 -0
- package/dist/ui/shadcn/dialog/dialog-portal.svelte +7 -0
- package/dist/ui/shadcn/dialog/dialog-portal.svelte.d.ts +3 -0
- package/dist/ui/shadcn/dialog/dialog-title.svelte +17 -0
- package/dist/ui/shadcn/dialog/dialog-title.svelte.d.ts +4 -0
- package/dist/ui/shadcn/dialog/dialog-trigger.svelte +11 -0
- package/dist/ui/shadcn/dialog/dialog-trigger.svelte.d.ts +4 -0
- package/dist/ui/shadcn/dialog/dialog.svelte +7 -0
- package/dist/ui/shadcn/dialog/dialog.svelte.d.ts +3 -0
- package/dist/ui/shadcn/dialog/index.d.ts +11 -0
- package/dist/ui/shadcn/dialog/index.js +13 -0
- package/dist/ui/shadcn/input/index.d.ts +2 -0
- package/dist/ui/shadcn/input/index.js +4 -0
- package/dist/ui/shadcn/input/input.svelte +48 -0
- package/dist/ui/shadcn/input/input.svelte.d.ts +13 -0
- package/dist/ui/shadcn/input-group/index.d.ts +7 -0
- package/dist/ui/shadcn/input-group/index.js +9 -0
- package/dist/ui/shadcn/input-group/input-group-addon.svelte +53 -0
- package/dist/ui/shadcn/input-group/input-group-addon.svelte.d.ts +32 -0
- package/dist/ui/shadcn/input-group/input-group-button.svelte +49 -0
- package/dist/ui/shadcn/input-group/input-group-button.svelte.d.ts +32 -0
- package/dist/ui/shadcn/input-group/input-group-input.svelte +23 -0
- package/dist/ui/shadcn/input-group/input-group-input.svelte.d.ts +11 -0
- package/dist/ui/shadcn/input-group/input-group-text.svelte +22 -0
- package/dist/ui/shadcn/input-group/input-group-text.svelte.d.ts +5 -0
- package/dist/ui/shadcn/input-group/input-group-textarea.svelte +23 -0
- package/dist/ui/shadcn/input-group/input-group-textarea.svelte.d.ts +3 -0
- package/dist/ui/shadcn/input-group/input-group.svelte +24 -0
- package/dist/ui/shadcn/input-group/input-group.svelte.d.ts +5 -0
- package/dist/ui/shadcn/tabs/tabs.svelte.d.ts +1 -1
- package/dist/ui/shadcn/textarea/index.d.ts +2 -0
- package/dist/ui/shadcn/textarea/index.js +4 -0
- package/dist/ui/shadcn/textarea/textarea.svelte +23 -0
- package/dist/ui/shadcn/textarea/textarea.svelte.d.ts +5 -0
- package/dist/ui/tab-item.svelte +35 -9
- package/dist/ui/tab-item.svelte.d.ts +9 -4
- package/dist/ui/tabs-phase.svelte +19 -0
- package/dist/ui/tabs-phase.svelte.d.ts +9 -0
- package/dist/ui/tabs.svelte +47 -24
- package/dist/ui/tabs.svelte.d.ts +16 -6
- package/dist/vite.d.ts +8 -6
- package/dist/vite.js +140 -47
- package/package.json +8 -2
package/dist/ui/badge.svelte
CHANGED
|
@@ -19,12 +19,15 @@
|
|
|
19
19
|
const {
|
|
20
20
|
variant = 'default',
|
|
21
21
|
href,
|
|
22
|
+
external = false,
|
|
22
23
|
children
|
|
23
24
|
}: {
|
|
24
25
|
/** Visual intent. Default: `default`. */
|
|
25
26
|
variant?: BadgeVariant;
|
|
26
27
|
/** Turns the badge into a link. */
|
|
27
28
|
href?: string;
|
|
29
|
+
/** For a linked badge, open in a new tab with `rel="noopener"`. */
|
|
30
|
+
external?: boolean;
|
|
28
31
|
children: Snippet;
|
|
29
32
|
} = $props();
|
|
30
33
|
|
|
@@ -49,6 +52,12 @@
|
|
|
49
52
|
};
|
|
50
53
|
</script>
|
|
51
54
|
|
|
52
|
-
<ShadcnBadge
|
|
55
|
+
<ShadcnBadge
|
|
56
|
+
variant={base[variant]}
|
|
57
|
+
{href}
|
|
58
|
+
target={external ? '_blank' : undefined}
|
|
59
|
+
rel={external ? 'noopener noreferrer' : undefined}
|
|
60
|
+
class={extra[variant]}
|
|
61
|
+
>
|
|
53
62
|
{@render children()}
|
|
54
63
|
</ShadcnBadge>
|
|
@@ -5,6 +5,8 @@ type $$ComponentProps = {
|
|
|
5
5
|
variant?: BadgeVariant;
|
|
6
6
|
/** Turns the badge into a link. */
|
|
7
7
|
href?: string;
|
|
8
|
+
/** For a linked badge, open in a new tab with `rel="noopener"`. */
|
|
9
|
+
external?: boolean;
|
|
8
10
|
children: Snippet;
|
|
9
11
|
};
|
|
10
12
|
declare const Badge: import("svelte").Component<$$ComponentProps, {}, "">;
|
package/dist/ui/callout.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
export type
|
|
2
|
+
export type CalloutVariant = 'note' | 'tip' | 'warning' | 'danger';
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
@@ -11,32 +11,32 @@
|
|
|
11
11
|
import type { Snippet } from 'svelte';
|
|
12
12
|
|
|
13
13
|
const {
|
|
14
|
-
|
|
14
|
+
variant = 'note',
|
|
15
15
|
title,
|
|
16
16
|
children
|
|
17
17
|
}: {
|
|
18
18
|
/** Visual intent. Default: `note`. */
|
|
19
|
-
|
|
20
|
-
/** Optional heading; defaults to the capitalized
|
|
19
|
+
variant?: CalloutVariant;
|
|
20
|
+
/** Optional heading; defaults to the capitalized variant. */
|
|
21
21
|
title?: string;
|
|
22
22
|
children: Snippet;
|
|
23
23
|
} = $props();
|
|
24
24
|
|
|
25
|
-
const variants: Record<
|
|
25
|
+
const variants: Record<CalloutVariant, { icon: Component; label: string; klass: string }> = {
|
|
26
26
|
note: { icon: Info, label: 'Note', klass: 'callout-note' },
|
|
27
27
|
tip: { icon: Lightbulb, label: 'Tip', klass: 'callout-tip' },
|
|
28
28
|
warning: { icon: TriangleAlert, label: 'Warning', klass: 'callout-warning' },
|
|
29
29
|
danger: { icon: CircleAlert, label: 'Danger', klass: 'callout-danger' }
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const Icon = $derived(
|
|
32
|
+
const resolved = $derived(variants[variant]);
|
|
33
|
+
const Icon = $derived(resolved.icon);
|
|
34
34
|
</script>
|
|
35
35
|
|
|
36
|
-
<div role="note" class="not-prose callout {
|
|
36
|
+
<div role="note" class="not-prose callout {resolved.klass}">
|
|
37
37
|
<Icon class="callout-icon" size={18} aria-hidden="true" />
|
|
38
38
|
<div class="callout-body">
|
|
39
|
-
<p class="callout-title">{title ??
|
|
39
|
+
<p class="callout-title">{title ?? resolved.label}</p>
|
|
40
40
|
<div class="callout-content">{@render children()}</div>
|
|
41
41
|
</div>
|
|
42
42
|
</div>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type CalloutVariant = 'note' | 'tip' | 'warning' | 'danger';
|
|
2
2
|
import type { Component } from 'svelte';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
type $$ComponentProps = {
|
|
5
5
|
/** Visual intent. Default: `note`. */
|
|
6
|
-
|
|
7
|
-
/** Optional heading; defaults to the capitalized
|
|
6
|
+
variant?: CalloutVariant;
|
|
7
|
+
/** Optional heading; defaults to the capitalized variant. */
|
|
8
8
|
title?: string;
|
|
9
9
|
children: Snippet;
|
|
10
10
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Button } from '../shadcn/button/index.js';
|
|
4
4
|
import { Separator } from '../shadcn/separator/index.js';
|
|
5
5
|
import GithubIcon from '../icons/github.svelte';
|
|
6
|
+
import SearchTrigger from '../search-trigger.svelte';
|
|
6
7
|
import ThemeToggle from '../theme-toggle.svelte';
|
|
7
8
|
import type { DocsmithConfig } from '../../config.js';
|
|
8
9
|
import BookOpenText from '@lucide/svelte/icons/book-open-text';
|
|
@@ -11,20 +12,13 @@
|
|
|
11
12
|
const {
|
|
12
13
|
config,
|
|
13
14
|
logo,
|
|
14
|
-
actions
|
|
15
|
-
standalone = false
|
|
15
|
+
actions
|
|
16
16
|
}: {
|
|
17
17
|
config: DocsmithConfig;
|
|
18
18
|
/** Custom logo mark; defaults to a book icon in a primary-tinted chip. */
|
|
19
19
|
logo?: Snippet;
|
|
20
20
|
/** Extra header controls, rendered before the theme toggle. */
|
|
21
21
|
actions?: Snippet;
|
|
22
|
-
/**
|
|
23
|
-
* Show at every breakpoint (for pages without the docs mobile header, e.g.
|
|
24
|
-
* a landing page). Default: desktop-only, since the docs shell pairs this
|
|
25
|
-
* with a separate mobile header.
|
|
26
|
-
*/
|
|
27
|
-
standalone?: boolean;
|
|
28
22
|
} = $props();
|
|
29
23
|
|
|
30
24
|
let isScrolled = $state(false);
|
|
@@ -33,9 +27,7 @@
|
|
|
33
27
|
<svelte:window onscroll={() => (isScrolled = window.scrollY > 50)} />
|
|
34
28
|
|
|
35
29
|
<header
|
|
36
|
-
class="sticky top-0 z-40 w-full transition-all duration-200 {
|
|
37
|
-
? 'block'
|
|
38
|
-
: 'hidden lg:block'} {isScrolled
|
|
30
|
+
class="sticky top-0 z-40 hidden w-full transition-all duration-200 lg:block {isScrolled
|
|
39
31
|
? 'bg-background/60 supports-backdrop-filter:bg-background/60 border-border/40 border-b backdrop-blur-xl'
|
|
40
32
|
: 'border-transparent bg-transparent'}"
|
|
41
33
|
>
|
|
@@ -82,6 +74,8 @@
|
|
|
82
74
|
</div>
|
|
83
75
|
{/if}
|
|
84
76
|
|
|
77
|
+
<SearchTrigger />
|
|
78
|
+
|
|
85
79
|
<Separator orientation="vertical" class="bg-border/40 mx-1 min-h-0 h-6" />
|
|
86
80
|
|
|
87
81
|
<div class="flex items-center gap-0.5">
|
|
@@ -6,12 +6,6 @@ type $$ComponentProps = {
|
|
|
6
6
|
logo?: Snippet;
|
|
7
7
|
/** Extra header controls, rendered before the theme toggle. */
|
|
8
8
|
actions?: Snippet;
|
|
9
|
-
/**
|
|
10
|
-
* Show at every breakpoint (for pages without the docs mobile header, e.g.
|
|
11
|
-
* a landing page). Default: desktop-only, since the docs shell pairs this
|
|
12
|
-
* with a separate mobile header.
|
|
13
|
-
*/
|
|
14
|
-
standalone?: boolean;
|
|
15
9
|
};
|
|
16
10
|
declare const DocsHeader: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
11
|
type DocsHeader = ReturnType<typeof DocsHeader>;
|
|
@@ -8,33 +8,39 @@
|
|
|
8
8
|
import { Button } from '../shadcn/button/index.js';
|
|
9
9
|
import * as Popover from '../shadcn/popover/index.js';
|
|
10
10
|
import { ScrollArea } from '../shadcn/scroll-area/index.js';
|
|
11
|
+
import { Separator } from '../shadcn/separator/index.js';
|
|
11
12
|
import * as Sheet from '../shadcn/sheet/index.js';
|
|
13
|
+
import { useSearch } from '../../search/context.svelte.js';
|
|
12
14
|
import BookOpenText from '@lucide/svelte/icons/book-open-text';
|
|
13
15
|
import Menu from '@lucide/svelte/icons/menu';
|
|
14
16
|
import PanelRight from '@lucide/svelte/icons/panel-right';
|
|
17
|
+
import SearchIcon from '@lucide/svelte/icons/search';
|
|
15
18
|
import type { Snippet } from 'svelte';
|
|
16
19
|
|
|
17
20
|
const {
|
|
18
21
|
config,
|
|
19
|
-
nav,
|
|
20
|
-
title,
|
|
21
|
-
tocItems,
|
|
22
|
+
nav = [],
|
|
23
|
+
title = config.title,
|
|
24
|
+
tocItems = [],
|
|
22
25
|
tocActiveId = null,
|
|
23
26
|
logo,
|
|
24
27
|
actions
|
|
25
28
|
}: {
|
|
26
29
|
config: DocsmithConfig;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
title
|
|
30
|
+
/** Sidebar groups; omit on non-doc pages (the landing/`page` layout). */
|
|
31
|
+
nav?: NavGroup[];
|
|
32
|
+
/** Title shown between the menu and controls. Defaults to the site title. */
|
|
33
|
+
title?: string;
|
|
30
34
|
/** In-page TOC entries; the TOC popover is hidden when empty. */
|
|
31
|
-
tocItems
|
|
35
|
+
tocItems?: TocItem[];
|
|
32
36
|
/** Id of the heading currently in view. */
|
|
33
37
|
tocActiveId?: string | null;
|
|
34
38
|
logo?: Snippet;
|
|
35
39
|
actions?: Snippet;
|
|
36
40
|
} = $props();
|
|
37
41
|
|
|
42
|
+
const search = useSearch();
|
|
43
|
+
|
|
38
44
|
let isMenuOpen = $state(false);
|
|
39
45
|
let isTocOpen = $state(false);
|
|
40
46
|
|
|
@@ -72,7 +78,26 @@
|
|
|
72
78
|
</a>
|
|
73
79
|
</div>
|
|
74
80
|
<ScrollArea class="my-4 h-[calc(100vh-8rem)] pb-10 pl-2">
|
|
75
|
-
|
|
81
|
+
{#if config.nav?.length}
|
|
82
|
+
<nav class="flex flex-col gap-0.5 px-5 pb-1">
|
|
83
|
+
{#each config.nav as link (link.href)}
|
|
84
|
+
<a
|
|
85
|
+
href={link.href}
|
|
86
|
+
target={link.external ? '_blank' : undefined}
|
|
87
|
+
rel={link.external ? 'noopener noreferrer' : undefined}
|
|
88
|
+
class="text-muted-foreground hover:text-foreground rounded-md py-1.5 text-sm font-medium transition-colors"
|
|
89
|
+
>
|
|
90
|
+
{link.label}
|
|
91
|
+
</a>
|
|
92
|
+
{/each}
|
|
93
|
+
</nav>
|
|
94
|
+
{#if nav.length}
|
|
95
|
+
<Separator class="bg-border/60 mx-5 my-2" />
|
|
96
|
+
{/if}
|
|
97
|
+
{/if}
|
|
98
|
+
{#if nav.length}
|
|
99
|
+
<DocsSidebar {nav} class="static block h-auto w-full" />
|
|
100
|
+
{/if}
|
|
76
101
|
</ScrollArea>
|
|
77
102
|
</Sheet.Content>
|
|
78
103
|
</Sheet.Root>
|
|
@@ -84,6 +109,13 @@
|
|
|
84
109
|
{@render actions()}
|
|
85
110
|
{/if}
|
|
86
111
|
|
|
112
|
+
{#if search}
|
|
113
|
+
<Button variant="ghost" size="icon" class="size-8" onclick={() => (search.open = true)}>
|
|
114
|
+
<SearchIcon class="size-4" />
|
|
115
|
+
<span class="sr-only">Search documentation</span>
|
|
116
|
+
</Button>
|
|
117
|
+
{/if}
|
|
118
|
+
|
|
87
119
|
<ThemeToggle />
|
|
88
120
|
|
|
89
121
|
{#if tocItems.length > 0}
|
|
@@ -3,11 +3,12 @@ import type { TocItem } from '../../toc/index.js';
|
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
type $$ComponentProps = {
|
|
5
5
|
config: DocsmithConfig;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
title
|
|
6
|
+
/** Sidebar groups; omit on non-doc pages (the landing/`page` layout). */
|
|
7
|
+
nav?: NavGroup[];
|
|
8
|
+
/** Title shown between the menu and controls. Defaults to the site title. */
|
|
9
|
+
title?: string;
|
|
9
10
|
/** In-page TOC entries; the TOC popover is hidden when empty. */
|
|
10
|
-
tocItems
|
|
11
|
+
tocItems?: TocItem[];
|
|
11
12
|
/** Id of the heading currently in view. */
|
|
12
13
|
tocActiveId?: string | null;
|
|
13
14
|
logo?: Snippet;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { page } from '$app/state';
|
|
3
3
|
import { afterNavigate } from '$app/navigation';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
navFromContent,
|
|
6
|
+
type DocsContentItem,
|
|
7
|
+
type DocsmithConfig,
|
|
8
|
+
type SearchDoc
|
|
9
|
+
} from '../../config.js';
|
|
5
10
|
import { createToc } from '../../toc/index.js';
|
|
11
|
+
import { createSearchState } from '../../search/context.svelte.js';
|
|
12
|
+
import Search from '../search.svelte';
|
|
6
13
|
import BackgroundPattern from '../background-pattern.svelte';
|
|
7
14
|
import ThemeProvider from '../theme-provider.svelte';
|
|
8
15
|
import DocsHeader from './docs-header.svelte';
|
|
@@ -11,6 +18,7 @@
|
|
|
11
18
|
import DocsSidebar from './docs-sidebar.svelte';
|
|
12
19
|
import PrevNextNav from './prev-next-nav.svelte';
|
|
13
20
|
import Breadcrumbs, { type Crumb } from './breadcrumbs.svelte';
|
|
21
|
+
import SeoHead from './seo-head.svelte';
|
|
14
22
|
import TableOfContents from '../table-of-contents.svelte';
|
|
15
23
|
import type { Snippet } from 'svelte';
|
|
16
24
|
|
|
@@ -21,12 +29,26 @@
|
|
|
21
29
|
logo,
|
|
22
30
|
actions,
|
|
23
31
|
footer,
|
|
32
|
+
search,
|
|
33
|
+
seo,
|
|
24
34
|
pattern = false,
|
|
25
35
|
layout = 'docs'
|
|
26
36
|
}: {
|
|
27
37
|
config: DocsmithConfig;
|
|
28
38
|
content?: DocsContentItem[];
|
|
29
39
|
children: Snippet;
|
|
40
|
+
/**
|
|
41
|
+
* Override the head tags for this page. Doc pages get their `<title>` and
|
|
42
|
+
* description from frontmatter automatically; use this on non-doc pages
|
|
43
|
+
* (the landing page, custom routes) or to override.
|
|
44
|
+
*/
|
|
45
|
+
seo?: { title?: string; description?: string };
|
|
46
|
+
/**
|
|
47
|
+
* Enable the ⌘K search palette by lazily providing the generated index,
|
|
48
|
+
* e.g. `search={() => import('svelte-docsmith/search').then((m) => m.docs)}`.
|
|
49
|
+
* Omit to hide search. The index is fetched only when search first opens.
|
|
50
|
+
*/
|
|
51
|
+
search?: () => Promise<SearchDoc[]>;
|
|
30
52
|
/** Custom logo mark for the header and mobile menu. */
|
|
31
53
|
logo?: Snippet;
|
|
32
54
|
/** Extra header controls (desktop and mobile), before the theme toggle. */
|
|
@@ -44,8 +66,18 @@
|
|
|
44
66
|
layout?: 'docs' | 'page';
|
|
45
67
|
} = $props();
|
|
46
68
|
|
|
69
|
+
// Publish search state on context so the header triggers and the single
|
|
70
|
+
// dialog share one open-state; only when the consumer wired an index. The
|
|
71
|
+
// `search` loader is static for the shell's lifetime, so reading it once at
|
|
72
|
+
// init is intentional.
|
|
73
|
+
// svelte-ignore state_referenced_locally
|
|
74
|
+
if (search) createSearchState();
|
|
75
|
+
|
|
47
76
|
const nav = $derived(navFromContent(content));
|
|
48
77
|
|
|
78
|
+
// The content entry for the current route drives the SEO title/description.
|
|
79
|
+
const currentEntry = $derived(content.find((item) => item.path === page.url.pathname));
|
|
80
|
+
|
|
49
81
|
// Ordered flat page list drives the prev/next links.
|
|
50
82
|
const flatNav = $derived(nav.flatMap((group) => group.items));
|
|
51
83
|
const pageIndex = $derived(flatNav.findIndex((item) => item.url === page.url.pathname));
|
|
@@ -81,28 +113,40 @@
|
|
|
81
113
|
const tocItems = $derived(toc.items.length ? toc.items : pageToc);
|
|
82
114
|
</script>
|
|
83
115
|
|
|
116
|
+
<SeoHead
|
|
117
|
+
{config}
|
|
118
|
+
title={seo?.title ?? currentEntry?.title}
|
|
119
|
+
description={seo?.description ?? currentEntry?.description}
|
|
120
|
+
/>
|
|
121
|
+
|
|
84
122
|
<div class="relative isolate flex min-h-screen flex-col">
|
|
85
123
|
<!-- Owns light/dark for the whole app — consumers never wire mode-watcher. -->
|
|
86
124
|
<ThemeProvider />
|
|
87
125
|
|
|
126
|
+
{#if search}
|
|
127
|
+
<Search load={search} />
|
|
128
|
+
{/if}
|
|
129
|
+
|
|
88
130
|
{#if pattern}
|
|
89
131
|
<BackgroundPattern />
|
|
90
132
|
{/if}
|
|
91
133
|
|
|
134
|
+
<!-- One header system everywhere: DocsHeader on desktop, DocsMobileHeader
|
|
135
|
+
below lg. The `page` layout just omits the sidebar nav and in-page TOC. -->
|
|
136
|
+
<DocsHeader {config} {logo} {actions} />
|
|
137
|
+
|
|
92
138
|
{#if layout === 'page'}
|
|
93
|
-
<
|
|
139
|
+
<DocsMobileHeader {config} {logo} {actions} />
|
|
94
140
|
|
|
95
141
|
<main class="flex-1">
|
|
96
142
|
{@render children()}
|
|
97
143
|
</main>
|
|
98
144
|
{:else}
|
|
99
|
-
<DocsHeader {config} {logo} {actions} />
|
|
100
|
-
|
|
101
145
|
<DocsMobileHeader
|
|
102
146
|
{config}
|
|
103
147
|
{nav}
|
|
104
148
|
title={currentTitle}
|
|
105
|
-
tocItems
|
|
149
|
+
{tocItems}
|
|
106
150
|
tocActiveId={toc.activeId}
|
|
107
151
|
{logo}
|
|
108
152
|
{actions}
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import { type DocsContentItem, type DocsmithConfig } from '../../config.js';
|
|
1
|
+
import { type DocsContentItem, type DocsmithConfig, type SearchDoc } from '../../config.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
config: DocsmithConfig;
|
|
5
5
|
content?: DocsContentItem[];
|
|
6
6
|
children: Snippet;
|
|
7
|
+
/**
|
|
8
|
+
* Override the head tags for this page. Doc pages get their `<title>` and
|
|
9
|
+
* description from frontmatter automatically; use this on non-doc pages
|
|
10
|
+
* (the landing page, custom routes) or to override.
|
|
11
|
+
*/
|
|
12
|
+
seo?: {
|
|
13
|
+
title?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Enable the ⌘K search palette by lazily providing the generated index,
|
|
18
|
+
* e.g. `search={() => import('svelte-docsmith/search').then((m) => m.docs)}`.
|
|
19
|
+
* Omit to hide search. The index is fetched only when search first opens.
|
|
20
|
+
*/
|
|
21
|
+
search?: () => Promise<SearchDoc[]>;
|
|
7
22
|
/** Custom logo mark for the header and mobile menu. */
|
|
8
23
|
logo?: Snippet;
|
|
9
24
|
/** Extra header controls (desktop and mobile), before the theme toggle. */
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/state';
|
|
3
|
+
import type { DocsContentItem, DocsmithConfig, SearchDoc } from '../../config.js';
|
|
4
|
+
import DocsShell from './docs-shell.svelte';
|
|
5
|
+
import ArrowLeft from '@lucide/svelte/icons/arrow-left';
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
config,
|
|
10
|
+
content = [],
|
|
11
|
+
search,
|
|
12
|
+
status,
|
|
13
|
+
title,
|
|
14
|
+
message,
|
|
15
|
+
home = '/',
|
|
16
|
+
homeLabel = 'Back to home',
|
|
17
|
+
pattern = true,
|
|
18
|
+
children
|
|
19
|
+
}: {
|
|
20
|
+
config: DocsmithConfig;
|
|
21
|
+
/** Content index, so the error page keeps the same header/footer as the site. */
|
|
22
|
+
content?: DocsContentItem[];
|
|
23
|
+
/** Enable the ⌘K search palette on the error page (same loader as DocsShell). */
|
|
24
|
+
search?: () => Promise<SearchDoc[]>;
|
|
25
|
+
/** HTTP status; defaults to the current `page.status`. */
|
|
26
|
+
status?: number;
|
|
27
|
+
/** Heading; defaults to a message keyed off the status. */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Body line; defaults to the error message, or a status-appropriate default. */
|
|
30
|
+
message?: string;
|
|
31
|
+
/** Where the primary action links. Default: the site root. */
|
|
32
|
+
home?: string;
|
|
33
|
+
homeLabel?: string;
|
|
34
|
+
/** Render the decorative page background. Default: `true`. */
|
|
35
|
+
pattern?: boolean;
|
|
36
|
+
/** Extra content below the action (e.g. a search prompt or links). */
|
|
37
|
+
children?: Snippet;
|
|
38
|
+
} = $props();
|
|
39
|
+
|
|
40
|
+
const resolvedStatus = $derived(status ?? page.status);
|
|
41
|
+
const isNotFound = $derived(resolvedStatus === 404);
|
|
42
|
+
const resolvedTitle = $derived(title ?? (isNotFound ? 'Page not found' : 'Something went wrong'));
|
|
43
|
+
const resolvedMessage = $derived(
|
|
44
|
+
message ??
|
|
45
|
+
page.error?.message ??
|
|
46
|
+
(isNotFound
|
|
47
|
+
? "The page you're looking for doesn't exist or has moved."
|
|
48
|
+
: 'An unexpected error occurred. Please try again.')
|
|
49
|
+
);
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<DocsShell {config} {content} {search} {pattern} layout="page" seo={{ title: resolvedTitle }}>
|
|
53
|
+
<section
|
|
54
|
+
class="mx-auto flex min-h-[60vh] max-w-2xl flex-col items-center justify-center px-4 py-20 text-center md:px-6"
|
|
55
|
+
>
|
|
56
|
+
<p class="text-primary font-mono text-sm font-semibold tracking-widest tabular-nums">
|
|
57
|
+
{resolvedStatus}
|
|
58
|
+
</p>
|
|
59
|
+
<h1 class="mt-4 text-4xl font-semibold tracking-tight text-balance sm:text-5xl">
|
|
60
|
+
{resolvedTitle}
|
|
61
|
+
</h1>
|
|
62
|
+
<p class="text-muted-foreground mt-4 text-lg leading-relaxed text-pretty">
|
|
63
|
+
{resolvedMessage}
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
|
67
|
+
<a
|
|
68
|
+
href={home}
|
|
69
|
+
class="group bg-primary text-primary-foreground inline-flex h-11 items-center gap-2 rounded-lg px-6 font-medium shadow-sm transition-all hover:bg-primary/90 hover:shadow-md"
|
|
70
|
+
>
|
|
71
|
+
<ArrowLeft class="size-4 transition-transform group-hover:-translate-x-0.5" />
|
|
72
|
+
{homeLabel}
|
|
73
|
+
</a>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{#if children}
|
|
77
|
+
<div class="mt-8">
|
|
78
|
+
{@render children()}
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
81
|
+
</section>
|
|
82
|
+
</DocsShell>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DocsContentItem, DocsmithConfig, SearchDoc } from '../../config.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
config: DocsmithConfig;
|
|
5
|
+
/** Content index, so the error page keeps the same header/footer as the site. */
|
|
6
|
+
content?: DocsContentItem[];
|
|
7
|
+
/** Enable the ⌘K search palette on the error page (same loader as DocsShell). */
|
|
8
|
+
search?: () => Promise<SearchDoc[]>;
|
|
9
|
+
/** HTTP status; defaults to the current `page.status`. */
|
|
10
|
+
status?: number;
|
|
11
|
+
/** Heading; defaults to a message keyed off the status. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Body line; defaults to the error message, or a status-appropriate default. */
|
|
14
|
+
message?: string;
|
|
15
|
+
/** Where the primary action links. Default: the site root. */
|
|
16
|
+
home?: string;
|
|
17
|
+
homeLabel?: string;
|
|
18
|
+
/** Render the decorative page background. Default: `true`. */
|
|
19
|
+
pattern?: boolean;
|
|
20
|
+
/** Extra content below the action (e.g. a search prompt or links). */
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
};
|
|
23
|
+
declare const ErrorPage: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
24
|
+
type ErrorPage = ReturnType<typeof ErrorPage>;
|
|
25
|
+
export default ErrorPage;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/state';
|
|
3
|
+
import type { DocsmithConfig } from '../../config.js';
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
config,
|
|
7
|
+
title,
|
|
8
|
+
description
|
|
9
|
+
}: {
|
|
10
|
+
config: DocsmithConfig;
|
|
11
|
+
/** The page's own title (frontmatter or override); omit for the site root. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** The page's own description; falls back to `config.description`. */
|
|
14
|
+
description?: string;
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
// "Page · Site", or just the site title when they would duplicate (the home page).
|
|
18
|
+
const fullTitle = $derived(
|
|
19
|
+
title && title !== config.title ? `${title} · ${config.title}` : config.title
|
|
20
|
+
);
|
|
21
|
+
const ogTitle = $derived(title ?? config.title);
|
|
22
|
+
const metaDescription = $derived(description ?? config.description);
|
|
23
|
+
|
|
24
|
+
const origin = $derived(config.url?.replace(/\/$/, ''));
|
|
25
|
+
// Absolute URLs need a configured origin; otherwise canonical/og:url are omitted.
|
|
26
|
+
const canonical = $derived(origin ? origin + page.url.pathname : undefined);
|
|
27
|
+
const image = $derived.by(() => {
|
|
28
|
+
const src = config.ogImage;
|
|
29
|
+
if (!src) return undefined;
|
|
30
|
+
if (/^https?:\/\//.test(src)) return src;
|
|
31
|
+
return origin ? origin + '/' + src.replace(/^\//, '') : src;
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<svelte:head>
|
|
36
|
+
<title>{fullTitle}</title>
|
|
37
|
+
{#if metaDescription}
|
|
38
|
+
<meta name="description" content={metaDescription} />
|
|
39
|
+
{/if}
|
|
40
|
+
{#if canonical}
|
|
41
|
+
<link rel="canonical" href={canonical} />
|
|
42
|
+
{/if}
|
|
43
|
+
|
|
44
|
+
<meta property="og:title" content={ogTitle} />
|
|
45
|
+
{#if metaDescription}
|
|
46
|
+
<meta property="og:description" content={metaDescription} />
|
|
47
|
+
{/if}
|
|
48
|
+
<meta property="og:type" content="website" />
|
|
49
|
+
<meta property="og:site_name" content={config.title} />
|
|
50
|
+
{#if canonical}
|
|
51
|
+
<meta property="og:url" content={canonical} />
|
|
52
|
+
{/if}
|
|
53
|
+
{#if image}
|
|
54
|
+
<meta property="og:image" content={image} />
|
|
55
|
+
{/if}
|
|
56
|
+
|
|
57
|
+
<meta name="twitter:card" content={image ? 'summary_large_image' : 'summary'} />
|
|
58
|
+
<meta name="twitter:title" content={ogTitle} />
|
|
59
|
+
{#if metaDescription}
|
|
60
|
+
<meta name="twitter:description" content={metaDescription} />
|
|
61
|
+
{/if}
|
|
62
|
+
{#if image}
|
|
63
|
+
<meta name="twitter:image" content={image} />
|
|
64
|
+
{/if}
|
|
65
|
+
</svelte:head>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DocsmithConfig } from '../../config.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
config: DocsmithConfig;
|
|
4
|
+
/** The page's own title (frontmatter or override); omit for the site root. */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** The page's own description; falls back to `config.description`. */
|
|
7
|
+
description?: string;
|
|
8
|
+
};
|
|
9
|
+
declare const SeoHead: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type SeoHead = ReturnType<typeof SeoHead>;
|
|
11
|
+
export default SeoHead;
|
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
const { children, ...attrs }: HeadingProps = $props();
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
|
-
<h2 {...attrs} class="flex
|
|
8
|
+
<h2 {...attrs} class="group flex items-center gap-2">
|
|
9
9
|
{@render children()}
|
|
10
|
-
<a
|
|
11
|
-
|
|
10
|
+
<a
|
|
11
|
+
href="#{attrs.id}"
|
|
12
|
+
aria-label="Link to this section"
|
|
13
|
+
class="opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
|
|
14
|
+
>
|
|
15
|
+
<Link class="text-primary size-5" />
|
|
12
16
|
</a>
|
|
13
17
|
</h2>
|
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
const { children, ...attrs }: HeadingProps = $props();
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
|
-
<h3 {...attrs} class="flex
|
|
8
|
+
<h3 {...attrs} class="group flex items-center gap-2">
|
|
9
9
|
{@render children()}
|
|
10
|
-
<a
|
|
11
|
-
|
|
10
|
+
<a
|
|
11
|
+
href="#{attrs.id}"
|
|
12
|
+
aria-label="Link to this section"
|
|
13
|
+
class="opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
|
|
14
|
+
>
|
|
15
|
+
<Link class="text-primary size-5" />
|
|
12
16
|
</a>
|
|
13
17
|
</h3>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
* Plain CSS (not @apply): these style Shiki's output and ship inside the
|
|
47
47
|
* published package, where the consumer's Tailwind theme context isn't
|
|
48
48
|
* available to resolve @apply (Tailwind v4 compiles scoped styles in
|
|
49
|
-
* isolation).
|
|
49
|
+
* isolation).
|
|
50
50
|
*/
|
|
51
51
|
:global(pre code) {
|
|
52
52
|
display: block;
|