svelte-docsmith 0.9.0 → 0.10.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 (45) hide show
  1. package/bin/svelte-docsmith.mjs +161 -0
  2. package/dist/buildtime/archive.d.ts +31 -0
  3. package/dist/buildtime/archive.js +50 -0
  4. package/dist/buildtime/markdown-source.d.ts +31 -0
  5. package/dist/buildtime/markdown-source.js +90 -0
  6. package/dist/buildtime/vite/collect.d.ts +4 -3
  7. package/dist/buildtime/vite/collect.js +31 -12
  8. package/dist/buildtime/vite/extract.js +9 -35
  9. package/dist/buildtime/vite/frontmatter.js +4 -3
  10. package/dist/buildtime/vite/index.d.ts +9 -0
  11. package/dist/buildtime/vite/index.js +11 -4
  12. package/dist/components/chrome/copy-button.svelte +3 -2
  13. package/dist/components/chrome/search.svelte +16 -4
  14. package/dist/components/chrome/search.svelte.d.ts +4 -1
  15. package/dist/components/chrome/version-banner.svelte +82 -0
  16. package/dist/components/chrome/version-banner.svelte.d.ts +10 -0
  17. package/dist/components/chrome/version-switcher.svelte +63 -0
  18. package/dist/components/chrome/version-switcher.svelte.d.ts +14 -0
  19. package/dist/components/layouts/docs-header.svelte +26 -3
  20. package/dist/components/layouts/docs-header.svelte.d.ts +9 -1
  21. package/dist/components/layouts/docs-mobile-header.svelte +25 -2
  22. package/dist/components/layouts/docs-mobile-header.svelte.d.ts +9 -1
  23. package/dist/components/layouts/docs-shell.svelte +84 -66
  24. package/dist/components/layouts/docs-shell.svelte.d.ts +11 -2
  25. package/dist/components/layouts/error-page.svelte +23 -3
  26. package/dist/components/layouts/error-page.svelte.d.ts +8 -2
  27. package/dist/components/layouts/seo-head.svelte +8 -1
  28. package/dist/components/layouts/seo-head.svelte.d.ts +2 -0
  29. package/dist/core/content.d.ts +9 -0
  30. package/dist/core/docs-page.d.ts +70 -0
  31. package/dist/core/docs-page.js +55 -0
  32. package/dist/core/index.d.ts +2 -0
  33. package/dist/core/index.js +2 -0
  34. package/dist/core/version.d.ts +80 -0
  35. package/dist/core/version.js +85 -0
  36. package/dist/fallbacks/content.d.ts +2 -0
  37. package/dist/fallbacks/content.js +1 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +5 -0
  40. package/dist/search/context.svelte.d.ts +2 -0
  41. package/dist/search/context.svelte.js +2 -0
  42. package/dist/search/create-search.d.ts +8 -2
  43. package/dist/search/create-search.js +11 -3
  44. package/dist/theme.css +21 -0
  45. package/package.json +7 -2
@@ -16,7 +16,7 @@
16
16
  } = $props();
17
17
  </script>
18
18
 
19
- <Button {onclick} variant="ghost" size="icon" class="size-8 {className ?? ''}">
19
+ <Button {onclick} variant="ghost" size="icon" aria-label="Copy" class="size-8 {className ?? ''}">
20
20
  {#if copied}
21
21
  <div in:fade={{ duration: 80 }}>
22
22
  <Check class="text-emerald-500" />
@@ -26,5 +26,6 @@
26
26
  <Copy />
27
27
  </div>
28
28
  {/if}
29
- <span class="sr-only">Copy</span>
29
+ <!-- Announce success to screen readers without altering the button's name. -->
30
+ <span class="sr-only" aria-live="polite">{copied ? 'Copied to clipboard' : ''}</span>
30
31
  </Button>
@@ -17,8 +17,11 @@
17
17
  * Lazily provide the generated search records. Wired by the consumer so the
18
18
  * index is code-split and only fetched when the palette first opens, e.g.
19
19
  * `() => import('svelte-docsmith/search').then((m) => m.docs)`.
20
+ *
21
+ * Receives the active version id on a versioned site, so a loader may
22
+ * return just that version's records; results are scoped either way.
20
23
  */
21
- load: () => Promise<SearchDoc[]>;
24
+ load: (versionId?: string) => Promise<SearchDoc[]>;
22
25
  placeholder?: string;
23
26
  } = $props();
24
27
 
@@ -27,20 +30,29 @@
27
30
  let query = $state('');
28
31
  let engine = $state<SearchEngine | null>(null);
29
32
  let status = $state<'idle' | 'loading' | 'error'>('idle');
33
+ // The version the cached engine was built for, so a loader that returns only
34
+ // one version's records is rebuilt when the reader switches versions. Stays
35
+ // constant (and the engine cached) on unversioned and single-version sites.
36
+ let engineVersion: string | undefined;
30
37
 
31
38
  const trimmed = $derived(query.trim());
32
- const results = $derived(engine && trimmed ? engine.search(query) : []);
39
+ // Scope to the active version when the shell set one (versioned sites).
40
+ const results = $derived(
41
+ engine && trimmed ? engine.search(query, undefined, search?.version) : []
42
+ );
33
43
 
34
44
  // Build the index the first time the palette opens; keep it for later opens.
35
45
  async function ensureEngine() {
36
- if (engine || status === 'loading') return;
46
+ const version = search?.version;
47
+ if ((engine && engineVersion === version) || status === 'loading') return;
37
48
  status = 'loading';
38
49
  try {
39
50
  const [{ createSearchEngine }, docs] = await Promise.all([
40
51
  import('../../search/create-search.js'),
41
- load()
52
+ load(version)
42
53
  ]);
43
54
  engine = createSearchEngine(docs);
55
+ engineVersion = version;
44
56
  status = 'idle';
45
57
  } catch (error) {
46
58
  console.error('[svelte-docsmith] failed to load the search index', error);
@@ -4,8 +4,11 @@ type $$ComponentProps = {
4
4
  * Lazily provide the generated search records. Wired by the consumer so the
5
5
  * index is code-split and only fetched when the palette first opens, e.g.
6
6
  * `() => import('svelte-docsmith/search').then((m) => m.docs)`.
7
+ *
8
+ * Receives the active version id on a versioned site, so a loader may
9
+ * return just that version's records; results are scoped either way.
7
10
  */
8
- load: () => Promise<SearchDoc[]>;
11
+ load: (versionId?: string) => Promise<SearchDoc[]>;
9
12
  placeholder?: string;
10
13
  };
11
14
  declare const Search: import("svelte").Component<$$ComponentProps, {}, "">;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import TriangleAlert from '@lucide/svelte/icons/triangle-alert';
3
+ import ArrowRight from '@lucide/svelte/icons/arrow-right';
4
+ import {
5
+ mapPathToVersion,
6
+ scopeContent,
7
+ type ResolvedVersion,
8
+ type DocsContentItem
9
+ } from '../../core/index.js';
10
+
11
+ // Rendered by DocsShell only on an archived version, to warn a reader who
12
+ // landed there (usually from a search engine) and give them a one-click path
13
+ // to the current equivalent. A real <a> (not client nav) so it works without
14
+ // JS and stays crawlable.
15
+ const {
16
+ active,
17
+ current,
18
+ pathname,
19
+ content
20
+ }: {
21
+ active: ResolvedVersion;
22
+ current: ResolvedVersion;
23
+ pathname: string;
24
+ content: DocsContentItem[];
25
+ } = $props();
26
+
27
+ const currentHref = $derived(
28
+ mapPathToVersion(
29
+ pathname,
30
+ active,
31
+ current,
32
+ scopeContent(content, current.id).map((c) => c.path)
33
+ )
34
+ );
35
+ </script>
36
+
37
+ <div class="docsmith-version-banner" role="note">
38
+ <TriangleAlert class="banner-icon" size={18} aria-hidden="true" />
39
+ <p>
40
+ You're reading the <strong>{active.label}</strong> docs. The current version is
41
+ <strong>{current.label}</strong>.
42
+ <a href={currentHref}>View this page in {current.label} <ArrowRight size={14} /></a>
43
+ </p>
44
+ </div>
45
+
46
+ <style>
47
+ .docsmith-version-banner {
48
+ display: flex;
49
+ gap: 0.6rem;
50
+ align-items: flex-start;
51
+ margin-bottom: 1.75rem;
52
+ padding: 0.75rem 1rem;
53
+ border: 1px solid;
54
+ border-radius: var(--radius);
55
+ font-size: 0.9rem;
56
+ line-height: 1.5;
57
+ color: var(--foreground);
58
+ border-color: oklch(0.62 0.14 75 / 0.3);
59
+ background: oklch(0.62 0.14 75 / 0.1);
60
+ }
61
+ :global(.docsmith-version-banner .banner-icon) {
62
+ margin-top: 0.1rem;
63
+ flex-shrink: 0;
64
+ color: oklch(0.62 0.14 75);
65
+ }
66
+ :global(.dark) .docsmith-version-banner :global(.banner-icon) {
67
+ color: oklch(0.82 0.13 80);
68
+ }
69
+ .docsmith-version-banner p {
70
+ margin: 0;
71
+ }
72
+ .docsmith-version-banner a {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ gap: 0.2rem;
76
+ font-weight: 600;
77
+ color: var(--primary);
78
+ white-space: nowrap;
79
+ text-decoration: underline;
80
+ text-underline-offset: 2px;
81
+ }
82
+ </style>
@@ -0,0 +1,10 @@
1
+ import { type ResolvedVersion, type DocsContentItem } from '../../core/index.js';
2
+ type $$ComponentProps = {
3
+ active: ResolvedVersion;
4
+ current: ResolvedVersion;
5
+ pathname: string;
6
+ content: DocsContentItem[];
7
+ };
8
+ declare const VersionBanner: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type VersionBanner = ReturnType<typeof VersionBanner>;
10
+ export default VersionBanner;
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ import { goto } from '$app/navigation';
3
+ import { Button } from '../shadcn/button/index.js';
4
+ import * as DropdownMenu from '../shadcn/dropdown-menu/index.js';
5
+ import Check from '@lucide/svelte/icons/check';
6
+ import ChevronsUpDown from '@lucide/svelte/icons/chevrons-up-down';
7
+ import {
8
+ mapPathToVersion,
9
+ scopeContent,
10
+ type ResolvedVersion,
11
+ type DocsContentItem
12
+ } from '../../core/index.js';
13
+
14
+ const {
15
+ versions,
16
+ active,
17
+ pathname,
18
+ content
19
+ }: {
20
+ /** All declared versions, in switcher order. */
21
+ versions: ResolvedVersion[];
22
+ /** The version the current page belongs to. */
23
+ active: ResolvedVersion;
24
+ /** The current normalized pathname, e.g. `/docs/v2/intro`. */
25
+ pathname: string;
26
+ /** Full content index, to map the current page into the chosen version. */
27
+ content: DocsContentItem[];
28
+ } = $props();
29
+
30
+ function select(target: ResolvedVersion) {
31
+ if (target.id === active.id) return;
32
+ const paths = scopeContent(content, target.id).map((c) => c.path);
33
+ goto(mapPathToVersion(pathname, active, target, paths));
34
+ }
35
+ </script>
36
+
37
+ <DropdownMenu.Root>
38
+ <DropdownMenu.Trigger>
39
+ {#snippet child({ props })}
40
+ <Button
41
+ variant="outline"
42
+ size="sm"
43
+ class="h-8 gap-1.5 px-2.5 font-medium"
44
+ aria-label={`Documentation version: ${active.label}. Change version`}
45
+ {...props}
46
+ >
47
+ <span class="truncate">{active.label}</span>
48
+ <ChevronsUpDown class="size-3.5 shrink-0 opacity-60" aria-hidden="true" />
49
+ </Button>
50
+ {/snippet}
51
+ </DropdownMenu.Trigger>
52
+ <DropdownMenu.Content align="start" class="w-44">
53
+ {#each versions as version (version.id)}
54
+ <DropdownMenu.Item onSelect={() => select(version)} class="gap-2">
55
+ <Check
56
+ class="size-4 {version.id === active.id ? 'opacity-100' : 'opacity-0'}"
57
+ aria-hidden="true"
58
+ />
59
+ <span class="flex-1 truncate">{version.label}</span>
60
+ </DropdownMenu.Item>
61
+ {/each}
62
+ </DropdownMenu.Content>
63
+ </DropdownMenu.Root>
@@ -0,0 +1,14 @@
1
+ import { type ResolvedVersion, type DocsContentItem } from '../../core/index.js';
2
+ type $$ComponentProps = {
3
+ /** All declared versions, in switcher order. */
4
+ versions: ResolvedVersion[];
5
+ /** The version the current page belongs to. */
6
+ active: ResolvedVersion;
7
+ /** The current normalized pathname, e.g. `/docs/v2/intro`. */
8
+ pathname: string;
9
+ /** Full content index, to map the current page into the chosen version. */
10
+ content: DocsContentItem[];
11
+ };
12
+ declare const VersionSwitcher: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type VersionSwitcher = ReturnType<typeof VersionSwitcher>;
14
+ export default VersionSwitcher;
@@ -6,7 +6,13 @@
6
6
  import SearchTrigger from '../chrome/search-trigger.svelte';
7
7
  import { useSearch } from '../../search/context.svelte.js';
8
8
  import ThemeToggle from '../chrome/theme-toggle.svelte';
9
- import type { DocsmithConfig, DocsmithLink } from '../../core/index.js';
9
+ import VersionSwitcher from '../chrome/version-switcher.svelte';
10
+ import type {
11
+ DocsmithConfig,
12
+ DocsmithLink,
13
+ DocsContentItem,
14
+ ResolvedVersion
15
+ } from '../../core/index.js';
10
16
  import BookOpenText from '@lucide/svelte/icons/book-open-text';
11
17
  import type { Snippet } from 'svelte';
12
18
  import { page } from '$app/state';
@@ -16,13 +22,25 @@
16
22
  const {
17
23
  config,
18
24
  logo,
19
- actions
25
+ actions,
26
+ versions = [],
27
+ active,
28
+ content = [],
29
+ pathname = ''
20
30
  }: {
21
31
  config: DocsmithConfig;
22
32
  /** Custom logo mark; defaults to a book icon in a primary-tinted chip. */
23
33
  logo?: Snippet;
24
34
  /** Extra header controls, rendered before the theme toggle. */
25
35
  actions?: Snippet;
36
+ /** Declared versions; renders the switcher once there's more than one. */
37
+ versions?: ResolvedVersion[];
38
+ /** The active version, for the switcher's current selection. */
39
+ active?: ResolvedVersion;
40
+ /** Content index, so the switcher can map the current page across versions. */
41
+ content?: DocsContentItem[];
42
+ /** Current normalized pathname, for the switcher's page mapping. */
43
+ pathname?: string;
26
44
  } = $props();
27
45
 
28
46
  // Present only when the consumer passed a `search` loader to DocsShell.
@@ -91,7 +109,11 @@
91
109
  </nav>
92
110
  {/if}
93
111
 
94
- {#if config.version}
112
+ {#if versions.length > 1 && active}
113
+ <div class="px-1">
114
+ <VersionSwitcher {versions} {active} {content} {pathname} />
115
+ </div>
116
+ {:else if config.version}
95
117
  <div class="px-2">
96
118
  <Badge
97
119
  variant="outline"
@@ -121,6 +143,7 @@
121
143
  size="icon"
122
144
  variant="ghost"
123
145
  target="_blank"
146
+ rel="noopener noreferrer"
124
147
  href={config.github}
125
148
  class="text-muted-foreground hover:text-foreground size-8"
126
149
  >
@@ -1,4 +1,4 @@
1
- import type { DocsmithConfig } from '../../core/index.js';
1
+ import type { DocsmithConfig, DocsContentItem, ResolvedVersion } from '../../core/index.js';
2
2
  import type { Snippet } from 'svelte';
3
3
  type $$ComponentProps = {
4
4
  config: DocsmithConfig;
@@ -6,6 +6,14 @@ type $$ComponentProps = {
6
6
  logo?: Snippet;
7
7
  /** Extra header controls, rendered before the theme toggle. */
8
8
  actions?: Snippet;
9
+ /** Declared versions; renders the switcher once there's more than one. */
10
+ versions?: ResolvedVersion[];
11
+ /** The active version, for the switcher's current selection. */
12
+ active?: ResolvedVersion;
13
+ /** Content index, so the switcher can map the current page across versions. */
14
+ content?: DocsContentItem[];
15
+ /** Current normalized pathname, for the switcher's page mapping. */
16
+ pathname?: string;
9
17
  };
10
18
  declare const DocsHeader: import("svelte").Component<$$ComponentProps, {}, "">;
11
19
  type DocsHeader = ReturnType<typeof DocsHeader>;
@@ -1,10 +1,16 @@
1
1
  <script lang="ts">
2
2
  import { page } from '$app/state';
3
- import type { DocsmithConfig, NavGroup } from '../../core/index.js';
3
+ import type {
4
+ DocsmithConfig,
5
+ NavGroup,
6
+ DocsContentItem,
7
+ ResolvedVersion
8
+ } from '../../core/index.js';
4
9
  import type { TocItem } from '../../toc/index.js';
5
10
  import DocsSidebar from './docs-sidebar.svelte';
6
11
  import TableOfContents from '../chrome/table-of-contents.svelte';
7
12
  import ThemeToggle from '../chrome/theme-toggle.svelte';
13
+ import VersionSwitcher from '../chrome/version-switcher.svelte';
8
14
  import { Button } from '../shadcn/button/index.js';
9
15
  import * as Popover from '../shadcn/popover/index.js';
10
16
  import { ScrollArea } from '../shadcn/scroll-area/index.js';
@@ -24,7 +30,11 @@
24
30
  tocItems = [],
25
31
  tocActiveId = null,
26
32
  logo,
27
- actions
33
+ actions,
34
+ versions = [],
35
+ active,
36
+ content = [],
37
+ pathname = ''
28
38
  }: {
29
39
  config: DocsmithConfig;
30
40
  /** Sidebar groups; omit on non-doc pages (the landing/`page` layout). */
@@ -37,6 +47,14 @@
37
47
  tocActiveId?: string | null;
38
48
  logo?: Snippet;
39
49
  actions?: Snippet;
50
+ /** Declared versions; renders the switcher in the drawer once there's more than one. */
51
+ versions?: ResolvedVersion[];
52
+ /** The active version, for the switcher's current selection. */
53
+ active?: ResolvedVersion;
54
+ /** Content index, so the switcher can map the current page across versions. */
55
+ content?: DocsContentItem[];
56
+ /** Current normalized pathname, for the switcher's page mapping. */
57
+ pathname?: string;
40
58
  } = $props();
41
59
 
42
60
  const search = useSearch();
@@ -77,6 +95,11 @@
77
95
  <span class="inline-block">{config.title}</span>
78
96
  </a>
79
97
  </div>
98
+ {#if versions.length > 1 && active}
99
+ <div class="px-7 pb-3">
100
+ <VersionSwitcher {versions} {active} {content} {pathname} />
101
+ </div>
102
+ {/if}
80
103
  <ScrollArea class="my-4 h-[calc(100vh-8rem)] pb-10 pl-2">
81
104
  {#if config.nav?.length}
82
105
  <nav aria-label="Main" class="flex flex-col gap-0.5 px-5 pb-1">
@@ -1,4 +1,4 @@
1
- import type { DocsmithConfig, NavGroup } from '../../core/index.js';
1
+ import type { DocsmithConfig, NavGroup, DocsContentItem, ResolvedVersion } from '../../core/index.js';
2
2
  import type { TocItem } from '../../toc/index.js';
3
3
  import type { Snippet } from 'svelte';
4
4
  type $$ComponentProps = {
@@ -13,6 +13,14 @@ type $$ComponentProps = {
13
13
  tocActiveId?: string | null;
14
14
  logo?: Snippet;
15
15
  actions?: Snippet;
16
+ /** Declared versions; renders the switcher in the drawer once there's more than one. */
17
+ versions?: ResolvedVersion[];
18
+ /** The active version, for the switcher's current selection. */
19
+ active?: ResolvedVersion;
20
+ /** Content index, so the switcher can map the current page across versions. */
21
+ content?: DocsContentItem[];
22
+ /** Current normalized pathname, for the switcher's page mapping. */
23
+ pathname?: string;
16
24
  };
17
25
  declare const DocsMobileHeader: import("svelte").Component<$$ComponentProps, {}, "">;
18
26
  type DocsMobileHeader = ReturnType<typeof DocsMobileHeader>;