reallysimpledocs 1.0.0-beta.3 → 1.0.0-beta.5
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 +33 -31
- package/package.json +10 -5
- package/src/astro/index.js +46 -43
- package/src/astro/preview-source.js +90 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +259 -29
- package/src/css/styles/luma.css +11 -0
- package/src/css/styles/lyra.css +11 -0
- package/src/css/styles/maia.css +11 -0
- package/src/css/styles/mira.css +11 -0
- package/src/css/styles/nova.css +11 -0
- package/src/css/styles/rhea.css +11 -0
- package/src/css/styles/sera.css +11 -0
- package/src/css/styles/vega.css +11 -0
- package/src/runtime/components/BaseLayout.astro +20 -27
- package/src/runtime/components/CommandDialog.astro +29 -14
- package/src/runtime/components/DefaultContentHeader.astro +2 -0
- package/src/runtime/components/DefaultHead.astro +120 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarFooter.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +90 -122
- package/src/runtime/components/DropdownMenu.astro +1 -1
- package/src/runtime/components/Sidebar.astro +4 -0
- package/src/runtime/components/ThemeToggle.astro +21 -0
- package/src/runtime/components/mdx/Callout.astro +2 -9
- package/src/runtime/components/mdx/Card.astro +3 -8
- package/src/runtime/components/mdx/CardGrid.astro +1 -1
- package/src/runtime/components/mdx/Code.astro +2 -16
- package/src/runtime/components/mdx/CodeGroup.astro +7 -11
- package/src/runtime/components/mdx/Preview.astro +7 -12
- package/src/runtime/components/mdx/Steps.astro +1 -5
- package/src/runtime/components/mdx/Tab.astro +6 -0
- package/src/runtime/components/mdx/Tabs.astro +7 -7
- package/src/runtime/lib/docs.js +122 -16
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/macros.js +11 -2
- package/src/runtime/lib/markdown-export.js +163 -0
- package/src/runtime/lib/site.js +0 -1
- package/src/runtime/pages/search-index.ts +10 -1
- package/src/css/overrides.css +0 -1
- package/src/css/sources.css +0 -1
- package/src/runtime/scripts/copy-code.js +0 -109
package/README.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A simple documentation system for Astro, built with **Basecoat** and **Tailwind CSS**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
ReallySimpleDocs keeps docs portable: content lives in `docs/`, navigation lives in `docs/docs.json`, and the Astro integration owns the docs UI.
|
|
6
6
|
|
|
7
7
|
## Add to an Astro site
|
|
8
8
|
|
|
9
|
+
ReallySimpleDocs is an Astro integration. Add it to an existing Astro site, or create one first with `npm create astro@latest`.
|
|
10
|
+
|
|
9
11
|
```bash
|
|
10
|
-
npm install reallysimpledocs
|
|
12
|
+
npm install reallysimpledocs basecoat-css tailwindcss
|
|
11
13
|
```
|
|
12
14
|
|
|
13
15
|
Add the integration:
|
|
@@ -59,49 +61,41 @@ 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
|
|
66
|
+
Use component overrides for layout chrome such as sidebar branding, extra head tags, or header controls between search and the built-in theme toggle:
|
|
65
67
|
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
```js
|
|
69
|
+
reallySimpleDocs({
|
|
70
|
+
components: {
|
|
71
|
+
ContentHeader: "./src/components/ContentHeader.astro",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
70
74
|
```
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
For example, put only the GitHub button in `ContentHeader`; the theme toggle stays built in.
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
Use `.mdx` when a page needs ReallySimpleDocs components. Common components are available by default, so you do not need to import them in each page:
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
```mdx
|
|
81
|
+
# Button
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
npm install reallysimpledocs@beta basecoat-css@beta tailwindcss
|
|
83
|
+
<Callout title="MDX works">MDX is optional. Plain Markdown stays the default.</Callout>
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```css
|
|
85
|
-
/* src/styles/site.css */
|
|
86
|
-
@import "tailwindcss";
|
|
87
|
-
@import "basecoat-css/base";
|
|
88
|
-
@import "basecoat-css/styles/vega";
|
|
86
|
+
## Use Basecoat outside the docs
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
```
|
|
88
|
+
By default, ReallySimpleDocs manages one Tailwind/Basecoat stylesheet for the app. It scans `src/`, `docs/`, and the ReallySimpleDocs runtime, so Basecoat and Tailwind classes used by custom Astro pages outside the docs route are included in the same CSS build.
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
Disable managed CSS only when you want to provide the full stylesheet pipeline yourself:
|
|
94
91
|
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
<slot />
|
|
92
|
+
```js
|
|
93
|
+
reallySimpleDocs({
|
|
94
|
+
css: false,
|
|
95
|
+
});
|
|
102
96
|
```
|
|
103
97
|
|
|
104
|
-
Use
|
|
98
|
+
Use `css: false` when you bring your own Tailwind/Basecoat/RSD stylesheet. ReallySimpleDocs still provides its managed JavaScript.
|
|
105
99
|
|
|
106
100
|
## Local development (this repo)
|
|
107
101
|
|
|
@@ -110,6 +104,14 @@ npm install
|
|
|
110
104
|
npm run dev
|
|
111
105
|
```
|
|
112
106
|
|
|
107
|
+
Useful checks before publishing changes:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm run build
|
|
111
|
+
npm pack --dry-run
|
|
112
|
+
npm run smoke:consumer
|
|
113
|
+
```
|
|
114
|
+
|
|
113
115
|
## Documentation
|
|
114
116
|
|
|
115
117
|
Go to [ReallySimpleDocs.com](https://reallysimpledocs.com).
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reallysimpledocs",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.js",
|
|
7
7
|
"./astro": "./src/astro/index.js",
|
|
8
|
-
"./components": "./src/components/index.js"
|
|
8
|
+
"./components": "./src/components/index.js",
|
|
9
|
+
"./css": "./src/css/custom.css",
|
|
10
|
+
"./css/styles/*": "./src/css/styles/*.css",
|
|
11
|
+
"./css/styles/*.css": "./src/css/styles/*.css"
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
11
14
|
"src",
|
|
@@ -20,9 +23,9 @@
|
|
|
20
23
|
"check": "npm run build && npm pack --dry-run && npm run smoke:consumer"
|
|
21
24
|
},
|
|
22
25
|
"dependencies": {
|
|
26
|
+
"@astrojs/markdown-remark": "^7.2.0",
|
|
23
27
|
"@astrojs/mdx": "^6.0.3",
|
|
24
28
|
"@tailwindcss/vite": "4.1.17",
|
|
25
|
-
"astro": "^6.4.6",
|
|
26
29
|
"github-slugger": "^2.0.0",
|
|
27
30
|
"lucide-static": "^0.552.0",
|
|
28
31
|
"lunr": "^2.3.9",
|
|
@@ -30,11 +33,13 @@
|
|
|
30
33
|
"shiki": "^4.1.0"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
|
-
"
|
|
36
|
+
"astro": "^6.4.6",
|
|
37
|
+
"basecoat-css": "^1.0.0-beta.5",
|
|
34
38
|
"tailwindcss": ">=4.1.17 <5"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
|
-
"
|
|
41
|
+
"astro": "^6.4.6",
|
|
42
|
+
"basecoat-css": "^1.0.0-beta.5",
|
|
38
43
|
"tailwindcss": "4.1.17"
|
|
39
44
|
},
|
|
40
45
|
"overrides": {
|
package/src/astro/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import { fileURLToPath } from "node:url";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import mdx from "@astrojs/mdx";
|
|
5
|
+
import { unified } from "@astrojs/markdown-remark";
|
|
5
6
|
import tailwindcss from "@tailwindcss/vite";
|
|
7
|
+
import { remarkPreviewSource } from "./preview-source.js";
|
|
6
8
|
|
|
7
9
|
const basecoatStyles = new Set(["vega", "nova", "maia", "lyra", "mira", "luma", "sera", "rhea"]);
|
|
8
10
|
|
|
@@ -26,10 +28,7 @@ const normalizeArray = (value) => (Array.isArray(value) ? value : value ? [value
|
|
|
26
28
|
|
|
27
29
|
const normalizeImportPath = (filePath) => filePath.replaceAll(path.sep, "/");
|
|
28
30
|
|
|
29
|
-
const
|
|
30
|
-
const relative = path.relative(dir, file);
|
|
31
|
-
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
32
|
-
};
|
|
31
|
+
const normalizeBoolean = (value, fallback = true) => (value === undefined ? fallback : Boolean(value));
|
|
33
32
|
|
|
34
33
|
const shikiConfig = {
|
|
35
34
|
themes: {
|
|
@@ -71,6 +70,7 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
71
70
|
siteFile: options.siteFile || null,
|
|
72
71
|
style,
|
|
73
72
|
customCss: normalizeArray(options.customCss),
|
|
73
|
+
css: normalizeBoolean(options.css),
|
|
74
74
|
components: options.components || {},
|
|
75
75
|
routeBase: options.routeBase ?? "/docs",
|
|
76
76
|
assetsBase: options.assetsBase || "/assets",
|
|
@@ -79,7 +79,7 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
79
79
|
return {
|
|
80
80
|
name: "reallysimpledocs",
|
|
81
81
|
hooks: {
|
|
82
|
-
"astro:config:setup": ({ injectRoute, updateConfig, config }) => {
|
|
82
|
+
"astro:config:setup": ({ injectRoute, injectScript, updateConfig, config }) => {
|
|
83
83
|
const root = fileURLToPath(config.root);
|
|
84
84
|
const generatedDir = path.join(root, ".astro", "reallysimpledocs");
|
|
85
85
|
const generatedStyles = path.join(generatedDir, "styles.css");
|
|
@@ -91,27 +91,39 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
91
91
|
siteFile: normalizedOptions.siteFile ? path.resolve(root, normalizedOptions.siteFile) : null,
|
|
92
92
|
routeBase: normalizeRouteBase(normalizedOptions.routeBase),
|
|
93
93
|
};
|
|
94
|
+
const mdxModules = collectMdxModules(docsDir);
|
|
94
95
|
const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
const componentDefaults = {
|
|
97
|
+
Head: runtimePath("components/DefaultHeadContent.astro"),
|
|
98
|
+
SidebarHeader: runtimePath("components/DefaultSidebarHeader.astro"),
|
|
99
|
+
SidebarFooter: runtimePath("components/DefaultSidebarFooter.astro"),
|
|
100
|
+
ContentHeader: runtimePath("components/DefaultContentHeader.astro"),
|
|
98
101
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
'@import "basecoat-css/base";',
|
|
105
|
-
`@import "basecoat-css/styles/${style}";`,
|
|
106
|
-
`@import ${JSON.stringify(cssPath("sources.css"))};`,
|
|
107
|
-
`@import ${JSON.stringify(cssPath("custom.css"))};`,
|
|
108
|
-
`@import ${JSON.stringify(cssPath("overrides.css"))};`,
|
|
109
|
-
...normalizedOptions.customCss.map((css) => `@import ${JSON.stringify(resolveUserId(css))};`),
|
|
110
|
-
`@source ${JSON.stringify(runtimePath("**/*.{astro,js,ts}"))};`,
|
|
111
|
-
`@source ${JSON.stringify(path.join(virtualConfig.docsDir, "**/*.{md,mdx}"))};`,
|
|
112
|
-
"",
|
|
113
|
-
].join("\n"),
|
|
102
|
+
const components = Object.fromEntries(
|
|
103
|
+
Object.entries(componentDefaults).map(([name, defaultPath]) => [
|
|
104
|
+
name,
|
|
105
|
+
normalizedOptions.components[name] || defaultPath,
|
|
106
|
+
]),
|
|
114
107
|
);
|
|
108
|
+
fs.mkdirSync(generatedDir, { recursive: true });
|
|
109
|
+
const styleSource = normalizedOptions.css
|
|
110
|
+
? [
|
|
111
|
+
'@import "tailwindcss";',
|
|
112
|
+
'@import "basecoat-css/base";',
|
|
113
|
+
`@import "basecoat-css/styles/${style}";`,
|
|
114
|
+
`@import ${JSON.stringify(cssPath("custom.css"))};`,
|
|
115
|
+
`@import ${JSON.stringify(cssPath(`styles/${style}.css`))};`,
|
|
116
|
+
...normalizedOptions.customCss.map((css) => `@import ${JSON.stringify(resolveUserId(css))};`),
|
|
117
|
+
`@source ${JSON.stringify(path.join(root, "src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"))};`,
|
|
118
|
+
`@source ${JSON.stringify(runtimePath("**/*.{astro,js,ts}"))};`,
|
|
119
|
+
`@source ${JSON.stringify(path.join(virtualConfig.docsDir, "**/*.{md,mdx}"))};`,
|
|
120
|
+
"",
|
|
121
|
+
].join("\n")
|
|
122
|
+
: "/* ReallySimpleDocs CSS disabled by config. */\n";
|
|
123
|
+
fs.writeFileSync(generatedStyles, styleSource);
|
|
124
|
+
if (normalizedOptions.css) {
|
|
125
|
+
injectScript("page-ssr", 'import "virtual:reallysimpledocs/styles.css";');
|
|
126
|
+
}
|
|
115
127
|
|
|
116
128
|
injectRoute({
|
|
117
129
|
pattern: routePattern(normalizedOptions.routeBase),
|
|
@@ -142,6 +154,9 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
142
154
|
}),
|
|
143
155
|
],
|
|
144
156
|
markdown: {
|
|
157
|
+
processor: unified({
|
|
158
|
+
remarkPlugins: [[remarkPreviewSource, { docsDir }]],
|
|
159
|
+
}),
|
|
145
160
|
shikiConfig,
|
|
146
161
|
},
|
|
147
162
|
vite: {
|
|
@@ -152,34 +167,20 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
152
167
|
tailwindcss(),
|
|
153
168
|
{
|
|
154
169
|
name: "reallysimpledocs-virtual-modules",
|
|
155
|
-
configureServer(server) {
|
|
156
|
-
const invalidateMdxPages = () => {
|
|
157
|
-
const module = server.moduleGraph.getModuleById("\0virtual:reallysimpledocs/mdx-pages");
|
|
158
|
-
if (module) server.moduleGraph.invalidateModule(module);
|
|
159
|
-
server.ws.send({ type: "full-reload" });
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
server.watcher.add(path.join(docsDir, "**/*.mdx"));
|
|
163
|
-
server.watcher.on("add", (file) => {
|
|
164
|
-
if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
|
|
165
|
-
});
|
|
166
|
-
server.watcher.on("unlink", (file) => {
|
|
167
|
-
if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
|
|
168
|
-
});
|
|
169
|
-
},
|
|
170
170
|
resolveId(id) {
|
|
171
171
|
if (id === "virtual:reallysimpledocs/config") return "\0virtual:reallysimpledocs/config";
|
|
172
172
|
if (id === "virtual:reallysimpledocs/mdx-pages") return "\0virtual:reallysimpledocs/mdx-pages";
|
|
173
173
|
if (id === "virtual:reallysimpledocs/styles.css") return generatedStyles;
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
for (const name of Object.keys(components)) {
|
|
175
|
+
if (id === `virtual:reallysimpledocs/components/${name}`) {
|
|
176
|
+
return `\0virtual:reallysimpledocs/components/${name}`;
|
|
177
|
+
}
|
|
176
178
|
}
|
|
177
179
|
return null;
|
|
178
180
|
},
|
|
179
181
|
load(id) {
|
|
180
182
|
if (id === "\0virtual:reallysimpledocs/config") return `export default ${JSON.stringify(virtualConfig)};`;
|
|
181
183
|
if (id === "\0virtual:reallysimpledocs/mdx-pages") {
|
|
182
|
-
const mdxModules = collectMdxModules(docsDir);
|
|
183
184
|
const imports = Object.entries(mdxModules)
|
|
184
185
|
.map(([slug, filePath], index) => `import * as mdx${index} from ${JSON.stringify(filePath)};`)
|
|
185
186
|
.join("\n");
|
|
@@ -195,8 +196,10 @@ export async function getMdxPage(slug) {
|
|
|
195
196
|
}
|
|
196
197
|
`;
|
|
197
198
|
}
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
for (const [name, componentPath] of Object.entries(components)) {
|
|
200
|
+
if (id === `\0virtual:reallysimpledocs/components/${name}`) {
|
|
201
|
+
return `export { default } from ${JSON.stringify(resolveUserId(componentPath))};`;
|
|
202
|
+
}
|
|
200
203
|
}
|
|
201
204
|
return null;
|
|
202
205
|
},
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { toMarkdown } from "mdast-util-to-markdown";
|
|
2
|
+
import { mdxToMarkdown } from "mdast-util-mdx";
|
|
3
|
+
|
|
4
|
+
export function remarkPreviewSource({ docsDir } = {}) {
|
|
5
|
+
const normalizedDocsDir = docsDir ? normalizePath(docsDir).replace(/\/$/, "") : "";
|
|
6
|
+
|
|
7
|
+
return (tree, file) => {
|
|
8
|
+
if (normalizedDocsDir && !isDocsFile(file?.path, normalizedDocsDir)) return;
|
|
9
|
+
|
|
10
|
+
visit(tree, (node) => {
|
|
11
|
+
if (node?.type !== "mdxJsxFlowElement") return;
|
|
12
|
+
if (node.name === "Preview") addPreviewSource(node);
|
|
13
|
+
if (node.name === "Tabs") addTabsMetadata(node);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isDocsFile(filePath, docsDir) {
|
|
19
|
+
const normalized = normalizePath(filePath || "");
|
|
20
|
+
return normalized === docsDir || normalized.startsWith(`${docsDir}/`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizePath(value) {
|
|
24
|
+
return String(value || "").replaceAll("\\", "/");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function addPreviewSource(node) {
|
|
28
|
+
node.attributes ||= [];
|
|
29
|
+
if (hasAttr(node, "code")) return;
|
|
30
|
+
|
|
31
|
+
const source = toMarkdown({ type: "root", children: node.children || [] }, { extensions: [mdxToMarkdown()] }).trim();
|
|
32
|
+
node.attributes.push({ type: "mdxJsxAttribute", name: "code", value: source });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function addTabsMetadata(node) {
|
|
36
|
+
node.attributes ||= [];
|
|
37
|
+
if (hasAttr(node, "tabs")) return;
|
|
38
|
+
|
|
39
|
+
const tabNodes = (node.children || []).filter((child) => child?.type === "mdxJsxFlowElement" && child.name === "Tab");
|
|
40
|
+
if (!tabNodes.length) return;
|
|
41
|
+
|
|
42
|
+
const seen = new Map();
|
|
43
|
+
const tabs = tabNodes.map((tab, index) => {
|
|
44
|
+
tab.attributes ||= [];
|
|
45
|
+
const title = getAttr(tab, "title") || `Tab ${index + 1}`;
|
|
46
|
+
const value = uniqueValue(slugify(title) || `tab-${index + 1}`, seen);
|
|
47
|
+
if (!hasAttr(tab, "slot")) {
|
|
48
|
+
tab.attributes.push({ type: "mdxJsxAttribute", name: "slot", value });
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
label: title,
|
|
52
|
+
slot: value,
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
node.attributes.push({
|
|
57
|
+
type: "mdxJsxAttribute",
|
|
58
|
+
name: "tabs",
|
|
59
|
+
value: JSON.stringify(tabs),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function visit(node, visitor) {
|
|
64
|
+
visitor(node);
|
|
65
|
+
if (!Array.isArray(node?.children)) return;
|
|
66
|
+
for (const child of node.children) visit(child, visitor);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getAttr(node, name) {
|
|
70
|
+
const attr = (node.attributes || []).find((item) => item.type === "mdxJsxAttribute" && item.name === name);
|
|
71
|
+
return typeof attr?.value === "string" ? attr.value : "";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function hasAttr(node, name) {
|
|
75
|
+
return (node.attributes || []).some((item) => item.type === "mdxJsxAttribute" && item.name === name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function slugify(value) {
|
|
79
|
+
return String(value || "")
|
|
80
|
+
.trim()
|
|
81
|
+
.toLowerCase()
|
|
82
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
83
|
+
.replace(/^-+|-+$/g, "");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function uniqueValue(value, seen) {
|
|
87
|
+
const count = seen.get(value) || 0;
|
|
88
|
+
seen.set(value, count + 1);
|
|
89
|
+
return count ? `${value}-${count + 1}` : value;
|
|
90
|
+
}
|
package/src/components/index.js
CHANGED
|
@@ -10,4 +10,5 @@ export { default as CodeGroup } from "../runtime/components/mdx/CodeGroup.astro"
|
|
|
10
10
|
export { default as Preview } from "../runtime/components/mdx/Preview.astro";
|
|
11
11
|
export { default as Step } from "../runtime/components/mdx/Step.astro";
|
|
12
12
|
export { default as Steps } from "../runtime/components/mdx/Steps.astro";
|
|
13
|
+
export { default as Tab } from "../runtime/components/mdx/Tab.astro";
|
|
13
14
|
export { default as Tabs } from "../runtime/components/mdx/Tabs.astro";
|