reallysimpledocs 1.0.0-beta.3 → 1.0.0-beta.4
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/CHANGELOG.md +35 -0
- package/README.md +10 -8
- package/package.json +10 -2
- package/src/astro/index.js +56 -1
- package/src/css/custom.css +35 -24
- package/src/runtime/components/CommandDialog.astro +19 -2
- package/src/runtime/components/DefaultContentHeader.astro +19 -0
- package/src/runtime/components/DefaultSidebarFooter.astro +2 -0
- package/src/runtime/components/DocsLayout.astro +100 -81
- package/src/runtime/components/Sidebar.astro +4 -0
- package/src/runtime/components/mdx/Callout.astro +28 -5
- package/src/runtime/components/mdx/Preview.astro +2 -1
- package/src/runtime/lib/docs.js +92 -14
- package/src/runtime/lib/macros.js +5 -1
- package/src/runtime/lib/markdown-export.js +200 -0
- package/src/runtime/lib/markdown-exporters/index.js +144 -0
- package/src/runtime/lib/mdx-source.js +19 -0
- package/src/runtime/lib/preview-source-plugin.js +33 -0
- package/src/runtime/lib/site.js +0 -1
- package/src/runtime/pages/doc.astro +3 -1
- package/src/runtime/pages/llms-full.txt.ts +2 -2
- package/src/runtime/pages/llms.txt.ts +2 -2
- package/src/runtime/pages/markdown.ts +2 -2
- package/src/runtime/pages/search-index.ts +2 -2
- package/src/runtime/scripts/copy-code.js +28 -7
- package/src/site/ContentHeader.astro +33 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
## [1.0.0-beta.4] - 2026-06-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added MDX documentation components for callouts, code blocks, code groups, previews, tabs, steps, and cards.
|
|
12
|
+
- Added automatic MDX component availability in docs pages, so common ReallySimpleDocs components do not need page-level imports.
|
|
13
|
+
- Added custom `mdxComponents` registration for project-specific MDX components.
|
|
14
|
+
- Added custom `markdownSerializers` registration so project-specific MDX components can control Markdown, search, and LLM export output.
|
|
15
|
+
- Added Markdown export serializers for ReallySimpleDocs components so generated Markdown, search content, and `llms-full.txt` avoid raw MDX component noise.
|
|
16
|
+
- Added customizable sidebar header, sidebar footer, and content header component overrides.
|
|
17
|
+
- Added breadcrumb rendering from the docs navigation tree.
|
|
18
|
+
- Added Lunr-powered command search with result previews and highlighted matches.
|
|
19
|
+
- Added Basecoat style selection through the ReallySimpleDocs integration config.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Ported ReallySimpleDocs to an Astro integration with generated docs routes, assets, Markdown exports, search index, and Basecoat styling.
|
|
24
|
+
- Updated docs content to use Markdown by default and MDX only for richer component examples.
|
|
25
|
+
- Updated code examples to use inline component children instead of passing source code as props.
|
|
26
|
+
- Changed empty table-of-contents behavior so the right panel is hidden instead of rendering an empty “On this page” section.
|
|
27
|
+
- Changed breadcrumbs to render in the sticky content header for nested pages.
|
|
28
|
+
- Updated the docs overview and component pages to explain component usage, Markdown output, and Basecoat interoperability.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Fixed Shiki code block colors across light and dark mode.
|
|
33
|
+
- Fixed copy-code button layout for regular code blocks and tabbed code blocks.
|
|
34
|
+
- Fixed callout rendering in Markdown and MDX pages.
|
|
35
|
+
- Fixed sidebar and header markup to follow Basecoat component patterns.
|
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A simple documentation system for Astro, built with **Basecoat** and **Tailwind CSS**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
ReallySimpleDocs gives you a shadcn/ui-compatible docs template without a React runtime. It uses Basecoat for the same design language and CSS-variable model, while staying close to plain Astro, Markdown, HTML, CSS, and a small amount of vanilla JavaScript.
|
|
6
|
+
|
|
7
|
+
Docs stay portable: content lives in `docs/`, navigation lives in `docs/docs.json`, and the Astro integration owns the docs UI.
|
|
6
8
|
|
|
7
9
|
## Add to an Astro site
|
|
8
10
|
|
|
@@ -59,9 +61,9 @@ docs/
|
|
|
59
61
|
Welcome to the docs.
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
ReallySimpleDocs injects docs pages, Lunr search, per-page Markdown exports, `llms.txt`, and `llms-full.txt`.
|
|
63
65
|
|
|
64
|
-
Use `.mdx` when a page needs
|
|
66
|
+
Use `.mdx` when a page needs ReallySimpleDocs components. Common ReallySimpleDocs components are available by default, so you do not need to import them in each page:
|
|
65
67
|
|
|
66
68
|
```mdx
|
|
67
69
|
# Button
|
|
@@ -71,7 +73,7 @@ Use `.mdx` when a page needs reallysimpledocs components. Common reallysimpledoc
|
|
|
71
73
|
|
|
72
74
|
## Use Basecoat on the rest of the site
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
ReallySimpleDocs styles and scripts its generated docs pages. For custom Astro pages outside the docs route, such as a landing page or app page, use Basecoat directly in your own layout.
|
|
75
77
|
|
|
76
78
|
Install stays the same:
|
|
77
79
|
|
|
@@ -101,7 +103,7 @@ import "basecoat-css/all.min";
|
|
|
101
103
|
<slot />
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
Use the same Basecoat `style` in your
|
|
106
|
+
Use the same Basecoat `style` in your ReallySimpleDocs config and your site stylesheet if you want the docs and custom pages to match.
|
|
105
107
|
|
|
106
108
|
## Local development (this repo)
|
|
107
109
|
|
|
@@ -114,13 +116,13 @@ npm run dev
|
|
|
114
116
|
|
|
115
117
|
Go to [ReallySimpleDocs.com](https://reallysimpledocs.com).
|
|
116
118
|
|
|
117
|
-
## Support the project
|
|
119
|
+
## Support the project
|
|
118
120
|
|
|
119
|
-
- [Contribute code](/CONTRIBUTING.md)
|
|
121
|
+
- [Contribute code](https://github.com/hunvreus/reallysimpledocs/blob/main/CONTRIBUTING.md)
|
|
120
122
|
- [Report issues](https://github.com/hunvreus/reallysimpledocs/issues)
|
|
121
123
|
- [Sponsor me](https://github.com/sponsors/hunvreus)
|
|
122
124
|
- [Star the project on GitHub](https://github.com/hunvreus/reallysimpledocs)
|
|
123
125
|
|
|
124
126
|
## License
|
|
125
127
|
|
|
126
|
-
[MIT](/LICENSE.md)
|
|
128
|
+
[MIT](https://github.com/hunvreus/reallysimpledocs/blob/main/LICENSE.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reallysimpledocs",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
12
12
|
"README.md",
|
|
13
|
+
"CHANGELOG.md",
|
|
13
14
|
"LICENSE.md"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
"check": "npm run build && npm pack --dry-run && npm run smoke:consumer"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
24
|
+
"@astrojs/markdown-remark": "^7.2.0",
|
|
23
25
|
"@astrojs/mdx": "^6.0.3",
|
|
24
26
|
"@tailwindcss/vite": "4.1.17",
|
|
25
27
|
"astro": "^6.4.6",
|
|
@@ -27,7 +29,13 @@
|
|
|
27
29
|
"lucide-static": "^0.552.0",
|
|
28
30
|
"lunr": "^2.3.9",
|
|
29
31
|
"marked": "^16.4.1",
|
|
30
|
-
"
|
|
32
|
+
"mdast-util-gfm": "^3.1.0",
|
|
33
|
+
"mdast-util-to-markdown": "^2.1.2",
|
|
34
|
+
"remark-gfm": "^4.0.1",
|
|
35
|
+
"remark-mdx": "^3.1.1",
|
|
36
|
+
"remark-parse": "^11.0.0",
|
|
37
|
+
"shiki": "^4.1.0",
|
|
38
|
+
"unified": "^11.0.5"
|
|
31
39
|
},
|
|
32
40
|
"peerDependencies": {
|
|
33
41
|
"basecoat-css": "^1.0.0-beta.2",
|
package/src/astro/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { unified as astroMarkdown } from "@astrojs/markdown-remark";
|
|
4
5
|
import mdx from "@astrojs/mdx";
|
|
5
6
|
import tailwindcss from "@tailwindcss/vite";
|
|
7
|
+
import { previewSourceRemarkPlugin } from "../runtime/lib/preview-source-plugin.js";
|
|
6
8
|
|
|
7
9
|
const basecoatStyles = new Set(["vega", "nova", "maia", "lyra", "mira", "luma", "sera", "rhea"]);
|
|
8
10
|
|
|
@@ -26,6 +28,8 @@ const normalizeArray = (value) => (Array.isArray(value) ? value : value ? [value
|
|
|
26
28
|
|
|
27
29
|
const normalizeImportPath = (filePath) => filePath.replaceAll(path.sep, "/");
|
|
28
30
|
|
|
31
|
+
const normalizeRecord = (value) => (value && typeof value === "object" && !Array.isArray(value) ? value : {});
|
|
32
|
+
|
|
29
33
|
const isInsideDir = (dir, file) => {
|
|
30
34
|
const relative = path.relative(dir, file);
|
|
31
35
|
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
@@ -59,6 +63,18 @@ function collectMdxModules(docsDir) {
|
|
|
59
63
|
return modules;
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
function componentModuleSource(modules) {
|
|
67
|
+
const entries = Object.entries(modules);
|
|
68
|
+
const imports = entries
|
|
69
|
+
.map(([name, moduleId], index) => `import component${index} from ${JSON.stringify(moduleId)};`)
|
|
70
|
+
.join("\n");
|
|
71
|
+
return `${imports}
|
|
72
|
+
export default {
|
|
73
|
+
${entries.map(([name], index) => ` ${JSON.stringify(name)}: component${index},`).join("\n")}
|
|
74
|
+
};
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
|
|
62
78
|
export default function reallySimpleDocs(options = {}) {
|
|
63
79
|
const style = options.style || "vega";
|
|
64
80
|
if (!basecoatStyles.has(style)) {
|
|
@@ -72,6 +88,8 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
72
88
|
style,
|
|
73
89
|
customCss: normalizeArray(options.customCss),
|
|
74
90
|
components: options.components || {},
|
|
91
|
+
mdxComponents: normalizeRecord(options.mdxComponents),
|
|
92
|
+
markdownSerializers: normalizeRecord(options.markdownSerializers),
|
|
75
93
|
routeBase: options.routeBase ?? "/docs",
|
|
76
94
|
assetsBase: options.assetsBase || "/assets",
|
|
77
95
|
};
|
|
@@ -84,17 +102,35 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
84
102
|
const generatedDir = path.join(root, ".astro", "reallysimpledocs");
|
|
85
103
|
const generatedStyles = path.join(generatedDir, "styles.css");
|
|
86
104
|
const docsDir = path.resolve(root, normalizedOptions.docsDir);
|
|
105
|
+
const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
|
|
106
|
+
const markdownSerializers = Object.fromEntries(
|
|
107
|
+
Object.entries(normalizedOptions.markdownSerializers).map(([name, moduleId]) => [
|
|
108
|
+
name,
|
|
109
|
+
normalizeImportPath(resolveUserId(moduleId)),
|
|
110
|
+
]),
|
|
111
|
+
);
|
|
112
|
+
const mdxComponents = Object.fromEntries(
|
|
113
|
+
Object.entries(normalizedOptions.mdxComponents).map(([name, moduleId]) => [
|
|
114
|
+
name,
|
|
115
|
+
normalizeImportPath(resolveUserId(moduleId)),
|
|
116
|
+
]),
|
|
117
|
+
);
|
|
87
118
|
const virtualConfig = {
|
|
88
119
|
...normalizedOptions,
|
|
89
120
|
root,
|
|
90
121
|
docsDir,
|
|
91
122
|
siteFile: normalizedOptions.siteFile ? path.resolve(root, normalizedOptions.siteFile) : null,
|
|
123
|
+
mdxComponents,
|
|
124
|
+
markdownSerializers,
|
|
92
125
|
routeBase: normalizeRouteBase(normalizedOptions.routeBase),
|
|
93
126
|
};
|
|
94
|
-
const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
|
|
95
127
|
const components = {
|
|
128
|
+
ContentHeader:
|
|
129
|
+
normalizedOptions.components.ContentHeader || runtimePath("components/DefaultContentHeader.astro"),
|
|
96
130
|
SidebarHeader:
|
|
97
131
|
normalizedOptions.components.SidebarHeader || runtimePath("components/DefaultSidebarHeader.astro"),
|
|
132
|
+
SidebarFooter:
|
|
133
|
+
normalizedOptions.components.SidebarFooter || runtimePath("components/DefaultSidebarFooter.astro"),
|
|
98
134
|
};
|
|
99
135
|
fs.mkdirSync(generatedDir, { recursive: true });
|
|
100
136
|
fs.writeFileSync(
|
|
@@ -142,6 +178,9 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
142
178
|
}),
|
|
143
179
|
],
|
|
144
180
|
markdown: {
|
|
181
|
+
processor: astroMarkdown({
|
|
182
|
+
remarkPlugins: [previewSourceRemarkPlugin],
|
|
183
|
+
}),
|
|
145
184
|
shikiConfig,
|
|
146
185
|
},
|
|
147
186
|
vite: {
|
|
@@ -170,10 +209,17 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
170
209
|
resolveId(id) {
|
|
171
210
|
if (id === "virtual:reallysimpledocs/config") return "\0virtual:reallysimpledocs/config";
|
|
172
211
|
if (id === "virtual:reallysimpledocs/mdx-pages") return "\0virtual:reallysimpledocs/mdx-pages";
|
|
212
|
+
if (id === "virtual:reallysimpledocs/mdx-components") return "\0virtual:reallysimpledocs/mdx-components";
|
|
173
213
|
if (id === "virtual:reallysimpledocs/styles.css") return generatedStyles;
|
|
174
214
|
if (id === "virtual:reallysimpledocs/components/SidebarHeader") {
|
|
175
215
|
return "\0virtual:reallysimpledocs/components/SidebarHeader";
|
|
176
216
|
}
|
|
217
|
+
if (id === "virtual:reallysimpledocs/components/SidebarFooter") {
|
|
218
|
+
return "\0virtual:reallysimpledocs/components/SidebarFooter";
|
|
219
|
+
}
|
|
220
|
+
if (id === "virtual:reallysimpledocs/components/ContentHeader") {
|
|
221
|
+
return "\0virtual:reallysimpledocs/components/ContentHeader";
|
|
222
|
+
}
|
|
177
223
|
return null;
|
|
178
224
|
},
|
|
179
225
|
load(id) {
|
|
@@ -195,9 +241,18 @@ export async function getMdxPage(slug) {
|
|
|
195
241
|
}
|
|
196
242
|
`;
|
|
197
243
|
}
|
|
244
|
+
if (id === "\0virtual:reallysimpledocs/mdx-components") {
|
|
245
|
+
return componentModuleSource(mdxComponents);
|
|
246
|
+
}
|
|
198
247
|
if (id === "\0virtual:reallysimpledocs/components/SidebarHeader") {
|
|
199
248
|
return `export { default } from ${JSON.stringify(resolveUserId(components.SidebarHeader))};`;
|
|
200
249
|
}
|
|
250
|
+
if (id === "\0virtual:reallysimpledocs/components/SidebarFooter") {
|
|
251
|
+
return `export { default } from ${JSON.stringify(resolveUserId(components.SidebarFooter))};`;
|
|
252
|
+
}
|
|
253
|
+
if (id === "\0virtual:reallysimpledocs/components/ContentHeader") {
|
|
254
|
+
return `export { default } from ${JSON.stringify(resolveUserId(components.ContentHeader))};`;
|
|
255
|
+
}
|
|
201
256
|
return null;
|
|
202
257
|
},
|
|
203
258
|
},
|
package/src/css/custom.css
CHANGED
|
@@ -23,37 +23,40 @@
|
|
|
23
23
|
> h1:first-child {
|
|
24
24
|
@apply hidden;
|
|
25
25
|
}
|
|
26
|
-
p > a,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
th > a {
|
|
26
|
+
> p > a,
|
|
27
|
+
> blockquote a,
|
|
28
|
+
> :is(ol, ul) a,
|
|
29
|
+
> dl a,
|
|
30
|
+
> table a {
|
|
32
31
|
@apply underline underline-offset-2 decoration-muted-foreground/50 hover:decoration-foreground/50 transition-all;
|
|
33
32
|
}
|
|
34
|
-
code:not(pre > code)
|
|
33
|
+
> p code:not(pre > code),
|
|
34
|
+
> blockquote code:not(pre > code),
|
|
35
|
+
> :is(ol, ul) code:not(pre > code),
|
|
36
|
+
> dl code:not(pre > code),
|
|
37
|
+
> table code:not(pre > code) {
|
|
35
38
|
@apply relative rounded-md bg-muted px-[0.3rem] py-[0.2rem] font-mono text-[13px];
|
|
36
39
|
}
|
|
37
|
-
pre {
|
|
40
|
+
> pre {
|
|
38
41
|
@apply rounded-lg;
|
|
39
42
|
}
|
|
40
|
-
pre > code {
|
|
43
|
+
> pre > code {
|
|
41
44
|
@apply block min-w-full w-max px-4 py-3 text-[13px];
|
|
42
45
|
}
|
|
43
|
-
pre:has(> code) {
|
|
46
|
+
> pre:has(> code) {
|
|
44
47
|
@apply max-w-full overflow-x-auto;
|
|
45
48
|
}
|
|
46
|
-
blockquote {
|
|
49
|
+
> blockquote {
|
|
47
50
|
@apply border-l-4 border-border pl-4;
|
|
48
51
|
}
|
|
49
|
-
ol {
|
|
52
|
+
> ol {
|
|
50
53
|
@apply list-decimal ml-6;
|
|
51
54
|
}
|
|
52
|
-
ul {
|
|
55
|
+
> ul {
|
|
53
56
|
@apply list-disc ml-6;
|
|
54
57
|
}
|
|
55
|
-
ol,
|
|
56
|
-
ul {
|
|
58
|
+
> ol,
|
|
59
|
+
> ul {
|
|
57
60
|
li {
|
|
58
61
|
@apply mt-2;
|
|
59
62
|
|
|
@@ -63,7 +66,7 @@
|
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
|
-
dl {
|
|
69
|
+
> dl {
|
|
67
70
|
@apply border-b pb-3 my-6;
|
|
68
71
|
dt {
|
|
69
72
|
@apply border-t pt-3 mt-3 first:mt-0;
|
|
@@ -75,16 +78,24 @@
|
|
|
75
78
|
@apply mt-4 border-b-0 pb-0 pl-8 my-0;
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
|
-
b,
|
|
79
|
-
strong
|
|
81
|
+
> p b,
|
|
82
|
+
> p strong,
|
|
83
|
+
> blockquote b,
|
|
84
|
+
> blockquote strong,
|
|
85
|
+
> :is(ol, ul) b,
|
|
86
|
+
> :is(ol, ul) strong,
|
|
87
|
+
> dl b,
|
|
88
|
+
> dl strong,
|
|
89
|
+
> table b,
|
|
90
|
+
> table strong {
|
|
80
91
|
@apply font-semibold;
|
|
81
92
|
}
|
|
82
93
|
> p,
|
|
83
|
-
blockquote,
|
|
84
|
-
ul,
|
|
85
|
-
ol,
|
|
86
|
-
dl,
|
|
87
|
-
pre {
|
|
94
|
+
> blockquote,
|
|
95
|
+
> ul,
|
|
96
|
+
> ol,
|
|
97
|
+
> dl,
|
|
98
|
+
> pre {
|
|
88
99
|
@apply my-6;
|
|
89
100
|
}
|
|
90
101
|
> *:first-child {
|
|
@@ -93,7 +104,7 @@
|
|
|
93
104
|
> *:last-child {
|
|
94
105
|
@apply !mb-0;
|
|
95
106
|
}
|
|
96
|
-
table {
|
|
107
|
+
> table {
|
|
97
108
|
@apply w-full caption-bottom text-sm;
|
|
98
109
|
thead {
|
|
99
110
|
@apply [&_tr]:border-b;
|
|
@@ -16,7 +16,7 @@ const { indexUrl } = Astro.props;
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<script>
|
|
19
|
-
import
|
|
19
|
+
import lunrUrl from "lunr/lunr.js?url";
|
|
20
20
|
|
|
21
21
|
const root = document.querySelector("[data-search]");
|
|
22
22
|
const indexUrl = root?.getAttribute("data-index-url");
|
|
@@ -26,6 +26,7 @@ const { indexUrl } = Astro.props;
|
|
|
26
26
|
const menu = dialog?.querySelector('[role="menu"]');
|
|
27
27
|
const state = {
|
|
28
28
|
loadPromise: null,
|
|
29
|
+
lunrPromise: null,
|
|
29
30
|
index: null,
|
|
30
31
|
documents: [],
|
|
31
32
|
bySlug: new Map(),
|
|
@@ -197,6 +198,21 @@ const { indexUrl } = Astro.props;
|
|
|
197
198
|
refreshCommand();
|
|
198
199
|
}
|
|
199
200
|
|
|
201
|
+
function loadLunr() {
|
|
202
|
+
if (window.lunr) return Promise.resolve(window.lunr);
|
|
203
|
+
if (!state.lunrPromise) {
|
|
204
|
+
state.lunrPromise = new Promise((resolve, reject) => {
|
|
205
|
+
const script = document.createElement("script");
|
|
206
|
+
script.src = lunrUrl;
|
|
207
|
+
script.async = true;
|
|
208
|
+
script.onload = () => (window.lunr ? resolve(window.lunr) : reject(new Error("Failed to load lunr.")));
|
|
209
|
+
script.onerror = () => reject(new Error("Failed to load lunr."));
|
|
210
|
+
document.head.appendChild(script);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return state.lunrPromise;
|
|
214
|
+
}
|
|
215
|
+
|
|
200
216
|
async function loadIndex() {
|
|
201
217
|
if (state.index) return state;
|
|
202
218
|
if (!indexUrl) throw new Error("Missing reallysimpledocs search index URL.");
|
|
@@ -204,7 +220,8 @@ const { indexUrl } = Astro.props;
|
|
|
204
220
|
renderLoading();
|
|
205
221
|
state.loadPromise = fetch(indexUrl)
|
|
206
222
|
.then((response) => response.json())
|
|
207
|
-
.then((data) => {
|
|
223
|
+
.then(async (data) => {
|
|
224
|
+
const lunr = await loadLunr();
|
|
208
225
|
state.documents = data.documents || [];
|
|
209
226
|
state.bySlug = new Map(state.documents.map((doc) => [doc.slug, doc]));
|
|
210
227
|
state.index = lunr.Index.load(data.index);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Moon from "lucide-static/icons/moon.svg?raw";
|
|
3
|
+
import Sun from "lucide-static/icons/sun.svg?raw";
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<nav class="hidden items-center gap-2 sm:flex">
|
|
7
|
+
<button
|
|
8
|
+
type="button"
|
|
9
|
+
aria-label="Toggle dark mode"
|
|
10
|
+
data-tooltip="Toggle dark mode"
|
|
11
|
+
data-side="bottom"
|
|
12
|
+
data-align="end"
|
|
13
|
+
onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))"
|
|
14
|
+
class="btn-sm-icon-ghost"
|
|
15
|
+
>
|
|
16
|
+
<span class="hidden dark:block"><Fragment set:html={Sun} /></span>
|
|
17
|
+
<span class="block dark:hidden"><Fragment set:html={Moon} /></span>
|
|
18
|
+
</button>
|
|
19
|
+
</nav>
|
|
@@ -4,12 +4,11 @@ import ArrowLeft from "lucide-static/icons/arrow-left.svg?raw";
|
|
|
4
4
|
import ArrowRight from "lucide-static/icons/arrow-right.svg?raw";
|
|
5
5
|
import Check from "lucide-static/icons/check.svg?raw";
|
|
6
6
|
import Copy from "lucide-static/icons/copy.svg?raw";
|
|
7
|
-
import Menu from "lucide-static/icons/menu.svg?raw";
|
|
8
|
-
import Moon from "lucide-static/icons/moon.svg?raw";
|
|
9
7
|
import PanelLeft from "lucide-static/icons/panel-left.svg?raw";
|
|
10
8
|
import Search from "lucide-static/icons/search.svg?raw";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
9
|
+
import ChevronRight from "lucide-static/icons/chevron-right.svg?raw";
|
|
10
|
+
import Ellipsis from "lucide-static/icons/ellipsis.svg?raw";
|
|
11
|
+
import ContentHeader from "virtual:reallysimpledocs/components/ContentHeader";
|
|
13
12
|
import ClaudeLogo from "../../icons/logos/claude.svg?raw";
|
|
14
13
|
import CursorLogo from "../../icons/logos/cursor.svg?raw";
|
|
15
14
|
import MarkdownLogo from "../../icons/logos/markdown.svg?raw";
|
|
@@ -18,18 +17,36 @@ import BaseLayout from "./BaseLayout.astro";
|
|
|
18
17
|
import CommandDialog from "./CommandDialog.astro";
|
|
19
18
|
import DropdownMenu from "./DropdownMenu.astro";
|
|
20
19
|
import Sidebar from "./Sidebar.astro";
|
|
21
|
-
import { buildMenus, getNavigation } from "../lib/docs.js";
|
|
20
|
+
import { buildMenus, getBreadcrumbs, getNavigation } from "../lib/docs.js";
|
|
22
21
|
import { addSvgClass } from "../lib/html.js";
|
|
22
|
+
import { renderDropdownMenu } from "../lib/macros.js";
|
|
23
23
|
import { routeFilePath } from "../lib/paths.js";
|
|
24
24
|
import { getSite } from "../lib/site.js";
|
|
25
25
|
|
|
26
26
|
const { page, headings } = Astro.props;
|
|
27
27
|
const site = getSite(config);
|
|
28
28
|
const navigation = getNavigation(config, page);
|
|
29
|
+
const breadcrumbs = getBreadcrumbs(config, page);
|
|
29
30
|
const { sidebar } = buildMenus(config, page.slug);
|
|
31
|
+
const hasToc = headings.length > 0;
|
|
30
32
|
const searchIndexUrl = routeFilePath(config.routeBase, "search-index.json");
|
|
33
|
+
const chevronRightIcon = addSvgClass(ChevronRight, "size-3.5");
|
|
34
|
+
const ellipsisIcon = addSvgClass(Ellipsis, "size-4");
|
|
31
35
|
const panelLeftIcon = addSvgClass(PanelLeft, "size-4");
|
|
32
36
|
const searchIcon = addSvgClass(Search, "size-4");
|
|
37
|
+
const breadcrumbItems =
|
|
38
|
+
breadcrumbs.length < 2
|
|
39
|
+
? []
|
|
40
|
+
: breadcrumbs.length > 3
|
|
41
|
+
? [
|
|
42
|
+
breadcrumbs[0],
|
|
43
|
+
{
|
|
44
|
+
type: "dropdown",
|
|
45
|
+
items: breadcrumbs.slice(1, -1),
|
|
46
|
+
},
|
|
47
|
+
breadcrumbs.at(-1),
|
|
48
|
+
]
|
|
49
|
+
: breadcrumbs;
|
|
33
50
|
const siteUrl = Astro.site?.toString().replace(/\/$/, "") || site.url || "";
|
|
34
51
|
const pageUrl = `${siteUrl}${page.path}`;
|
|
35
52
|
const llmPrompt = `Read from ${pageUrl} so I can ask questions about it.`;
|
|
@@ -84,74 +101,74 @@ const actionItems = [
|
|
|
84
101
|
<Fragment set:html={panelLeftIcon} />
|
|
85
102
|
</button>
|
|
86
103
|
|
|
104
|
+
{
|
|
105
|
+
breadcrumbItems.length > 0 && (
|
|
106
|
+
<nav class="hidden min-w-0 overflow-visible md:block" aria-label="Breadcrumb">
|
|
107
|
+
<ol class="flex min-w-0 items-center gap-1.5 overflow-visible text-sm text-muted-foreground">
|
|
108
|
+
{breadcrumbItems.map((crumb, index) => {
|
|
109
|
+
const current = index === breadcrumbItems.length - 1;
|
|
110
|
+
return (
|
|
111
|
+
<>
|
|
112
|
+
{index > 0 && (
|
|
113
|
+
<li class="shrink-0" aria-hidden="true">
|
|
114
|
+
<Fragment set:html={chevronRightIcon} />
|
|
115
|
+
</li>
|
|
116
|
+
)}
|
|
117
|
+
<li class="inline-flex min-w-0 items-center gap-1.5 overflow-visible">
|
|
118
|
+
{crumb.type === "dropdown" ? (
|
|
119
|
+
<Fragment
|
|
120
|
+
set:html={renderDropdownMenu({
|
|
121
|
+
id: `breadcrumb-${page.slug.replace(/[^a-z0-9_-]/gi, "-")}`,
|
|
122
|
+
trigger: ellipsisIcon,
|
|
123
|
+
items: crumb.items.map((item) => ({
|
|
124
|
+
label: item.label,
|
|
125
|
+
url: item.url,
|
|
126
|
+
})),
|
|
127
|
+
triggerAttrs: {
|
|
128
|
+
class: "flex size-4 items-center justify-center hover:text-foreground cursor-pointer",
|
|
129
|
+
"aria-label": "Show breadcrumb pages",
|
|
130
|
+
},
|
|
131
|
+
popoverAttrs: { "data-align": "start" },
|
|
132
|
+
})}
|
|
133
|
+
/>
|
|
134
|
+
) : current || !crumb.url ? (
|
|
135
|
+
<span class="truncate font-medium text-foreground">{crumb.label}</span>
|
|
136
|
+
) : (
|
|
137
|
+
<a href={crumb.url} class="truncate transition-colors hover:text-foreground">
|
|
138
|
+
{crumb.label}
|
|
139
|
+
</a>
|
|
140
|
+
)}
|
|
141
|
+
</li>
|
|
142
|
+
</>
|
|
143
|
+
);
|
|
144
|
+
})}
|
|
145
|
+
</ol>
|
|
146
|
+
</nav>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
87
150
|
<button
|
|
88
151
|
type="button"
|
|
89
|
-
class="
|
|
152
|
+
class="btn-sm-icon-ghost ml-auto sm:hidden"
|
|
153
|
+
aria-label="Search"
|
|
154
|
+
data-tooltip="Search"
|
|
155
|
+
data-side="bottom"
|
|
90
156
|
onclick="window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal()"
|
|
91
157
|
>
|
|
92
158
|
<Fragment set:html={searchIcon} />
|
|
93
|
-
Search the docs...
|
|
94
|
-
<kbd class="kbd ml-auto -mr-1.5">⌘K</kbd>
|
|
95
159
|
</button>
|
|
96
160
|
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
target={link.attrs?.target}
|
|
107
|
-
rel={link.attrs?.rel}
|
|
108
|
-
>
|
|
109
|
-
{link.icon && <Fragment set:html={link.icon} />}
|
|
110
|
-
{!link.iconOnly && link.label}
|
|
111
|
-
</a>
|
|
112
|
-
))
|
|
113
|
-
}
|
|
114
|
-
<button
|
|
115
|
-
type="button"
|
|
116
|
-
aria-label="Toggle dark mode"
|
|
117
|
-
data-tooltip="Toggle dark mode"
|
|
118
|
-
data-side="bottom"
|
|
119
|
-
data-align="end"
|
|
120
|
-
onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))"
|
|
121
|
-
class="btn-sm-icon-outline"
|
|
122
|
-
>
|
|
123
|
-
<span class="hidden dark:block"><Fragment set:html={Sun} /></span>
|
|
124
|
-
<span class="block dark:hidden"><Fragment set:html={Moon} /></span>
|
|
125
|
-
</button>
|
|
126
|
-
</nav>
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
class="relative hidden h-8 w-full min-w-0 max-w-72 cursor-text items-center gap-x-2 rounded-md border border-input bg-background px-3 py-1 text-base text-muted-foreground outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:bg-input/30 sm:ml-auto sm:flex md:text-sm"
|
|
164
|
+
onclick="window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal()"
|
|
165
|
+
>
|
|
166
|
+
<Fragment set:html={searchIcon} />
|
|
167
|
+
Search...
|
|
168
|
+
<kbd class="kbd ml-auto -mr-1.5">⌘K</kbd>
|
|
169
|
+
</button>
|
|
127
170
|
|
|
128
|
-
<
|
|
129
|
-
<button
|
|
130
|
-
type="button"
|
|
131
|
-
class="btn-sm-icon-ghost peer group"
|
|
132
|
-
aria-label="Toggle menu"
|
|
133
|
-
aria-expanded="false"
|
|
134
|
-
aria-controls="mobile-menu"
|
|
135
|
-
onclick="this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') === 'true' ? 'false' : 'true')"
|
|
136
|
-
>
|
|
137
|
-
<span class="group-aria-expanded:hidden"><Fragment set:html={Menu} /></span>
|
|
138
|
-
<span class="hidden group-aria-expanded:block"><Fragment set:html={X} /></span>
|
|
139
|
-
</button>
|
|
140
|
-
<nav
|
|
141
|
-
id="mobile-menu"
|
|
142
|
-
class="fixed inset-0 top-14 z-50 flex invisible -translate-y-16 scale-95 flex-col gap-2 bg-background p-4 opacity-0 transition-all pointer-events-none peer-aria-expanded:visible peer-aria-expanded:translate-y-0 peer-aria-expanded:scale-100 peer-aria-expanded:opacity-100 peer-aria-expanded:pointer-events-auto"
|
|
143
|
-
data-mobile-menu
|
|
144
|
-
>
|
|
145
|
-
{
|
|
146
|
-
(site.links || []).map((link) => (
|
|
147
|
-
<a class="btn-lg-ghost justify-between" href={link.url} target={link.attrs?.target} rel={link.attrs?.rel}>
|
|
148
|
-
<span>{link.label}</span>
|
|
149
|
-
{link.icon && <Fragment set:html={link.icon} />}
|
|
150
|
-
</a>
|
|
151
|
-
))
|
|
152
|
-
}
|
|
153
|
-
</nav>
|
|
154
|
-
</div>
|
|
171
|
+
<ContentHeader site={site} page={page} navigation={navigation} config={config} />
|
|
155
172
|
</div>
|
|
156
173
|
</header>
|
|
157
174
|
|
|
@@ -221,22 +238,24 @@ const actionItems = [
|
|
|
221
238
|
</footer>
|
|
222
239
|
</div>
|
|
223
240
|
</article>
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
241
|
+
{
|
|
242
|
+
hasToc && (
|
|
243
|
+
<aside class="hidden w-56 xl:block">
|
|
244
|
+
<div class="docs-toc sticky top-20 space-y-2 text-sm [&_a]:text-muted-foreground [&_a]:hover:text-foreground [&_a[data-active='true']]:text-foreground [&_ol]:space-y-2 [&_ol_ol]:mt-2 [&_ol_ol]:pl-3" data-docs-toc>
|
|
245
|
+
<h2 class="flex items-center gap-1 font-medium text-muted-foreground">On this page</h2>
|
|
246
|
+
<ol>
|
|
247
|
+
{headings.map((heading) => (
|
|
248
|
+
<li class={heading.depth === 3 ? "pl-3" : ""}>
|
|
249
|
+
<a href={`#${heading.id}`} data-toc-link>
|
|
250
|
+
{heading.text}
|
|
251
|
+
</a>
|
|
252
|
+
</li>
|
|
253
|
+
))}
|
|
254
|
+
</ol>
|
|
255
|
+
</div>
|
|
256
|
+
</aside>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
240
259
|
</div>
|
|
241
260
|
</main>
|
|
242
261
|
|