svelte-docsmith 0.12.2 → 0.13.1
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 +40 -11
- package/dist/buildtime/archive.d.ts +18 -0
- package/dist/buildtime/archive.js +61 -1
- package/dist/buildtime/archives.d.ts +3 -0
- package/dist/buildtime/archives.js +3 -0
- package/dist/buildtime/cli/archive-version.d.ts +3 -0
- package/dist/buildtime/cli/archive-version.js +30 -2
- package/dist/buildtime/cli/run.js +3 -0
- package/dist/buildtime/git.d.ts +13 -6
- package/dist/buildtime/git.js +31 -6
- package/dist/buildtime/markdown-layout.svelte +3 -1
- package/dist/buildtime/markdown-source.d.ts +9 -0
- package/dist/buildtime/markdown-source.js +22 -0
- package/dist/buildtime/vite/index.d.ts +4 -0
- package/dist/buildtime/vite/index.js +4 -1
- package/dist/components/chrome/background-pattern.svelte +22 -4
- package/dist/components/chrome/search.svelte +64 -9
- package/dist/components/chrome/theme-toggle.svelte +5 -1
- package/dist/components/landing/cta.svelte +15 -3
- package/dist/components/landing/cta.svelte.d.ts +6 -1
- package/dist/components/landing/feature-grid.svelte +4 -2
- package/dist/components/landing/feature.svelte +1 -1
- package/dist/components/landing/hero.svelte +2 -2
- package/dist/components/layouts/docs-mobile-header.svelte +22 -4
- package/dist/components/layouts/docs-shell.svelte +5 -1
- package/dist/components/layouts/docs-sidebar.svelte +1 -1
- package/dist/components/layouts/page-feedback.svelte +44 -2
- package/dist/components/layouts/page-feedback.svelte.d.ts +4 -0
- package/dist/components/layouts/prev-next-nav.svelte +16 -4
- package/dist/core/content.d.ts +5 -3
- package/dist/core/docs-page.d.ts +5 -4
- package/dist/core/docs-page.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/nav.d.ts +9 -4
- package/dist/core/nav.js +24 -5
- package/dist/core/version.d.ts +24 -2
- package/dist/core/version.js +3 -0
- package/dist/index.js +6 -4
- package/dist/theme.css +8 -3
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -15,8 +15,17 @@ Write a markdown file under `src/routes/docs/` and you get a styled page with
|
|
|
15
15
|
syntax highlighting, heading anchors, a sidebar derived from your content, and a
|
|
16
16
|
live table of contents. No per-page wiring, no content collection to configure.
|
|
17
17
|
|
|
18
|
-
> **Status: pre-1.0.** Published to npm and usable today.
|
|
19
|
-
>
|
|
18
|
+
> **Status: pre-1.0.** Published to npm and usable today. Minor releases may still
|
|
19
|
+
> include breaking changes until v1.0.
|
|
20
|
+
>
|
|
21
|
+
> **What 1.0 will lock.** Authoring components, `DocsShell`, `ErrorPage`,
|
|
22
|
+
> `defineConfig`, the preprocessor, the Vite plugin, the sitemap / llms / feed
|
|
23
|
+
> generators, the search engine, and the theming contract — ordinary semver,
|
|
24
|
+
> breaking changes ride a major.
|
|
25
|
+
>
|
|
26
|
+
> **What stays experimental.** Versioning: `DocsVersions`, `currentOnly`, the
|
|
27
|
+
> archive marker, and the `archive-version` command. They may still change in a
|
|
28
|
+
> minor until authors have used them for real.
|
|
20
29
|
|
|
21
30
|
## Install
|
|
22
31
|
|
|
@@ -166,6 +175,20 @@ plugin you already added:
|
|
|
166
175
|
Graph / Twitter tags for every page from its frontmatter, no per-page wiring.
|
|
167
176
|
- **Error pages.** `ErrorPage` gives a styled 404 that keeps the site chrome;
|
|
168
177
|
drop it into a SvelteKit `+error.svelte`.
|
|
178
|
+
- **Versioned docs.** Declare `versions` on the Vite plugin, freeze a release
|
|
179
|
+
with `npx svelte-docsmith archive-version <id>`, and pass the generated
|
|
180
|
+
manifest to `DocsShell`. Scope sitemap / llms endpoints to the current
|
|
181
|
+
version with `currentOnly`.
|
|
182
|
+
- **Changelog.** Point the plugin at your `CHANGELOG.md`; import the parsed
|
|
183
|
+
releases from `svelte-docsmith/changelog`, render them with `Changelog`, and
|
|
184
|
+
wire `generateFeed` into an Atom endpoint.
|
|
185
|
+
- **Mermaid.** A mermaid code fence is rendered client-side (optional `mermaid`
|
|
186
|
+
peer dependency, loaded only on pages that need it).
|
|
187
|
+
- **Landing sections.** `Hero`, `FeatureGrid`, `Feature`, `CTA`, and `Action`
|
|
188
|
+
for the marketing page in front of your docs.
|
|
189
|
+
- **Generators.** Framework-agnostic helpers for `sitemap.xml`, `llms.txt` /
|
|
190
|
+
`llms-full.txt`, and the changelog Atom feed — drop them into `+server.ts`
|
|
191
|
+
routes over the generated indexes.
|
|
169
192
|
- **Components.** `Callout`, `Tabs`, `Steps`, `Card`, `Accordion`, `Badge`,
|
|
170
193
|
`Kbd`, `FileTree`, `PropsTable`, and more, authored right inside markdown.
|
|
171
194
|
- Heading anchors, a scroll-spy table of contents, and one-click copy on every
|
|
@@ -187,15 +210,21 @@ override the tokens yourself.
|
|
|
187
210
|
|
|
188
211
|
## What's exported
|
|
189
212
|
|
|
190
|
-
| Entry point | What it is
|
|
191
|
-
| ------------------------------ |
|
|
192
|
-
| `svelte-docsmith` |
|
|
193
|
-
| `svelte-docsmith/preprocess` | `docsmith()`, the mdsvex/Shiki pipeline (Node, config time)
|
|
194
|
-
| `svelte-docsmith/vite` | `docsmith()`, the content
|
|
195
|
-
| `svelte-docsmith/content` | `docs`, the generated sidebar content index
|
|
196
|
-
| `svelte-docsmith/search` | `docs`, the generated full-text search index (lazy-load it)
|
|
197
|
-
| `svelte-docsmith/
|
|
198
|
-
| `svelte-docsmith/
|
|
213
|
+
| Entry point | What it is |
|
|
214
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
215
|
+
| `svelte-docsmith` | Components (`DocsShell`, `Changelog`, landing sections, `LiveExample`, `Callout`, …), `defineConfig`, `createSearchEngine`, `generateSitemap` / `generateLlmsTxt` / `generateLlmsFullTxt` / `generateFeed`, `currentOnly`, and the types |
|
|
216
|
+
| `svelte-docsmith/preprocess` | `docsmith()`, the mdsvex/Shiki pipeline (Node, config time) |
|
|
217
|
+
| `svelte-docsmith/vite` | `docsmith()`, the content / search / llms / changelog indexes and `?source` transform (Node, build) |
|
|
218
|
+
| `svelte-docsmith/content` | `docs` and `versions`, the generated sidebar content index and version manifest |
|
|
219
|
+
| `svelte-docsmith/search` | `docs`, the generated full-text search index (lazy-load it) |
|
|
220
|
+
| `svelte-docsmith/llms` | `docs`, the generated LLM content index (for `llms.txt` / `llms-full.txt` routes) |
|
|
221
|
+
| `svelte-docsmith/changelog` | `releases`, the generated changelog index (parsed from `CHANGELOG.md`) |
|
|
222
|
+
| `svelte-docsmith/mermaid` | the Mermaid diagram component (lazy-loaded by the preprocessor when a mermaid fence is present) |
|
|
223
|
+
| `svelte-docsmith/theme.css` | the base style contract |
|
|
224
|
+
| `svelte-docsmith/themes/*.css` | the eleven theme presets |
|
|
225
|
+
|
|
226
|
+
The package also ships a CLI binary: `npx svelte-docsmith archive-version <id>`
|
|
227
|
+
freezes the current docs into a versioned archive.
|
|
199
228
|
|
|
200
229
|
The vendored shadcn primitives and internal helpers (the TOC engine, clipboard
|
|
201
230
|
utility, markdown renderer map) are **not** part of the public API; they can
|
|
@@ -29,3 +29,21 @@ export declare function rewriteDocsLinks(text: string, options: {
|
|
|
29
29
|
* existing `lastUpdated` and any page without frontmatter alone.
|
|
30
30
|
*/
|
|
31
31
|
export declare function freezeLastUpdated(text: string, date: string | undefined): string;
|
|
32
|
+
/**
|
|
33
|
+
* The specifiers a page's `<script>` imports from outside the freeze boundary,
|
|
34
|
+
* deduplicated in source order. `pageDir` is the page's directory relative to
|
|
35
|
+
* the docs root, which is what its relative imports resolve against.
|
|
36
|
+
*
|
|
37
|
+
* Archiving copies the docs root and nothing else, so anything reached from
|
|
38
|
+
* outside it keeps resolving to current code: a live example on an archived
|
|
39
|
+
* page demonstrates the current library rather than the version the page
|
|
40
|
+
* documents, with a green build and nothing said. Hence `$lib`, bare npm
|
|
41
|
+
* specifiers and relative paths that climb out all count, while a relative
|
|
42
|
+
* import landing inside the docs root is frozen along with the page.
|
|
43
|
+
*
|
|
44
|
+
* `svelte-docsmith` itself is never reported. An archived page importing the
|
|
45
|
+
* authoring components is the expected shape, and those are covered by the
|
|
46
|
+
* stability promise rather than by this notice. See
|
|
47
|
+
* `docs/adr/0005-an-archive-freezes-content-not-dependencies.md`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function boundaryCrossings(source: string, pageDir: string): string[];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* CLI is a thin wrapper over them. See `docs/adr/0002-archives-are-rewritten-source-copies.md`.
|
|
9
9
|
*/
|
|
10
10
|
import { atBoundary, firstSegmentUnder } from '../utils/url.js';
|
|
11
|
-
import { outsideCodeFences, withFrontmatter } from './markdown-source.js';
|
|
11
|
+
import { outsideCodeFences, scriptBlocks, withFrontmatter } from './markdown-source.js';
|
|
12
12
|
/** Route files at the docs root that an archive nested inside it already inherits. */
|
|
13
13
|
export function isInheritedRouteFile(name) {
|
|
14
14
|
return /^\+(layout|error)\./.test(name);
|
|
@@ -50,3 +50,63 @@ export function freezeLastUpdated(text, date) {
|
|
|
50
50
|
return text;
|
|
51
51
|
return withFrontmatter(text, (front) => /^lastUpdated:/m.test(front) ? front : `${front}\nlastUpdated: '${date}'`);
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* An import's specifier, static or dynamic. The clause between `import` and
|
|
55
|
+
* `from` may not hold a quote or a paren, so the scan cannot run past a
|
|
56
|
+
* side-effect import to borrow a later `from`, and `import.meta.glob('./x')`
|
|
57
|
+
* matches nothing.
|
|
58
|
+
*/
|
|
59
|
+
const IMPORT = /\bimport\s*\(?\s*(?:[^'"()]*?\bfrom\s*)?['"]([^'"]+)['"]/g;
|
|
60
|
+
/** The package an archived page is expected to import, and its subpaths. */
|
|
61
|
+
const LIBRARY = 'svelte-docsmith';
|
|
62
|
+
/**
|
|
63
|
+
* Whether a relative specifier climbs out of the docs root when resolved from a
|
|
64
|
+
* page `depth` directories inside it. Counting segments rather than resolving
|
|
65
|
+
* real paths keeps the rule pure, and the docs root is the only boundary that
|
|
66
|
+
* matters: where under it a surviving import lands is the archive's business.
|
|
67
|
+
*/
|
|
68
|
+
function escapesRoot(specifier, depth) {
|
|
69
|
+
let level = depth;
|
|
70
|
+
for (const segment of specifier.split('/')) {
|
|
71
|
+
if (segment === '..')
|
|
72
|
+
level--;
|
|
73
|
+
else if (segment !== '.' && segment !== '')
|
|
74
|
+
level++;
|
|
75
|
+
if (level < 0)
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The specifiers a page's `<script>` imports from outside the freeze boundary,
|
|
82
|
+
* deduplicated in source order. `pageDir` is the page's directory relative to
|
|
83
|
+
* the docs root, which is what its relative imports resolve against.
|
|
84
|
+
*
|
|
85
|
+
* Archiving copies the docs root and nothing else, so anything reached from
|
|
86
|
+
* outside it keeps resolving to current code: a live example on an archived
|
|
87
|
+
* page demonstrates the current library rather than the version the page
|
|
88
|
+
* documents, with a green build and nothing said. Hence `$lib`, bare npm
|
|
89
|
+
* specifiers and relative paths that climb out all count, while a relative
|
|
90
|
+
* import landing inside the docs root is frozen along with the page.
|
|
91
|
+
*
|
|
92
|
+
* `svelte-docsmith` itself is never reported. An archived page importing the
|
|
93
|
+
* authoring components is the expected shape, and those are covered by the
|
|
94
|
+
* stability promise rather than by this notice. See
|
|
95
|
+
* `docs/adr/0005-an-archive-freezes-content-not-dependencies.md`.
|
|
96
|
+
*/
|
|
97
|
+
export function boundaryCrossings(source, pageDir) {
|
|
98
|
+
const depth = pageDir.split('/').filter(Boolean).length;
|
|
99
|
+
const found = new Set();
|
|
100
|
+
for (const block of scriptBlocks(source)) {
|
|
101
|
+
for (const [, specifier] of block.matchAll(IMPORT)) {
|
|
102
|
+
if (specifier === LIBRARY || specifier.startsWith(`${LIBRARY}/`))
|
|
103
|
+
continue;
|
|
104
|
+
// Only a package name can start with something other than a dot, and no
|
|
105
|
+
// package name starts with one, so this is the whole relative/bare split.
|
|
106
|
+
if (specifier.startsWith('.') && !escapesRoot(specifier, depth))
|
|
107
|
+
continue;
|
|
108
|
+
found.add(specifier);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...found];
|
|
112
|
+
}
|
|
@@ -6,6 +6,9 @@ import type { ArchivesOnDisk } from '../core/version.js';
|
|
|
6
6
|
* are otherwise indistinguishable, since a page is assigned to a version by its
|
|
7
7
|
* first directory segment. See
|
|
8
8
|
* `docs/adr/0003-the-archive-marker-defines-an-archive.md`.
|
|
9
|
+
*
|
|
10
|
+
* @experimental Outside the 1.0 stability promise. Name and role may change in
|
|
11
|
+
* a minor until authors have used versioning for real.
|
|
9
12
|
*/
|
|
10
13
|
export declare const ARCHIVE_MARKER = ".docsmith-archive";
|
|
11
14
|
/** The text written into a new archive's marker file. */
|
|
@@ -16,6 +16,9 @@ import path from 'node:path';
|
|
|
16
16
|
* are otherwise indistinguishable, since a page is assigned to a version by its
|
|
17
17
|
* first directory segment. See
|
|
18
18
|
* `docs/adr/0003-the-archive-marker-defines-an-archive.md`.
|
|
19
|
+
*
|
|
20
|
+
* @experimental Outside the 1.0 stability promise. Name and role may change in
|
|
21
|
+
* a minor until authors have used versioning for real.
|
|
19
22
|
*/
|
|
20
23
|
export const ARCHIVE_MARKER = '.docsmith-archive';
|
|
21
24
|
/** The text written into a new archive's marker file. */
|
|
@@ -14,5 +14,8 @@ export type ArchiveVersionOptions = {
|
|
|
14
14
|
* Copy the docs root into `<content>/<id>/`, mark it as an archive, and rewrite
|
|
15
15
|
* each copied page so it stays inside the archive and keeps its real
|
|
16
16
|
* last-updated date. Returns the number of pages archived.
|
|
17
|
+
*
|
|
18
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor until
|
|
19
|
+
* authors have used versioning for real.
|
|
17
20
|
*/
|
|
18
21
|
export declare function archiveVersion(options: ArchiveVersionOptions, log: (line: string) => void): number;
|
|
@@ -3,15 +3,23 @@
|
|
|
3
3
|
* archived version folder so they keep serving the release they document while
|
|
4
4
|
* the docs root goes on being edited.
|
|
5
5
|
*
|
|
6
|
+
* Freezes content, not dependencies: the prose, samples and URLs are that
|
|
7
|
+
* version's, while anything a page imports keeps resolving to whatever the app
|
|
8
|
+
* currently has installed. See
|
|
9
|
+
* `docs/adr/0005-an-archive-freezes-content-not-dependencies.md`.
|
|
10
|
+
*
|
|
6
11
|
* The pure text transforms live in `../archive.js`; this is the filesystem work
|
|
7
12
|
* around them. It takes its working directory and its output sink rather than
|
|
8
13
|
* reading `process.cwd()` and calling `console.log`, so the whole command runs
|
|
9
14
|
* in-process under test. See `docs/adr/0002-archives-are-rewritten-source-copies.md`.
|
|
15
|
+
*
|
|
16
|
+
* @experimental Outside the 1.0 stability promise. Flags and behaviour may
|
|
17
|
+
* change in a minor until authors have used versioning for real.
|
|
10
18
|
*/
|
|
11
19
|
import fs from 'node:fs';
|
|
12
20
|
import path from 'node:path';
|
|
13
21
|
import { assertValidVersionId } from '../../core/version.js';
|
|
14
|
-
import { freezeLastUpdated, isInheritedRouteFile, rewriteDocsLinks } from '../archive.js';
|
|
22
|
+
import { boundaryCrossings, freezeLastUpdated, isInheritedRouteFile, rewriteDocsLinks } from '../archive.js';
|
|
15
23
|
import { ARCHIVE_MARKER, discoverArchives, markerContents } from '../archives.js';
|
|
16
24
|
import { commitDates } from '../git.js';
|
|
17
25
|
import { docsBaseFrom } from '../paths.js';
|
|
@@ -21,6 +29,9 @@ import { CliError } from './error.js';
|
|
|
21
29
|
* Copy the docs root into `<content>/<id>/`, mark it as an archive, and rewrite
|
|
22
30
|
* each copied page so it stays inside the archive and keeps its real
|
|
23
31
|
* last-updated date. Returns the number of pages archived.
|
|
32
|
+
*
|
|
33
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor until
|
|
34
|
+
* authors have used versioning for real.
|
|
24
35
|
*/
|
|
25
36
|
export function archiveVersion(options, log) {
|
|
26
37
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -84,17 +95,23 @@ export function archiveVersion(options, log) {
|
|
|
84
95
|
// be read: an archive is written once and never edited again, so a page that
|
|
85
96
|
// misses its date here keeps the wrong one for good.
|
|
86
97
|
const dates = commitDates(contentDir, (reason) => {
|
|
87
|
-
log(`\n!
|
|
98
|
+
log(`\n! Skipping git-derived page dates: ${reason}`);
|
|
88
99
|
log(' Archived pages will keep no last-updated date.\n');
|
|
89
100
|
});
|
|
90
101
|
copyCurrent(contentDir, toDir);
|
|
91
102
|
fs.writeFileSync(path.join(toDir, ARCHIVE_MARKER), markerContents(id));
|
|
92
103
|
let pages = 0;
|
|
104
|
+
/** One `path: specifiers` line per page that imports across the freeze boundary. */
|
|
105
|
+
const crossings = [];
|
|
93
106
|
for (const { file, source } of eachCopiedFile()) {
|
|
94
107
|
if (!isPageFile(path.basename(file)))
|
|
95
108
|
continue;
|
|
96
109
|
pages++;
|
|
97
110
|
const text = fs.readFileSync(file, 'utf-8');
|
|
111
|
+
const pageDir = path.relative(toDir, path.dirname(file)).split(path.sep).join('/');
|
|
112
|
+
const crossed = boundaryCrossings(text, pageDir);
|
|
113
|
+
if (crossed.length)
|
|
114
|
+
crossings.push(` ${rel(file)}: ${crossed.join(', ')}`);
|
|
98
115
|
fs.writeFileSync(file, freezeLastUpdated(rewriteDocsLinks(text, { docsBase, versionId: id, archivedIds }), dates.get(source)));
|
|
99
116
|
}
|
|
100
117
|
log(`\n✓ Archived the current docs into ${rel(toDir)} (${pages} pages)\n`);
|
|
@@ -102,6 +119,17 @@ export function archiveVersion(options, log) {
|
|
|
102
119
|
// Only claim the dates were kept when the walk actually produced some.
|
|
103
120
|
if (dates.size)
|
|
104
121
|
log('Each page kept its real last-updated date.');
|
|
122
|
+
// Informational: the archive is correct as content, and there is nothing to
|
|
123
|
+
// fix here. Said out loud because the alternative is a green build hiding a
|
|
124
|
+
// v1 page that demos v2, which nothing downstream can notice.
|
|
125
|
+
if (crossings.length) {
|
|
126
|
+
const count = crossings.length === 1 ? '1 page' : `${crossings.length} pages`;
|
|
127
|
+
log(`\n! ${count} import across the freeze boundary:`);
|
|
128
|
+
crossings.sort().forEach(log);
|
|
129
|
+
log(' Archiving copies the docs root, not what it imports, so these keep');
|
|
130
|
+
log(' resolving to current code. Their examples will demonstrate the');
|
|
131
|
+
log(' current library rather than the version this archive documents.\n');
|
|
132
|
+
}
|
|
105
133
|
log('Review the diff, then update docsmith({ versions }) so the archive');
|
|
106
134
|
log('is served:\n');
|
|
107
135
|
log(' versions: {');
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* message and an exit code. Keeping the process boundary out here means the
|
|
8
8
|
* command is typechecked, and testable in-process against a temporary directory
|
|
9
9
|
* rather than by spawning node and parsing stdout.
|
|
10
|
+
*
|
|
11
|
+
* @experimental `archive-version` sits outside the 1.0 stability promise and
|
|
12
|
+
* may change in a minor until authors have used versioning for real.
|
|
10
13
|
*/
|
|
11
14
|
import { archiveVersion } from './archive-version.js';
|
|
12
15
|
import { CliError } from './error.js';
|
package/dist/buildtime/git.d.ts
CHANGED
|
@@ -14,12 +14,19 @@
|
|
|
14
14
|
* all-or-nothing: a truncated read would date some pages and not others, which
|
|
15
15
|
* looks like real data and is not.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* A shallow clone also yields no dates. Its single commit introduces every
|
|
18
|
+
* tracked file, so a walk would stamp every page with the same day — silent,
|
|
19
|
+
* plausible, and wrong for both page footers and sitemap `<lastmod>`. Omitting
|
|
20
|
+
* the date is legitimate; a uniform wrong date is not. Callers that need real
|
|
21
|
+
* dates should build from a full clone (`fetch-depth: 0` in CI, unshallow on
|
|
22
|
+
* the host).
|
|
23
|
+
*
|
|
24
|
+
* `onFail` fires for a truncated read or a shallow repository: those are the
|
|
25
|
+
* cases that would otherwise produce dates that look real and are not. A
|
|
26
|
+
* directory outside a repository, a missing directory, or a machine without git
|
|
27
|
+
* all yield no dates quietly, which every caller already handles. Reporting is
|
|
28
|
+
* the caller's to do, which is why this takes a callback rather than writing to
|
|
29
|
+
* the console itself.
|
|
23
30
|
*/
|
|
24
31
|
export declare function commitDates(dir: string, onFail?: (reason: string) => void,
|
|
25
32
|
/**
|
package/dist/buildtime/git.js
CHANGED
|
@@ -28,12 +28,19 @@ import path from 'node:path';
|
|
|
28
28
|
* all-or-nothing: a truncated read would date some pages and not others, which
|
|
29
29
|
* looks like real data and is not.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* A shallow clone also yields no dates. Its single commit introduces every
|
|
32
|
+
* tracked file, so a walk would stamp every page with the same day — silent,
|
|
33
|
+
* plausible, and wrong for both page footers and sitemap `<lastmod>`. Omitting
|
|
34
|
+
* the date is legitimate; a uniform wrong date is not. Callers that need real
|
|
35
|
+
* dates should build from a full clone (`fetch-depth: 0` in CI, unshallow on
|
|
36
|
+
* the host).
|
|
37
|
+
*
|
|
38
|
+
* `onFail` fires for a truncated read or a shallow repository: those are the
|
|
39
|
+
* cases that would otherwise produce dates that look real and are not. A
|
|
40
|
+
* directory outside a repository, a missing directory, or a machine without git
|
|
41
|
+
* all yield no dates quietly, which every caller already handles. Reporting is
|
|
42
|
+
* the caller's to do, which is why this takes a callback rather than writing to
|
|
43
|
+
* the console itself.
|
|
37
44
|
*/
|
|
38
45
|
export function commitDates(dir, onFail,
|
|
39
46
|
/**
|
|
@@ -43,6 +50,13 @@ export function commitDates(dir, onFail,
|
|
|
43
50
|
*/
|
|
44
51
|
maxBuffer = 256 * 1024 * 1024) {
|
|
45
52
|
const dates = new Map();
|
|
53
|
+
// One cheap probe before the walk. On a shallow clone the walk would "work"
|
|
54
|
+
// and return one day for every file — worse than failing, so refuse first.
|
|
55
|
+
if (isShallowRepository(dir)) {
|
|
56
|
+
onFail?.('shallow clone: git history is incomplete, so commit dates would be wrong. ' +
|
|
57
|
+
'Build from a full clone (e.g. actions/checkout fetch-depth: 0), or set lastUpdated in frontmatter.');
|
|
58
|
+
return dates;
|
|
59
|
+
}
|
|
46
60
|
const res = spawnSync('git', ['log', '--format=%cs', '--name-only', '--relative', '--no-renames', '--', '.'],
|
|
47
61
|
// The 1 MB default would truncate rather than throw on a large history,
|
|
48
62
|
// which is why the cap is raised and the result checked below.
|
|
@@ -71,6 +85,17 @@ maxBuffer = 256 * 1024 * 1024) {
|
|
|
71
85
|
}
|
|
72
86
|
return dates;
|
|
73
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Whether `dir` sits in a shallow clone. Outside a repository, or when git is
|
|
90
|
+
* missing, returns false so the walk's own failure path still owns that case.
|
|
91
|
+
*/
|
|
92
|
+
function isShallowRepository(dir) {
|
|
93
|
+
const res = spawnSync('git', ['rev-parse', '--is-shallow-repository'], {
|
|
94
|
+
cwd: dir,
|
|
95
|
+
encoding: 'utf-8'
|
|
96
|
+
});
|
|
97
|
+
return res.status === 0 && res.stdout.trim() === 'true';
|
|
98
|
+
}
|
|
74
99
|
/**
|
|
75
100
|
* Release dates keyed by version, read from the commit that introduced each
|
|
76
101
|
* `## <version>` heading in a changelog. Changesets does not write dates, and a
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
}: { title?: string; description?: string; children: Snippet } = $props();
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
<!-- max-w-prose (~65ch) keeps body measure in the DESIGN.md 65–75ch band on
|
|
27
|
+
ultrawide screens; code blocks and not-prose widgets still overflow locally. -->
|
|
28
|
+
<article class="prose prose-base dark:prose-invert max-w-prose pb-16">
|
|
27
29
|
{#if title}
|
|
28
30
|
<h1>{title}</h1>
|
|
29
31
|
{/if}
|
|
@@ -29,3 +29,12 @@ export declare function withFrontmatter(source: string, transform: (frontmatter:
|
|
|
29
29
|
export declare function outsideCodeFences(text: string, transform: (line: string) => string): string;
|
|
30
30
|
/** A page's prose lines, in order, with fenced code and its markers dropped. */
|
|
31
31
|
export declare function proseLines(text: string): Generator<string>;
|
|
32
|
+
/**
|
|
33
|
+
* The code inside each `<script>` block a page actually runs, in order.
|
|
34
|
+
*
|
|
35
|
+
* Fenced blocks are skipped, which is the whole reason this reads the page
|
|
36
|
+
* through {@link proseLines} rather than scanning the raw text: a docs page
|
|
37
|
+
* about authoring is mostly ```svelte samples that open with a `<script>`, and
|
|
38
|
+
* those are code being displayed, not code the page runs.
|
|
39
|
+
*/
|
|
40
|
+
export declare function scriptBlocks(text: string): Generator<string>;
|
|
@@ -88,3 +88,25 @@ export function* proseLines(text) {
|
|
|
88
88
|
yield line;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* A `<script>` element with any attributes, through its closing tag. The open
|
|
93
|
+
* tag has to start a line, indented no further than CommonMark lets HTML be:
|
|
94
|
+
* four spaces make an indented code block, and mid-line means a sentence about
|
|
95
|
+
* a script tag rather than one. Authoring prose writes `` `<script>` `` in
|
|
96
|
+
* inline code constantly, and a page that only talks about a script block has
|
|
97
|
+
* no script block.
|
|
98
|
+
*/
|
|
99
|
+
const SCRIPT_BLOCK = /^ {0,3}<script\b[^>]*>([\s\S]*?)<\/script>/gim;
|
|
100
|
+
/**
|
|
101
|
+
* The code inside each `<script>` block a page actually runs, in order.
|
|
102
|
+
*
|
|
103
|
+
* Fenced blocks are skipped, which is the whole reason this reads the page
|
|
104
|
+
* through {@link proseLines} rather than scanning the raw text: a docs page
|
|
105
|
+
* about authoring is mostly ```svelte samples that open with a `<script>`, and
|
|
106
|
+
* those are code being displayed, not code the page runs.
|
|
107
|
+
*/
|
|
108
|
+
export function* scriptBlocks(text) {
|
|
109
|
+
const prose = [...proseLines(splitFrontmatter(text).body)].join('\n');
|
|
110
|
+
for (const match of prose.matchAll(SCRIPT_BLOCK))
|
|
111
|
+
yield match[1];
|
|
112
|
+
}
|
|
@@ -31,6 +31,10 @@ export interface DocsmithViteOptions {
|
|
|
31
31
|
* keep their unprefixed URLs. Each archived version's pages live under
|
|
32
32
|
* `<content>/<id>/` and are served at `/docs/<id>/…`. Omit for a single,
|
|
33
33
|
* unversioned docs tree (the default).
|
|
34
|
+
*
|
|
35
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor
|
|
36
|
+
* until authors have used versioning for real. An archive freezes content,
|
|
37
|
+
* not dependencies.
|
|
34
38
|
*/
|
|
35
39
|
versions?: DocsVersions;
|
|
36
40
|
}
|
|
@@ -78,7 +78,10 @@ function contentIndexPlugin(options) {
|
|
|
78
78
|
// `dev` leaves the dates as they were until the server restarts.
|
|
79
79
|
let dates;
|
|
80
80
|
const trackedDates = () => (dates ??= commitDates(contentDir, (reason) => {
|
|
81
|
-
|
|
81
|
+
// Truncation and shallow clones both refuse dates deliberately; the
|
|
82
|
+
// reason line says which. "Could not read" would misframe a shallow
|
|
83
|
+
// skip as a hard failure.
|
|
84
|
+
console.warn(`[svelte-docsmith] skipping git-derived page dates under ${contentDir}\n` +
|
|
82
85
|
` ${reason}\n` +
|
|
83
86
|
` Pages will render without a last-updated date, and sitemap entries without \`lastmod\`.`);
|
|
84
87
|
}));
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
<!-- Decorative page background: a faint grid fading out from the top, with two
|
|
2
|
-
soft primary/accent glows.
|
|
2
|
+
soft primary/accent glows. Grid lines use --border so they track every theme
|
|
3
|
+
preset; render inside a `relative isolate` container. -->
|
|
3
4
|
<div
|
|
4
5
|
class="pointer-events-none absolute inset-0 -z-10 h-full w-full overflow-hidden"
|
|
5
6
|
aria-hidden="true"
|
|
6
7
|
>
|
|
7
|
-
<div
|
|
8
|
-
class="fixed inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-size-[24px_24px] mask-[radial-gradient(ellipse_80%_80%_at_50%_0%,#000_70%,transparent_100%)]"
|
|
9
|
-
></div>
|
|
8
|
+
<div class="pattern-grid fixed inset-0"></div>
|
|
10
9
|
|
|
11
10
|
<div
|
|
12
11
|
class="bg-primary/20 pointer-events-none fixed top-0 left-1/2 h-[500px] w-[800px] -translate-x-1/2 rounded-full opacity-40 mix-blend-screen blur-[120px] dark:opacity-20 dark:mix-blend-normal"
|
|
@@ -15,3 +14,22 @@ soft primary/accent glows. Render it inside a `relative isolate` container. -->
|
|
|
15
14
|
class="bg-accent/25 pointer-events-none fixed top-28 left-1/2 h-[400px] w-[600px] -translate-x-1/2 rounded-full opacity-30 mix-blend-screen blur-[100px] dark:opacity-20 dark:mix-blend-normal"
|
|
16
15
|
></div>
|
|
17
16
|
</div>
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
.pattern-grid {
|
|
20
|
+
/* 24px cell; line colour follows the theme border token at low opacity. */
|
|
21
|
+
background-image:
|
|
22
|
+
linear-gradient(
|
|
23
|
+
to right,
|
|
24
|
+
color-mix(in oklch, var(--border) 70%, transparent) 1px,
|
|
25
|
+
transparent 1px
|
|
26
|
+
),
|
|
27
|
+
linear-gradient(
|
|
28
|
+
to bottom,
|
|
29
|
+
color-mix(in oklch, var(--border) 70%, transparent) 1px,
|
|
30
|
+
transparent 1px
|
|
31
|
+
);
|
|
32
|
+
background-size: 24px 24px;
|
|
33
|
+
mask-image: radial-gradient(ellipse 80% 80% at 50% 0%, #000 70%, transparent 100%);
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
let query = $state('');
|
|
36
36
|
let engine = $state<SearchEngine | null>(null);
|
|
37
|
+
let indexDocs = $state<SearchDoc[]>([]);
|
|
37
38
|
let status = $state<'idle' | 'loading' | 'error'>('idle');
|
|
38
39
|
// The version the cached engine was built for, so a loader that returns only
|
|
39
40
|
// one version's records is rebuilt when the reader switches versions. Stays
|
|
@@ -44,6 +45,27 @@
|
|
|
44
45
|
// Scope to the active version on a versioned site; `undefined` otherwise.
|
|
45
46
|
const results = $derived(engine && trimmed ? engine.search(query, undefined, activeVersion) : []);
|
|
46
47
|
|
|
48
|
+
// Empty-state starters: prefer common first pages when present, then fill
|
|
49
|
+
// from the rest of the index so a custom site still has something useful.
|
|
50
|
+
const suggestions = $derived.by(() => {
|
|
51
|
+
const scoped = activeVersion ? indexDocs.filter((d) => d.version === activeVersion) : indexDocs;
|
|
52
|
+
if (!scoped.length) return [];
|
|
53
|
+
const preferred = [/introduction/i, /quick[- ]?start/i, /install/i, /getting started/i];
|
|
54
|
+
const picked: SearchDoc[] = [];
|
|
55
|
+
for (const re of preferred) {
|
|
56
|
+
const hit = scoped.find(
|
|
57
|
+
(d) =>
|
|
58
|
+
!picked.includes(d) && (re.test(d.title) || re.test(d.path) || re.test(d.section ?? ''))
|
|
59
|
+
);
|
|
60
|
+
if (hit) picked.push(hit);
|
|
61
|
+
}
|
|
62
|
+
for (const d of scoped) {
|
|
63
|
+
if (picked.length >= 4) break;
|
|
64
|
+
if (!picked.includes(d)) picked.push(d);
|
|
65
|
+
}
|
|
66
|
+
return picked.slice(0, 4);
|
|
67
|
+
});
|
|
68
|
+
|
|
47
69
|
// Build the index the first time the palette opens; keep it for later opens.
|
|
48
70
|
async function ensureEngine() {
|
|
49
71
|
const version = activeVersion;
|
|
@@ -54,6 +76,7 @@
|
|
|
54
76
|
import('../../search/create-search.js'),
|
|
55
77
|
load(version)
|
|
56
78
|
]);
|
|
79
|
+
indexDocs = docs;
|
|
57
80
|
engine = createSearchEngine(docs);
|
|
58
81
|
engineVersion = version;
|
|
59
82
|
status = 'idle';
|
|
@@ -106,16 +129,48 @@
|
|
|
106
129
|
Search is unavailable right now.
|
|
107
130
|
</div>
|
|
108
131
|
{:else if !trimmed}
|
|
109
|
-
|
|
110
|
-
class="text-muted-foreground
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
{#if suggestions.length}
|
|
133
|
+
<p class="text-muted-foreground px-2.5 pb-1.5 text-xs font-medium tracking-wide">
|
|
134
|
+
Suggested
|
|
135
|
+
</p>
|
|
136
|
+
{#each suggestions as doc (doc.path)}
|
|
137
|
+
<Command.LinkItem
|
|
138
|
+
href={doc.path}
|
|
139
|
+
onSelect={() => select(doc.path)}
|
|
140
|
+
class="group/result mb-0.5 flex items-center gap-3 rounded-lg px-2.5 py-2 aria-selected:bg-accent aria-selected:text-accent-foreground"
|
|
141
|
+
>
|
|
142
|
+
<span
|
|
143
|
+
class="border-border/60 bg-muted/40 text-muted-foreground group-aria-selected/result:border-accent-foreground/20 group-aria-selected/result:text-accent-foreground flex size-8 shrink-0 items-center justify-center rounded-md border"
|
|
144
|
+
>
|
|
145
|
+
<FileText class="size-4" />
|
|
146
|
+
</span>
|
|
147
|
+
<span class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
148
|
+
<span class="truncate font-medium">{doc.title}</span>
|
|
149
|
+
{#if doc.section}
|
|
150
|
+
<span
|
|
151
|
+
class="text-muted-foreground group-aria-selected/result:text-accent-foreground/80 text-xs"
|
|
152
|
+
>
|
|
153
|
+
{doc.section}
|
|
154
|
+
</span>
|
|
155
|
+
{/if}
|
|
156
|
+
</span>
|
|
157
|
+
<CornerDownLeft
|
|
158
|
+
class="text-muted-foreground group-aria-selected/result:text-accent-foreground size-4 shrink-0 opacity-0 group-aria-selected/result:opacity-100"
|
|
159
|
+
/>
|
|
160
|
+
</Command.LinkItem>
|
|
161
|
+
{/each}
|
|
162
|
+
{:else}
|
|
163
|
+
<div
|
|
164
|
+
class="text-muted-foreground flex flex-col items-center gap-3 px-4 py-10 text-center text-sm"
|
|
114
165
|
>
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
166
|
+
<span
|
|
167
|
+
class="bg-muted/60 text-muted-foreground flex size-11 items-center justify-center rounded-full"
|
|
168
|
+
>
|
|
169
|
+
<SearchIcon class="size-5" />
|
|
170
|
+
</span>
|
|
171
|
+
<span>Search across every page of the documentation.</span>
|
|
172
|
+
</div>
|
|
173
|
+
{/if}
|
|
119
174
|
{:else if results.length === 0}
|
|
120
175
|
<div class="text-muted-foreground px-4 py-10 text-center text-sm">
|
|
121
176
|
No results for <span class="text-foreground font-medium">“{trimmed}”</span>.
|
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
import { mode, toggleMode } from 'mode-watcher';
|
|
6
6
|
|
|
7
7
|
const { class: className }: { class?: string } = $props();
|
|
8
|
+
|
|
9
|
+
// Label names the destination mode so the control is self-describing for
|
|
10
|
+
// assistive tech and matches what the icon shows (sun = go light, moon = go dark).
|
|
11
|
+
const label = $derived(mode.current === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
|
|
8
12
|
</script>
|
|
9
13
|
|
|
10
14
|
<Button
|
|
11
15
|
size="icon"
|
|
12
16
|
variant="ghost"
|
|
13
17
|
onclick={toggleMode}
|
|
18
|
+
aria-label={label}
|
|
14
19
|
class="text-muted-foreground hover:text-foreground size-8 {className ?? ''}"
|
|
15
20
|
>
|
|
16
21
|
{#if mode.current === 'dark'}
|
|
@@ -18,5 +23,4 @@
|
|
|
18
23
|
{:else}
|
|
19
24
|
<Moon class="size-4" />
|
|
20
25
|
{/if}
|
|
21
|
-
<span class="sr-only">Toggle theme</span>
|
|
22
26
|
</Button>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
title,
|
|
6
6
|
description,
|
|
7
7
|
actions,
|
|
8
|
+
before,
|
|
8
9
|
children
|
|
9
10
|
}: {
|
|
10
11
|
title: string;
|
|
@@ -12,7 +13,12 @@
|
|
|
12
13
|
description?: string;
|
|
13
14
|
/** Call-to-action buttons. */
|
|
14
15
|
actions?: Snippet;
|
|
15
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Optional content above the heading (e.g. a pre-release note). Placed
|
|
18
|
+
* before the title so the section can still *end* on the actions.
|
|
19
|
+
*/
|
|
20
|
+
before?: Snippet;
|
|
21
|
+
/** Anything to sit below the actions, e.g. a secondary note. */
|
|
16
22
|
children?: Snippet;
|
|
17
23
|
} = $props();
|
|
18
24
|
</script>
|
|
@@ -29,12 +35,18 @@
|
|
|
29
35
|
></div>
|
|
30
36
|
</div>
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
{#if before}
|
|
39
|
+
<div class="mx-auto mb-8 max-w-xl text-left">{@render before()}</div>
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
<h2
|
|
43
|
+
class="font-serif mx-auto max-w-2xl text-3xl font-semibold tracking-tight text-balance sm:text-4xl"
|
|
44
|
+
>
|
|
33
45
|
{title}
|
|
34
46
|
</h2>
|
|
35
47
|
|
|
36
48
|
{#if description}
|
|
37
|
-
<p class="text-
|
|
49
|
+
<p class="text-foreground mx-auto mt-4 max-w-xl text-lg leading-relaxed text-pretty">
|
|
38
50
|
{description}
|
|
39
51
|
</p>
|
|
40
52
|
{/if}
|
|
@@ -5,7 +5,12 @@ type $$ComponentProps = {
|
|
|
5
5
|
description?: string;
|
|
6
6
|
/** Call-to-action buttons. */
|
|
7
7
|
actions?: Snippet;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Optional content above the heading (e.g. a pre-release note). Placed
|
|
10
|
+
* before the title so the section can still *end* on the actions.
|
|
11
|
+
*/
|
|
12
|
+
before?: Snippet;
|
|
13
|
+
/** Anything to sit below the actions, e.g. a secondary note. */
|
|
9
14
|
children?: Snippet;
|
|
10
15
|
};
|
|
11
16
|
declare const Cta: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
{#if title || description}
|
|
30
30
|
<div class="mx-auto max-w-2xl text-center">
|
|
31
31
|
{#if title}
|
|
32
|
-
<h2 class="text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
|
|
32
|
+
<h2 class="font-serif text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
|
|
33
|
+
{title}
|
|
34
|
+
</h2>
|
|
33
35
|
{/if}
|
|
34
36
|
{#if description}
|
|
35
|
-
<p class="text-
|
|
37
|
+
<p class="text-foreground mt-4 text-lg leading-relaxed text-pretty">
|
|
36
38
|
{description}
|
|
37
39
|
</p>
|
|
38
40
|
{/if}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<div class="min-w-0">
|
|
28
28
|
<h3 class="font-semibold">{title}</h3>
|
|
29
29
|
{#if children}
|
|
30
|
-
<p class="text-
|
|
30
|
+
<p class="text-foreground/80 mt-1 text-sm leading-relaxed">{@render children()}</p>
|
|
31
31
|
{/if}
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{/if}
|
|
41
41
|
|
|
42
42
|
<h1
|
|
43
|
-
class="text-5xl font-semibold tracking-[-0.03em] text-balance sm:text-6xl lg:text-[4.25rem] lg:leading-[1.02] {eyebrow
|
|
43
|
+
class="font-serif text-5xl font-semibold tracking-[-0.03em] text-balance sm:text-6xl lg:text-[4.25rem] lg:leading-[1.02] {eyebrow
|
|
44
44
|
? 'mt-6'
|
|
45
45
|
: ''}"
|
|
46
46
|
>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
{#if description}
|
|
51
51
|
<p
|
|
52
|
-
class="text-
|
|
52
|
+
class="text-foreground mt-6 max-w-xl text-lg leading-relaxed text-pretty {media
|
|
53
53
|
? ''
|
|
54
54
|
: 'mx-auto'}"
|
|
55
55
|
>
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
import * as Sheet from '../shadcn/sheet/index.js';
|
|
14
14
|
import { useSearch } from '../../search/context.svelte.js';
|
|
15
15
|
import { useDocsPage } from '../docs-page-context.js';
|
|
16
|
+
import GithubIcon from '../icons/github.svelte';
|
|
16
17
|
import BookOpenText from '@lucide/svelte/icons/book-open-text';
|
|
18
|
+
import List from '@lucide/svelte/icons/list';
|
|
17
19
|
import Menu from '@lucide/svelte/icons/menu';
|
|
18
|
-
import PanelRight from '@lucide/svelte/icons/panel-right';
|
|
19
20
|
import SearchIcon from '@lucide/svelte/icons/search';
|
|
20
21
|
import type { Snippet } from 'svelte';
|
|
21
22
|
|
|
@@ -124,15 +125,32 @@
|
|
|
124
125
|
</Button>
|
|
125
126
|
{/if}
|
|
126
127
|
|
|
128
|
+
{#if config.github}
|
|
129
|
+
<Button
|
|
130
|
+
size="icon"
|
|
131
|
+
variant="ghost"
|
|
132
|
+
target="_blank"
|
|
133
|
+
rel="noopener noreferrer"
|
|
134
|
+
href={config.github}
|
|
135
|
+
class="text-muted-foreground hover:text-foreground size-8"
|
|
136
|
+
>
|
|
137
|
+
<GithubIcon class="size-4" />
|
|
138
|
+
<span class="sr-only">GitHub</span>
|
|
139
|
+
</Button>
|
|
140
|
+
{/if}
|
|
141
|
+
|
|
127
142
|
<ThemeToggle />
|
|
128
143
|
|
|
129
144
|
{#if tocItems.length > 0}
|
|
130
145
|
<Popover.Root bind:open={isTocOpen}>
|
|
131
146
|
<Popover.Trigger>
|
|
132
147
|
{#snippet child({ props })}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
<!-- Labeled (not icon-only) so first-timers find "on this page"
|
|
149
|
+
without guessing PanelRight. Label collapses on the narrowest
|
|
150
|
+
phones so the title still has room. -->
|
|
151
|
+
<Button variant="ghost" size="sm" class="h-8 gap-1.5 px-2" {...props}>
|
|
152
|
+
<List class="size-4 shrink-0" />
|
|
153
|
+
<span class="text-xs font-medium max-[22rem]:sr-only">On this page</span>
|
|
136
154
|
</Button>
|
|
137
155
|
{/snippet}
|
|
138
156
|
</Popover.Trigger>
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
readingTime && view.readingMinutes ? `${view.readingMinutes} min read` : undefined
|
|
152
152
|
);
|
|
153
153
|
|
|
154
|
-
const breadcrumbs = $derived(view.breadcrumbs
|
|
154
|
+
const breadcrumbs = $derived(view.breadcrumbs);
|
|
155
155
|
|
|
156
156
|
// The archived-version banner, or nothing. Its "view this page in <current>"
|
|
157
157
|
// link is the current version's entry in the same list the switcher renders,
|
|
@@ -286,6 +286,10 @@
|
|
|
286
286
|
<PageFeedback
|
|
287
287
|
path={view.pathname}
|
|
288
288
|
onfeedback={typeof feedback === 'function' ? feedback : undefined}
|
|
289
|
+
editHref={view.editHref}
|
|
290
|
+
issueHref={config.github
|
|
291
|
+
? `${config.github}/issues/new?title=${encodeURIComponent(`Feedback: ${view.title}`)}&body=${encodeURIComponent(`Page: ${view.pathname}\n\n<!-- What was unclear or missing? -->\n`)}`
|
|
292
|
+
: undefined}
|
|
289
293
|
/>
|
|
290
294
|
</div>
|
|
291
295
|
{/key}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
href={node.url}
|
|
44
44
|
aria-current={current === node.url ? 'page' : undefined}
|
|
45
45
|
class={cn(
|
|
46
|
-
'hover:text-primary hover:bg-primary/
|
|
46
|
+
'hover:text-primary hover:bg-primary/10 block rounded-md px-2 py-1.5 text-sm transition-colors',
|
|
47
47
|
current === node.url
|
|
48
48
|
? 'text-primary bg-primary/20 font-medium'
|
|
49
49
|
: 'text-muted-foreground'
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
import { Button } from '../shadcn/button/index.js';
|
|
3
3
|
import ThumbsUp from '@lucide/svelte/icons/thumbs-up';
|
|
4
4
|
import ThumbsDown from '@lucide/svelte/icons/thumbs-down';
|
|
5
|
+
import SquarePen from '@lucide/svelte/icons/square-pen';
|
|
6
|
+
import ExternalLink from '@lucide/svelte/icons/external-link';
|
|
5
7
|
|
|
6
8
|
const {
|
|
7
9
|
path,
|
|
8
|
-
onfeedback
|
|
10
|
+
onfeedback,
|
|
11
|
+
editHref,
|
|
12
|
+
issueHref
|
|
9
13
|
}: {
|
|
10
14
|
/** The current page path, passed to the callback so it can attribute votes. */
|
|
11
15
|
path: string;
|
|
12
16
|
/** Called once per page when the reader votes; wire it to your analytics. */
|
|
13
17
|
onfeedback?: (vote: 'up' | 'down', path: string) => void;
|
|
18
|
+
/** "Edit this page" target — offered after a "No" vote when present. */
|
|
19
|
+
editHref?: string;
|
|
20
|
+
/** "Open an issue" target — offered after a "No" vote when present. */
|
|
21
|
+
issueHref?: string;
|
|
14
22
|
} = $props();
|
|
15
23
|
|
|
16
24
|
let vote = $state<'up' | 'down' | null>(null);
|
|
17
25
|
|
|
26
|
+
const hasRecovery = $derived(Boolean(editHref || issueHref));
|
|
27
|
+
|
|
18
28
|
function cast(value: 'up' | 'down') {
|
|
19
29
|
if (vote) return;
|
|
20
30
|
vote = value;
|
|
@@ -23,8 +33,40 @@
|
|
|
23
33
|
</script>
|
|
24
34
|
|
|
25
35
|
<div class="flex flex-col items-center gap-2.5 text-center">
|
|
26
|
-
{#if vote}
|
|
36
|
+
{#if vote === 'up'}
|
|
27
37
|
<p class="text-muted-foreground text-sm">Thanks for your feedback!</p>
|
|
38
|
+
{:else if vote === 'down'}
|
|
39
|
+
<p class="text-muted-foreground text-sm">
|
|
40
|
+
{hasRecovery ? 'Thanks — want to help improve this page?' : 'Thanks for your feedback!'}
|
|
41
|
+
</p>
|
|
42
|
+
{#if hasRecovery}
|
|
43
|
+
<div class="flex flex-wrap items-center justify-center gap-2">
|
|
44
|
+
{#if editHref}
|
|
45
|
+
<Button
|
|
46
|
+
variant="outline"
|
|
47
|
+
size="sm"
|
|
48
|
+
href={editHref}
|
|
49
|
+
target="_blank"
|
|
50
|
+
rel="noopener noreferrer"
|
|
51
|
+
>
|
|
52
|
+
<SquarePen />
|
|
53
|
+
Edit this page
|
|
54
|
+
</Button>
|
|
55
|
+
{/if}
|
|
56
|
+
{#if issueHref}
|
|
57
|
+
<Button
|
|
58
|
+
variant="outline"
|
|
59
|
+
size="sm"
|
|
60
|
+
href={issueHref}
|
|
61
|
+
target="_blank"
|
|
62
|
+
rel="noopener noreferrer"
|
|
63
|
+
>
|
|
64
|
+
<ExternalLink />
|
|
65
|
+
Open an issue
|
|
66
|
+
</Button>
|
|
67
|
+
{/if}
|
|
68
|
+
</div>
|
|
69
|
+
{/if}
|
|
28
70
|
{:else}
|
|
29
71
|
<p class="text-muted-foreground text-sm">Was this page helpful?</p>
|
|
30
72
|
<div class="flex items-center gap-2">
|
|
@@ -3,6 +3,10 @@ type $$ComponentProps = {
|
|
|
3
3
|
path: string;
|
|
4
4
|
/** Called once per page when the reader votes; wire it to your analytics. */
|
|
5
5
|
onfeedback?: (vote: 'up' | 'down', path: string) => void;
|
|
6
|
+
/** "Edit this page" target — offered after a "No" vote when present. */
|
|
7
|
+
editHref?: string;
|
|
8
|
+
/** "Open an issue" target — offered after a "No" vote when present. */
|
|
9
|
+
issueHref?: string;
|
|
6
10
|
};
|
|
7
11
|
declare const PageFeedback: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
12
|
type PageFeedback = ReturnType<typeof PageFeedback>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { NavItem } from '../../core/index.js';
|
|
3
|
+
import ChevronLeft from '@lucide/svelte/icons/chevron-left';
|
|
4
|
+
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
|
3
5
|
|
|
4
6
|
const {
|
|
5
7
|
prev,
|
|
@@ -16,9 +18,14 @@
|
|
|
16
18
|
{#if prev}
|
|
17
19
|
<a
|
|
18
20
|
href={prev.url}
|
|
19
|
-
class="border-border hover:border-primary flex min-w-0 flex-1 flex-col gap-1 rounded-lg border p-4 transition-colors"
|
|
21
|
+
class="border-border hover:border-primary/60 hover:bg-primary/5 group flex min-w-0 flex-1 flex-col gap-1 rounded-lg border p-4 transition-colors"
|
|
20
22
|
>
|
|
21
|
-
<span
|
|
23
|
+
<span
|
|
24
|
+
class="text-muted-foreground group-hover:text-primary flex items-center gap-1 text-xs"
|
|
25
|
+
>
|
|
26
|
+
<ChevronLeft class="size-3.5" aria-hidden="true" />
|
|
27
|
+
Previous
|
|
28
|
+
</span>
|
|
22
29
|
<span class="text-foreground truncate font-medium">{prev.title}</span>
|
|
23
30
|
</a>
|
|
24
31
|
{:else}
|
|
@@ -27,9 +34,14 @@
|
|
|
27
34
|
{#if next}
|
|
28
35
|
<a
|
|
29
36
|
href={next.url}
|
|
30
|
-
class="border-border hover:border-primary ml-auto flex min-w-0 flex-1 flex-col gap-1 rounded-lg border p-4 text-right transition-colors"
|
|
37
|
+
class="border-border hover:border-primary/60 hover:bg-primary/5 group ml-auto flex min-w-0 flex-1 flex-col items-end gap-1 rounded-lg border p-4 text-right transition-colors"
|
|
31
38
|
>
|
|
32
|
-
<span
|
|
39
|
+
<span
|
|
40
|
+
class="text-muted-foreground group-hover:text-primary flex items-center gap-1 text-xs"
|
|
41
|
+
>
|
|
42
|
+
Next
|
|
43
|
+
<ChevronRight class="size-3.5" aria-hidden="true" />
|
|
44
|
+
</span>
|
|
33
45
|
<span class="text-foreground truncate font-medium">{next.title}</span>
|
|
34
46
|
</a>
|
|
35
47
|
{:else}
|
package/dist/core/content.d.ts
CHANGED
|
@@ -25,9 +25,11 @@ export type DocsContentItem = {
|
|
|
25
25
|
sourcePath?: string;
|
|
26
26
|
/**
|
|
27
27
|
* The day (`YYYY-MM-DD`) the page's source file last changed, from git, or
|
|
28
|
-
* from the page's own `lastUpdated` frontmatter when it has one.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* from the page's own `lastUpdated` frontmatter when it has one. Absent when
|
|
29
|
+
* neither source supplies a day — including on a shallow clone, where git
|
|
30
|
+
* dates would be wrong. A calendar day rather than a timestamp: it is
|
|
31
|
+
* rendered as a UTC day, so it reads the same for every reader and matches
|
|
32
|
+
* on the server and the client.
|
|
31
33
|
*/
|
|
32
34
|
lastUpdated?: string;
|
|
33
35
|
/** Estimated reading time in whole minutes (at ~200 wpm), min 1. */
|
package/dist/core/docs-page.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* component that renders them.
|
|
10
10
|
*/
|
|
11
11
|
import type { DocsContentItem } from './content.js';
|
|
12
|
-
import { type NavGroup, type NavItem } from './nav.js';
|
|
12
|
+
import { type Breadcrumb, type NavGroup, type NavItem } from './nav.js';
|
|
13
13
|
import { type ResolvedVersion } from './version.js';
|
|
14
14
|
/** The page's build-time headings, as extracted into the content index. */
|
|
15
15
|
type PageToc = NonNullable<DocsContentItem['toc']>;
|
|
@@ -56,10 +56,11 @@ export type DocsPageView = {
|
|
|
56
56
|
/** The page's nav title, falling back to the site title off the nav tree. */
|
|
57
57
|
title: string;
|
|
58
58
|
/**
|
|
59
|
-
* Breadcrumb
|
|
60
|
-
*
|
|
59
|
+
* Breadcrumb trail: group steps (with a jump URL to the group's first page),
|
|
60
|
+
* then the current page as a title-only final crumb. No component types
|
|
61
|
+
* here — the shell maps this onto its `Crumb` shape.
|
|
61
62
|
*/
|
|
62
|
-
breadcrumbs:
|
|
63
|
+
breadcrumbs: Breadcrumb[];
|
|
63
64
|
/** The page's server-rendered TOC, from {@link entry}. */
|
|
64
65
|
toc: PageToc;
|
|
65
66
|
/**
|
package/dist/core/docs-page.js
CHANGED
|
@@ -24,7 +24,7 @@ export function resolveDocsPage(input) {
|
|
|
24
24
|
const title = pageIndex >= 0 ? flatNav[pageIndex].title : siteTitle;
|
|
25
25
|
const breadcrumbs = navTrail(nav, pathname) ?? [];
|
|
26
26
|
if (pageIndex >= 0)
|
|
27
|
-
breadcrumbs.push(title);
|
|
27
|
+
breadcrumbs.push({ title });
|
|
28
28
|
return {
|
|
29
29
|
pathname,
|
|
30
30
|
activeVersion: active,
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineConfig, type DocsmithConfig, type DocsmithLink, type DocsmithFooterColumn, type DocsmithAnnouncement } from './config.js';
|
|
2
2
|
export type { DocsContentItem, SearchDoc, LlmsDoc } from './content.js';
|
|
3
3
|
export { resolveVersions, activeVersion, currentVersion, currentOnly, scopeContent, mapPathToVersion, type DocsVersion, type DocsVersions, type ResolvedVersion } from './version.js';
|
|
4
|
-
export { navFromContent, flattenNav, navTrail, isNavGroup, type NavItem, type NavGroup, type NavNode } from './nav.js';
|
|
4
|
+
export { navFromContent, flattenNav, navTrail, type Breadcrumb, isNavGroup, type NavItem, type NavGroup, type NavNode } from './nav.js';
|
|
5
5
|
export { resolveDocsPage, type DocsPageView, type VersionLink } from './docs-page.js';
|
package/dist/core/nav.d.ts
CHANGED
|
@@ -26,9 +26,14 @@ export declare function navFromContent(content: DocsContentItem[]): NavGroup[];
|
|
|
26
26
|
* for the prev/next pager.
|
|
27
27
|
*/
|
|
28
28
|
export declare function flattenNav(nodes: NavNode[]): NavItem[];
|
|
29
|
+
/** One step in a breadcrumb trail: a label, and an optional jump target. */
|
|
30
|
+
export type Breadcrumb = {
|
|
31
|
+
title: string;
|
|
32
|
+
url?: string;
|
|
33
|
+
};
|
|
29
34
|
/**
|
|
30
|
-
* The trail of
|
|
31
|
-
*
|
|
32
|
-
*
|
|
35
|
+
* The trail of groups leading to the page at `url` (top group first, excluding
|
|
36
|
+
* the page itself), or `undefined` if the page is not in the tree. Each group
|
|
37
|
+
* step carries a `url` to its first leaf so breadcrumbs are navigable.
|
|
33
38
|
*/
|
|
34
|
-
export declare function navTrail(nodes: NavNode[], url: string):
|
|
39
|
+
export declare function navTrail(nodes: NavNode[], url: string): Breadcrumb[] | undefined;
|
package/dist/core/nav.js
CHANGED
|
@@ -82,16 +82,35 @@ export function flattenNav(nodes) {
|
|
|
82
82
|
return out;
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
85
|
+
* First leaf URL under a group, depth-first. Groups have no page of their own,
|
|
86
|
+
* so breadcrumbs that name a group link to its earliest page (the place a
|
|
87
|
+
* reader lands if they "go up" into that section).
|
|
88
|
+
*/
|
|
89
|
+
function firstLeafUrl(group) {
|
|
90
|
+
for (const child of group.items) {
|
|
91
|
+
if (isNavGroup(child)) {
|
|
92
|
+
const url = firstLeafUrl(child);
|
|
93
|
+
if (url)
|
|
94
|
+
return url;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return child.url;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The trail of groups leading to the page at `url` (top group first, excluding
|
|
104
|
+
* the page itself), or `undefined` if the page is not in the tree. Each group
|
|
105
|
+
* step carries a `url` to its first leaf so breadcrumbs are navigable.
|
|
88
106
|
*/
|
|
89
107
|
export function navTrail(nodes, url) {
|
|
90
108
|
for (const node of nodes) {
|
|
91
109
|
if (isNavGroup(node)) {
|
|
92
110
|
const inner = navTrail(node.items, url);
|
|
93
|
-
if (inner)
|
|
94
|
-
return [node.title, ...inner];
|
|
111
|
+
if (inner) {
|
|
112
|
+
return [{ title: node.title, url: firstLeafUrl(node) }, ...inner];
|
|
113
|
+
}
|
|
95
114
|
}
|
|
96
115
|
else if (node.url === url) {
|
|
97
116
|
return [];
|
package/dist/core/version.d.ts
CHANGED
|
@@ -7,9 +7,18 @@
|
|
|
7
7
|
* anyone edits; superseded releases are frozen copies under their own prefix.
|
|
8
8
|
* See `docs/adr/0001-unprefixed-current-docs.md`. All of it is a no-op when a
|
|
9
9
|
* site declares no versions.
|
|
10
|
+
*
|
|
11
|
+
* **Experimental.** The versioning API sits outside the 1.0 stability promise
|
|
12
|
+
* and may change in a minor until authors have used it for real. An archive
|
|
13
|
+
* freezes content, not dependencies — see
|
|
14
|
+
* `docs/adr/0005-an-archive-freezes-content-not-dependencies.md`.
|
|
10
15
|
*/
|
|
11
16
|
import type { DocsContentItem } from './content.js';
|
|
12
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* One documentation version, declared in the `docsmith()` vite plugin.
|
|
19
|
+
*
|
|
20
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor.
|
|
21
|
+
*/
|
|
13
22
|
export type DocsVersion = {
|
|
14
23
|
/** Stable id. Also the URL/directory segment for an archived version. */
|
|
15
24
|
id: string;
|
|
@@ -23,12 +32,22 @@ export type DocsVersion = {
|
|
|
23
32
|
* the docs root, is served unprefixed, and is the folder you edit. `archived`
|
|
24
33
|
* holds frozen copies of superseded releases, each in `<docs root>/<id>/`, in
|
|
25
34
|
* the order they should appear in the switcher (newest first).
|
|
35
|
+
*
|
|
36
|
+
* Freezes content, not dependencies: prose, samples and URLs stay that
|
|
37
|
+
* version's, while imports keep resolving to whatever the app has installed.
|
|
38
|
+
*
|
|
39
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor until
|
|
40
|
+
* authors have used versioning for real.
|
|
26
41
|
*/
|
|
27
42
|
export type DocsVersions = {
|
|
28
43
|
current: DocsVersion;
|
|
29
44
|
archived?: DocsVersion[];
|
|
30
45
|
};
|
|
31
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* A {@link DocsVersion} with the URL fields the runtime needs, computed at build time.
|
|
48
|
+
*
|
|
49
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor.
|
|
50
|
+
*/
|
|
32
51
|
export type ResolvedVersion = DocsVersion & {
|
|
33
52
|
/** Absolute URL base: the docs root for `current`, `/docs/<id>` for an archive. */
|
|
34
53
|
basePath: string;
|
|
@@ -98,6 +117,9 @@ export declare function currentVersion(versions: ResolvedVersion[]): ResolvedVer
|
|
|
98
117
|
* Keep only the current version's pages, for `sitemap.xml` and `llms.txt`, so
|
|
99
118
|
* search engines and LLMs index one canonical set. A no-op when the site
|
|
100
119
|
* declares no versions. Works over any record carrying `version`.
|
|
120
|
+
*
|
|
121
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor until
|
|
122
|
+
* authors have used versioning for real.
|
|
101
123
|
*/
|
|
102
124
|
export declare function currentOnly<T extends {
|
|
103
125
|
version?: string;
|
package/dist/core/version.js
CHANGED
|
@@ -153,6 +153,9 @@ export function currentVersion(versions) {
|
|
|
153
153
|
* Keep only the current version's pages, for `sitemap.xml` and `llms.txt`, so
|
|
154
154
|
* search engines and LLMs index one canonical set. A no-op when the site
|
|
155
155
|
* declares no versions. Works over any record carrying `version`.
|
|
156
|
+
*
|
|
157
|
+
* @experimental Outside the 1.0 stability promise. May change in a minor until
|
|
158
|
+
* authors have used versioning for real.
|
|
156
159
|
*/
|
|
157
160
|
export function currentOnly(items, versions) {
|
|
158
161
|
const current = currentVersion(versions);
|
package/dist/index.js
CHANGED
|
@@ -31,10 +31,12 @@ export { default as CTA } from './components/landing/cta.svelte';
|
|
|
31
31
|
export { default as Action } from './components/landing/action.svelte';
|
|
32
32
|
// Config
|
|
33
33
|
export { defineConfig } from './core/index.js';
|
|
34
|
-
// Versioned docs
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
34
|
+
// Versioned docs (experimental — outside the 1.0 stability promise). Declare
|
|
35
|
+
// `versions` in the `docsmith()` vite plugin; the resolved manifest is exported
|
|
36
|
+
// from `svelte-docsmith/content` and passed to `DocsShell`. `currentOnly`
|
|
37
|
+
// scopes sitemap.xml / llms.txt to the current version, so archived releases
|
|
38
|
+
// don't compete with it in search results. An archive freezes content, not
|
|
39
|
+
// dependencies.
|
|
38
40
|
export { currentOnly } from './core/index.js';
|
|
39
41
|
// Search engine (framework-agnostic; for building a custom search UI over the
|
|
40
42
|
// generated `svelte-docsmith/search` index)
|
package/dist/theme.css
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
@import 'tw-animate-css';
|
|
17
17
|
@import 'tailwindcss';
|
|
18
18
|
@import '@fontsource-variable/inter';
|
|
19
|
+
@import '@fontsource-variable/source-serif-4';
|
|
20
|
+
@import '@fontsource/jetbrains-mono';
|
|
19
21
|
|
|
20
22
|
/* Generate utilities for the classes used inside this package. */
|
|
21
23
|
@source './';
|
|
@@ -139,9 +141,12 @@
|
|
|
139
141
|
--sidebar-accent-foreground: oklch(0.2101 0.0318 264.6645);
|
|
140
142
|
--sidebar-border: oklch(0.9276 0.0058 264.5313);
|
|
141
143
|
--sidebar-ring: oklch(0.6716 0.1368 48.513);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
/* Fontsource variable packages register as "… Variable"; keep the
|
|
145
|
+
* static family names as fallbacks for consumers who load Google Fonts
|
|
146
|
+
* or system stacks instead. */
|
|
147
|
+
--font-sans: 'Inter Variable', Inter, sans-serif;
|
|
148
|
+
--font-serif: 'Source Serif 4 Variable', 'Source Serif 4', serif;
|
|
149
|
+
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
|
145
150
|
--radius: 0.75rem;
|
|
146
151
|
--shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
|
147
152
|
--shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-docsmith",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "A framework for building beautiful documentation sites with Svelte.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "George Daskalakis",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"repository": {
|
|
68
68
|
"type": "git",
|
|
69
|
-
"url": "https://github.com/geodask/svelte-docsmith.git"
|
|
69
|
+
"url": "git+https://github.com/geodask/svelte-docsmith.git"
|
|
70
70
|
},
|
|
71
71
|
"license": "MIT",
|
|
72
72
|
"peerDependencies": {
|
|
@@ -107,6 +107,8 @@
|
|
|
107
107
|
},
|
|
108
108
|
"dependencies": {
|
|
109
109
|
"@fontsource-variable/inter": "^5.2.8",
|
|
110
|
+
"@fontsource-variable/source-serif-4": "^5.3.0",
|
|
111
|
+
"@fontsource/jetbrains-mono": "^5.3.0",
|
|
110
112
|
"@hbsnow/rehype-sectionize": "^1.0.7",
|
|
111
113
|
"@lucide/svelte": "^1.24.0",
|
|
112
114
|
"@shikijs/transformers": "^3.3.0",
|