reallysimpledocs 1.0.0-beta.2 → 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.
Files changed (36) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +11 -11
  3. package/package.json +10 -2
  4. package/src/astro/index.js +94 -3
  5. package/src/components/index.js +0 -2
  6. package/src/css/custom.css +45 -205
  7. package/src/runtime/components/CommandDialog.astro +20 -3
  8. package/src/runtime/components/DefaultContentHeader.astro +19 -0
  9. package/src/runtime/components/DefaultSidebarFooter.astro +2 -0
  10. package/src/runtime/components/DocsLayout.astro +100 -81
  11. package/src/runtime/components/Sidebar.astro +4 -0
  12. package/src/runtime/components/mdx/Callout.astro +36 -26
  13. package/src/runtime/components/mdx/Card.astro +8 -7
  14. package/src/runtime/components/mdx/CardGrid.astro +1 -1
  15. package/src/runtime/components/mdx/Code.astro +33 -13
  16. package/src/runtime/components/mdx/CodeGroup.astro +38 -14
  17. package/src/runtime/components/mdx/Preview.astro +16 -6
  18. package/src/runtime/components/mdx/Steps.astro +5 -1
  19. package/src/runtime/components/mdx/Tabs.astro +1 -1
  20. package/src/runtime/lib/docs.js +107 -36
  21. package/src/runtime/lib/highlight.js +27 -0
  22. package/src/runtime/lib/macros.js +5 -1
  23. package/src/runtime/lib/markdown-export.js +200 -0
  24. package/src/runtime/lib/markdown-exporters/index.js +144 -0
  25. package/src/runtime/lib/mdx-source.js +19 -0
  26. package/src/runtime/lib/preview-source-plugin.js +33 -0
  27. package/src/runtime/lib/site.js +0 -1
  28. package/src/runtime/pages/doc.astro +4 -1
  29. package/src/runtime/pages/llms-full.txt.ts +2 -2
  30. package/src/runtime/pages/llms.txt.ts +2 -2
  31. package/src/runtime/pages/markdown.ts +2 -2
  32. package/src/runtime/pages/search-index.ts +2 -2
  33. package/src/runtime/scripts/copy-code.js +51 -6
  34. package/src/site/ContentHeader.astro +33 -0
  35. package/src/runtime/components/mdx/Badge.astro +0 -15
  36. package/src/runtime/components/mdx/LinkButton.astro +0 -14
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
- RSD keeps docs portable: content lives in `docs/`, navigation lives in `docs/docs.json`, and the Astro integration owns the docs UI.
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,21 +61,19 @@ docs/
59
61
  Welcome to the docs.
60
62
  ```
61
63
 
62
- RSD injects docs pages, Lunr search, per-page Markdown exports, `llms.txt`, and `llms-full.txt`.
64
+ ReallySimpleDocs injects docs pages, Lunr search, per-page Markdown exports, `llms.txt`, and `llms-full.txt`.
63
65
 
64
- Use `.md` for normal pages and `.mdx` when a page needs Astro components:
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
- import { Callout, Preview } from "reallysimpledocs/components";
68
-
69
69
  # Button
70
70
 
71
- <Callout type="tip">MDX is optional. Plain Markdown stays the default.</Callout>
71
+ <Callout title="MDX works">MDX is optional. Plain Markdown stays the default.</Callout>
72
72
  ```
73
73
 
74
74
  ## Use Basecoat on the rest of the site
75
75
 
76
- RSD styles and scripts its generated docs pages. For custom Astro pages outside the docs route, use Basecoat directly in your own layout.
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.
77
77
 
78
78
  Install stays the same:
79
79
 
@@ -103,7 +103,7 @@ import "basecoat-css/all.min";
103
103
  <slot />
104
104
  ```
105
105
 
106
- Use the same Basecoat `style` in your RSD config and your site stylesheet if you want the docs and custom pages to match.
106
+ Use the same Basecoat `style` in your ReallySimpleDocs config and your site stylesheet if you want the docs and custom pages to match.
107
107
 
108
108
  ## Local development (this repo)
109
109
 
@@ -116,13 +116,13 @@ npm run dev
116
116
 
117
117
  Go to [ReallySimpleDocs.com](https://reallysimpledocs.com).
118
118
 
119
- ## Support the project
119
+ ## Support the project
120
120
 
121
- - [Contribute code](/CONTRIBUTING.md)
121
+ - [Contribute code](https://github.com/hunvreus/reallysimpledocs/blob/main/CONTRIBUTING.md)
122
122
  - [Report issues](https://github.com/hunvreus/reallysimpledocs/issues)
123
123
  - [Sponsor me](https://github.com/sponsors/hunvreus)
124
124
  - [Star the project on GitHub](https://github.com/hunvreus/reallysimpledocs)
125
125
 
126
126
  ## License
127
127
 
128
- [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.2",
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
- "shiki": "^4.1.0"
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",
@@ -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,21 @@ 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
+
33
+ const isInsideDir = (dir, file) => {
34
+ const relative = path.relative(dir, file);
35
+ return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
36
+ };
37
+
38
+ const shikiConfig = {
39
+ themes: {
40
+ light: "github-light",
41
+ dark: "github-dark",
42
+ },
43
+ defaultColor: false,
44
+ };
45
+
29
46
  function collectMdxModules(docsDir) {
30
47
  if (!fs.existsSync(docsDir)) return {};
31
48
  const modules = {};
@@ -46,6 +63,18 @@ function collectMdxModules(docsDir) {
46
63
  return modules;
47
64
  }
48
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
+
49
78
  export default function reallySimpleDocs(options = {}) {
50
79
  const style = options.style || "vega";
51
80
  if (!basecoatStyles.has(style)) {
@@ -59,6 +88,8 @@ export default function reallySimpleDocs(options = {}) {
59
88
  style,
60
89
  customCss: normalizeArray(options.customCss),
61
90
  components: options.components || {},
91
+ mdxComponents: normalizeRecord(options.mdxComponents),
92
+ markdownSerializers: normalizeRecord(options.markdownSerializers),
62
93
  routeBase: options.routeBase ?? "/docs",
63
94
  assetsBase: options.assetsBase || "/assets",
64
95
  };
@@ -71,18 +102,35 @@ export default function reallySimpleDocs(options = {}) {
71
102
  const generatedDir = path.join(root, ".astro", "reallysimpledocs");
72
103
  const generatedStyles = path.join(generatedDir, "styles.css");
73
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
+ );
74
118
  const virtualConfig = {
75
119
  ...normalizedOptions,
76
120
  root,
77
121
  docsDir,
78
122
  siteFile: normalizedOptions.siteFile ? path.resolve(root, normalizedOptions.siteFile) : null,
123
+ mdxComponents,
124
+ markdownSerializers,
79
125
  routeBase: normalizeRouteBase(normalizedOptions.routeBase),
80
126
  };
81
- const mdxModules = collectMdxModules(docsDir);
82
- const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
83
127
  const components = {
128
+ ContentHeader:
129
+ normalizedOptions.components.ContentHeader || runtimePath("components/DefaultContentHeader.astro"),
84
130
  SidebarHeader:
85
131
  normalizedOptions.components.SidebarHeader || runtimePath("components/DefaultSidebarHeader.astro"),
132
+ SidebarFooter:
133
+ normalizedOptions.components.SidebarFooter || runtimePath("components/DefaultSidebarFooter.astro"),
86
134
  };
87
135
  fs.mkdirSync(generatedDir, { recursive: true });
88
136
  fs.writeFileSync(
@@ -123,7 +171,18 @@ export default function reallySimpleDocs(options = {}) {
123
171
  });
124
172
 
125
173
  updateConfig({
126
- integrations: [mdx()],
174
+ integrations: [
175
+ mdx({
176
+ syntaxHighlight: "shiki",
177
+ shikiConfig,
178
+ }),
179
+ ],
180
+ markdown: {
181
+ processor: astroMarkdown({
182
+ remarkPlugins: [previewSourceRemarkPlugin],
183
+ }),
184
+ shikiConfig,
185
+ },
127
186
  vite: {
128
187
  build: {
129
188
  cssMinify: false,
@@ -132,18 +191,41 @@ export default function reallySimpleDocs(options = {}) {
132
191
  tailwindcss(),
133
192
  {
134
193
  name: "reallysimpledocs-virtual-modules",
194
+ configureServer(server) {
195
+ const invalidateMdxPages = () => {
196
+ const module = server.moduleGraph.getModuleById("\0virtual:reallysimpledocs/mdx-pages");
197
+ if (module) server.moduleGraph.invalidateModule(module);
198
+ server.ws.send({ type: "full-reload" });
199
+ };
200
+
201
+ server.watcher.add(path.join(docsDir, "**/*.mdx"));
202
+ server.watcher.on("add", (file) => {
203
+ if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
204
+ });
205
+ server.watcher.on("unlink", (file) => {
206
+ if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
207
+ });
208
+ },
135
209
  resolveId(id) {
136
210
  if (id === "virtual:reallysimpledocs/config") return "\0virtual:reallysimpledocs/config";
137
211
  if (id === "virtual:reallysimpledocs/mdx-pages") return "\0virtual:reallysimpledocs/mdx-pages";
212
+ if (id === "virtual:reallysimpledocs/mdx-components") return "\0virtual:reallysimpledocs/mdx-components";
138
213
  if (id === "virtual:reallysimpledocs/styles.css") return generatedStyles;
139
214
  if (id === "virtual:reallysimpledocs/components/SidebarHeader") {
140
215
  return "\0virtual:reallysimpledocs/components/SidebarHeader";
141
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
+ }
142
223
  return null;
143
224
  },
144
225
  load(id) {
145
226
  if (id === "\0virtual:reallysimpledocs/config") return `export default ${JSON.stringify(virtualConfig)};`;
146
227
  if (id === "\0virtual:reallysimpledocs/mdx-pages") {
228
+ const mdxModules = collectMdxModules(docsDir);
147
229
  const imports = Object.entries(mdxModules)
148
230
  .map(([slug, filePath], index) => `import * as mdx${index} from ${JSON.stringify(filePath)};`)
149
231
  .join("\n");
@@ -159,9 +241,18 @@ export async function getMdxPage(slug) {
159
241
  }
160
242
  `;
161
243
  }
244
+ if (id === "\0virtual:reallysimpledocs/mdx-components") {
245
+ return componentModuleSource(mdxComponents);
246
+ }
162
247
  if (id === "\0virtual:reallysimpledocs/components/SidebarHeader") {
163
248
  return `export { default } from ${JSON.stringify(resolveUserId(components.SidebarHeader))};`;
164
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
+ }
165
256
  return null;
166
257
  },
167
258
  },
@@ -2,13 +2,11 @@ export { default as DocsLayout } from "../runtime/components/DocsLayout.astro";
2
2
  export { default as CommandDialog } from "../runtime/components/CommandDialog.astro";
3
3
  export { default as DropdownMenu } from "../runtime/components/DropdownMenu.astro";
4
4
  export { default as Sidebar } from "../runtime/components/Sidebar.astro";
5
- export { default as Badge } from "../runtime/components/mdx/Badge.astro";
6
5
  export { default as Callout } from "../runtime/components/mdx/Callout.astro";
7
6
  export { default as Card } from "../runtime/components/mdx/Card.astro";
8
7
  export { default as CardGrid } from "../runtime/components/mdx/CardGrid.astro";
9
8
  export { default as Code } from "../runtime/components/mdx/Code.astro";
10
9
  export { default as CodeGroup } from "../runtime/components/mdx/CodeGroup.astro";
11
- export { default as LinkButton } from "../runtime/components/mdx/LinkButton.astro";
12
10
  export { default as Preview } from "../runtime/components/mdx/Preview.astro";
13
11
  export { default as Step } from "../runtime/components/mdx/Step.astro";
14
12
  export { default as Steps } from "../runtime/components/mdx/Steps.astro";
@@ -23,37 +23,40 @@
23
23
  > h1:first-child {
24
24
  @apply hidden;
25
25
  }
26
- p > a,
27
- li > a,
28
- dt > a,
29
- dd > a,
30
- td > a,
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;
@@ -119,20 +130,25 @@
119
130
  }
120
131
  }
121
132
 
122
- .shiki {
123
- @apply bg-muted dark:bg-muted/50;
133
+ .shiki,
134
+ .astro-code {
135
+ @apply bg-muted/50;
136
+ background-color: color-mix(in oklab, var(--color-muted) 50%, transparent) !important;
124
137
  color: var(--shiki-light);
125
138
  }
126
139
 
127
- .shiki span {
140
+ .shiki span,
141
+ .astro-code span {
128
142
  color: var(--shiki-light);
129
143
  }
130
144
 
131
- html.dark .shiki {
145
+ html.dark .shiki,
146
+ html.dark .astro-code {
132
147
  color: var(--shiki-dark);
133
148
  }
134
149
 
135
- html.dark .shiki span {
150
+ html.dark .shiki span,
151
+ html.dark .astro-code span {
136
152
  color: var(--shiki-dark);
137
153
  }
138
154
 
@@ -155,179 +171,3 @@ html.dark .shiki span {
155
171
  .command mark {
156
172
  @apply bg-search-highlight/50 text-foreground rounded-sm px-0.5 dark:bg-search-highlight/25;
157
173
  }
158
-
159
- .docs-card-link {
160
- @apply transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50;
161
- }
162
-
163
- .alert[data-variant='warning'] > svg,
164
- .alert[data-variant='warning'] > h3,
165
- .alert[data-variant='warning'] > section {
166
- @apply text-amber-700 dark:text-amber-300;
167
- }
168
-
169
- .alert-destructive[data-variant='danger'] > svg,
170
- .alert-destructive[data-variant='danger'] > h3,
171
- .alert-destructive[data-variant='danger'] > section {
172
- @apply text-destructive;
173
- }
174
-
175
- .code-block {
176
- @apply relative my-6 max-w-full overflow-hidden rounded-lg;
177
- }
178
-
179
- .code-block > header,
180
- .code-group > header,
181
- .preview > header {
182
- @apply flex h-10 items-center gap-2 border-b border-border/50 bg-muted px-3 text-sm font-medium dark:bg-muted/50;
183
- }
184
-
185
- .code-block > header > :first-child,
186
- .code-group > header > :first-child,
187
- .preview > header > :first-child {
188
- @apply mr-auto;
189
- }
190
-
191
- .code-block > pre {
192
- @apply my-0 max-w-full overflow-x-auto rounded-none border-0;
193
- }
194
-
195
- .code-block > pre > code {
196
- @apply block min-w-full w-max px-4 py-3 text-[13px];
197
- }
198
-
199
- .code-block > .shiki {
200
- @apply my-0 rounded-none border-0;
201
- }
202
-
203
- .code-block > button {
204
- @apply absolute right-2.5 top-2.5;
205
- }
206
-
207
- .code-block button,
208
- .code-group button[aria-label='Copy code'],
209
- .preview button[aria-label='Copy code'] {
210
- @apply flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:hover:bg-accent/50 [&_svg]:size-4;
211
- }
212
-
213
- .code-block > header > button,
214
- .code-group > header > button,
215
- .preview > header > button {
216
- @apply ml-auto;
217
- }
218
-
219
- .code-group {
220
- @apply relative my-6 max-w-full overflow-hidden rounded-lg;
221
- }
222
-
223
- .code-group > header > .tabs {
224
- @apply my-0 min-w-0 max-w-full gap-0;
225
- }
226
-
227
- .code-group > header > .tabs > nav {
228
- @apply h-full justify-start rounded-none bg-transparent p-0;
229
- }
230
-
231
- .code-group > [role='tabpanel'] {
232
- @apply border-t-0;
233
- }
234
-
235
- .code-group .code-block {
236
- @apply my-0 min-w-0 rounded-none;
237
- }
238
-
239
- .code-group .code-block > header {
240
- @apply hidden;
241
- }
242
-
243
- .preview {
244
- @apply relative my-6 max-w-full overflow-hidden rounded-lg border border-border/50 bg-muted dark:bg-muted/50;
245
- }
246
-
247
- .preview > header > .tabs {
248
- @apply my-0 min-w-0 max-w-full gap-0;
249
- }
250
-
251
- .preview > header > .tabs > nav {
252
- @apply h-full justify-start rounded-none bg-transparent p-0;
253
- }
254
-
255
- .preview > [role='tabpanel'] {
256
- @apply border-t-0;
257
- }
258
-
259
- .preview > [data-code-panel] .code-block {
260
- @apply my-0 min-w-0 rounded-none;
261
- }
262
-
263
- .preview > header > button[aria-label='Copy code'] {
264
- display: none;
265
- }
266
-
267
- .preview:has(> [data-code-panel]:not([hidden])) > header > button[aria-label='Copy code'] {
268
- display: flex;
269
- }
270
-
271
- .preview > [data-preview-panel] > div {
272
- @apply flex min-h-64 w-full items-center justify-center bg-muted p-6 dark:bg-muted/50;
273
- }
274
-
275
- .steps {
276
- @apply mb-12 mt-6 md:ml-4 md:border-l md:pl-8;
277
- counter-reset: step;
278
- }
279
-
280
- .steps > section {
281
- counter-increment: step;
282
- }
283
-
284
- .steps > section > h3 {
285
- @apply relative mt-12 scroll-m-28 text-lg font-medium tracking-tight;
286
- }
287
-
288
- .steps > section:first-child > h3 {
289
- @apply mt-0;
290
- }
291
-
292
- .steps > section > h3::before {
293
- @apply mr-4 inline-flex size-7 items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground;
294
- content: counter(step);
295
- }
296
-
297
- @media (width >= 48rem) {
298
- .steps > section > h3::before {
299
- position: absolute;
300
- left: 0;
301
- top: 0;
302
- margin-right: 0;
303
- transform: translateX(-2.875rem);
304
- }
305
- }
306
-
307
- .steps > section > * + * {
308
- @apply mt-6;
309
- }
310
-
311
- .steps > section > h3 + * {
312
- @apply mt-4;
313
- }
314
-
315
- .steps > section > * {
316
- @apply leading-relaxed;
317
- }
318
-
319
- .steps > section > *:first-child {
320
- @apply mt-0;
321
- }
322
-
323
- .steps > section > *:last-child {
324
- @apply mb-0;
325
- }
326
-
327
- .prose > .alert,
328
- .prose > .alert-destructive,
329
- .prose > .card,
330
- .prose > .grid,
331
- .prose > .tabs {
332
- @apply my-6;
333
- }
@@ -16,7 +16,7 @@ const { indexUrl } = Astro.props;
16
16
  </div>
17
17
 
18
18
  <script>
19
- import lunr from "lunr";
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,14 +198,30 @@ 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
- if (!indexUrl) throw new Error("Missing RSD search index URL.");
218
+ if (!indexUrl) throw new Error("Missing reallysimpledocs search index URL.");
203
219
  if (!state.loadPromise) {
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);