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.
- package/CHANGELOG.md +35 -0
- package/README.md +11 -11
- package/package.json +10 -2
- package/src/astro/index.js +94 -3
- package/src/components/index.js +0 -2
- package/src/css/custom.css +45 -205
- package/src/runtime/components/CommandDialog.astro +20 -3
- 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 +36 -26
- package/src/runtime/components/mdx/Card.astro +8 -7
- package/src/runtime/components/mdx/CardGrid.astro +1 -1
- package/src/runtime/components/mdx/Code.astro +33 -13
- package/src/runtime/components/mdx/CodeGroup.astro +38 -14
- package/src/runtime/components/mdx/Preview.astro +16 -6
- package/src/runtime/components/mdx/Steps.astro +5 -1
- package/src/runtime/components/mdx/Tabs.astro +1 -1
- package/src/runtime/lib/docs.js +107 -36
- package/src/runtime/lib/highlight.js +27 -0
- 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 +4 -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 +51 -6
- package/src/site/ContentHeader.astro +33 -0
- package/src/runtime/components/mdx/Badge.astro +0 -15
- package/src/runtime/components/mdx/LinkButton.astro +0 -14
|
@@ -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
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import config from "virtual:reallysimpledocs/config";
|
|
3
3
|
import SidebarHeader from "virtual:reallysimpledocs/components/SidebarHeader";
|
|
4
|
+
import SidebarFooter from "virtual:reallysimpledocs/components/SidebarFooter";
|
|
4
5
|
import { renderSidebarContent } from "../lib/macros.js";
|
|
5
6
|
import { getSite } from "../lib/site.js";
|
|
6
7
|
|
|
@@ -16,5 +17,8 @@ const site = getSite(config);
|
|
|
16
17
|
<section class="scrollbar">
|
|
17
18
|
<Fragment set:html={renderSidebarContent(menu, "sidebar-content")} />
|
|
18
19
|
</section>
|
|
20
|
+
<footer>
|
|
21
|
+
<SidebarFooter site={site} />
|
|
22
|
+
</footer>
|
|
19
23
|
</nav>
|
|
20
24
|
</aside>
|
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
---
|
|
2
|
-
import CircleAlert from "lucide-static/icons/circle-alert.svg?raw";
|
|
3
|
-
import Info from "lucide-static/icons/info.svg?raw";
|
|
4
|
-
import Lightbulb from "lucide-static/icons/lightbulb.svg?raw";
|
|
5
|
-
import TriangleAlert from "lucide-static/icons/triangle-alert.svg?raw";
|
|
6
2
|
import * as lucideIcons from "lucide-static";
|
|
7
3
|
import { addSvgClass } from "../../lib/html.js";
|
|
8
4
|
|
|
9
|
-
const { type,
|
|
10
|
-
const normalizedType = String(
|
|
11
|
-
const calloutType = normalizedType === "
|
|
12
|
-
const destructive = calloutType === "
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
danger: CircleAlert,
|
|
26
|
-
};
|
|
5
|
+
const { type, title = "Note", icon: iconName, action, class: className = "" } = Astro.props;
|
|
6
|
+
const normalizedType = String(type || "").toLowerCase();
|
|
7
|
+
const calloutType = normalizedType === "warning" || normalizedType === "destructive" ? normalizedType : "";
|
|
8
|
+
const destructive = calloutType === "destructive";
|
|
9
|
+
const actionLabel = action?.label;
|
|
10
|
+
const actionClass = action?.class || "btn-sm-secondary";
|
|
11
|
+
const actionAttrs = action
|
|
12
|
+
? {
|
|
13
|
+
...action.attrs,
|
|
14
|
+
href: action.href,
|
|
15
|
+
target: action.target,
|
|
16
|
+
rel: action.rel,
|
|
17
|
+
onclick: action.onclick,
|
|
18
|
+
class: actionClass,
|
|
19
|
+
}
|
|
20
|
+
: {};
|
|
27
21
|
|
|
28
22
|
function resolveNamedIcon(name) {
|
|
29
23
|
if (!name || typeof name !== "string") return "";
|
|
@@ -39,16 +33,32 @@ function resolveNamedIcon(name) {
|
|
|
39
33
|
return lucideIcons[pascalName] || "";
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
const iconSource =
|
|
43
|
-
showIcon === false ? "" : typeof showIcon === "string" ? resolveNamedIcon(showIcon) : icons[normalizedType] || Info;
|
|
36
|
+
const iconSource = typeof iconName === "string" ? resolveNamedIcon(iconName) : "";
|
|
44
37
|
const icon = addSvgClass(iconSource, "size-4");
|
|
45
|
-
const heading = title
|
|
38
|
+
const heading = title;
|
|
39
|
+
const variantClass =
|
|
40
|
+
calloutType === "warning"
|
|
41
|
+
? "[&>svg]:text-amber-700 [&>h3]:text-amber-700 [&>section]:text-amber-700 dark:[&>svg]:text-amber-300 dark:[&>h3]:text-amber-300 dark:[&>section]:text-amber-300"
|
|
42
|
+
: destructive
|
|
43
|
+
? "[&>svg]:text-destructive [&>h3]:text-destructive [&>section]:text-destructive"
|
|
44
|
+
: "";
|
|
46
45
|
---
|
|
47
46
|
|
|
48
|
-
<div class:list={[destructive ? "alert-destructive" : "alert", className]}
|
|
47
|
+
<div class:list={[destructive ? "alert-destructive" : "alert", "my-6", variantClass, className]}>
|
|
49
48
|
{icon && <Fragment set:html={icon} />}
|
|
50
49
|
<h3>{heading}</h3>
|
|
51
50
|
<section>
|
|
52
51
|
<slot />
|
|
53
52
|
</section>
|
|
53
|
+
{
|
|
54
|
+
actionLabel && (
|
|
55
|
+
<footer>
|
|
56
|
+
{action?.href ? (
|
|
57
|
+
<a {...actionAttrs}>{actionLabel}</a>
|
|
58
|
+
) : (
|
|
59
|
+
<button type="button" {...actionAttrs}>{actionLabel}</button>
|
|
60
|
+
)}
|
|
61
|
+
</footer>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
54
64
|
</div>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
const { title, description, href, class: className = "" } = Astro.props;
|
|
3
3
|
const Tag = href ? "a" : "article";
|
|
4
|
+
const classes = [
|
|
5
|
+
"my-6 block w-full rounded-lg border bg-card p-3 text-card-foreground no-underline",
|
|
6
|
+
href && "transition-colors hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
7
|
+
className,
|
|
8
|
+
];
|
|
4
9
|
---
|
|
5
10
|
|
|
6
|
-
<Tag href={href} class:list={
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
{title && <h3>{title}</h3>}
|
|
10
|
-
{description && <p>{description}</p>}
|
|
11
|
-
</header>
|
|
12
|
-
)}
|
|
11
|
+
<Tag href={href} class:list={classes}>
|
|
12
|
+
{title && <h3 class="font-medium">{title}</h3>}
|
|
13
|
+
{description && <p class="mt-1 text-sm text-muted-foreground">{description}</p>}
|
|
13
14
|
</Tag>
|
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
---
|
|
2
|
-
import {
|
|
2
|
+
import { highlightCode } from "../../lib/highlight.js";
|
|
3
3
|
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
const { lang = "text", title, class: className = "" } = Astro.props;
|
|
5
|
+
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
6
|
+
const rawCode = decodeSlotText(slotHtml);
|
|
7
|
+
const html = await highlightCode(rawCode, lang);
|
|
8
|
+
const classes = [
|
|
9
|
+
"relative my-6 w-full max-w-full overflow-hidden rounded-lg",
|
|
10
|
+
className,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function decodeSlotText(value) {
|
|
14
|
+
return String(value || "")
|
|
15
|
+
.replace(/^<p>/, "")
|
|
16
|
+
.replace(/<\/p>$/, "")
|
|
17
|
+
.replace(/</g, "<")
|
|
18
|
+
.replace(/>/g, ">")
|
|
19
|
+
.replace(/&/g, "&")
|
|
20
|
+
.replace(/"/g, '"')
|
|
21
|
+
.replace(/'/g, "'")
|
|
22
|
+
.trim();
|
|
23
|
+
}
|
|
14
24
|
---
|
|
15
25
|
|
|
16
|
-
<div
|
|
17
|
-
|
|
26
|
+
<div
|
|
27
|
+
data-code-block
|
|
28
|
+
data-code-title={title || undefined}
|
|
29
|
+
class:list={classes}
|
|
30
|
+
>
|
|
31
|
+
{
|
|
32
|
+
title && (
|
|
33
|
+
<header class="flex h-10 items-center gap-2 border-b border-border/50 bg-muted/50 px-3 text-sm font-medium">
|
|
34
|
+
<span class="mr-auto">{title}</span>
|
|
35
|
+
</header>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
18
38
|
<Fragment set:html={html} />
|
|
19
39
|
</div>
|
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const { tabs = [], defaultTab, class: className = "" } = Astro.props;
|
|
2
|
+
const { defaultTab, class: className = "" } = Astro.props;
|
|
5
3
|
const id = `code-group-${Math.random().toString(36).slice(2)}`;
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
value: tab.value || tab.label || String(index),
|
|
9
|
-
lang: tab.lang || "text",
|
|
10
|
-
code: tab.code || "",
|
|
11
|
-
}));
|
|
4
|
+
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
5
|
+
const normalizedTabs = parseCodeTabs(slotHtml);
|
|
12
6
|
const defaultValue = defaultTab || normalizedTabs[0]?.value;
|
|
7
|
+
const classes = [
|
|
8
|
+
"relative my-6 w-full max-w-full overflow-hidden rounded-lg [&>[role=tabpanel]]:border-t-0 [&_[data-code-block]]:my-0 [&_[data-code-block]]:min-w-0 [&_[data-code-block]]:rounded-none [&_[data-code-block]>header]:hidden",
|
|
9
|
+
className,
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
function parseCodeTabs(html) {
|
|
13
|
+
const blocks = [];
|
|
14
|
+
const pattern =
|
|
15
|
+
/<div\b(?=[^>]*\bdata-code-block\b)(?=[^>]*\bdata-code-title="([^"]*)")[^>]*>([\s\S]*?)<\/div>/g;
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = pattern.exec(html))) {
|
|
18
|
+
const label = decodeHtml(match[1]).trim();
|
|
19
|
+
if (!label) continue;
|
|
20
|
+
blocks.push({
|
|
21
|
+
label,
|
|
22
|
+
value: label,
|
|
23
|
+
html: match[0],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return blocks;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function decodeHtml(value) {
|
|
30
|
+
return String(value || "")
|
|
31
|
+
.replace(/</g, "<")
|
|
32
|
+
.replace(/>/g, ">")
|
|
33
|
+
.replace(/&/g, "&")
|
|
34
|
+
.replace(/"/g, '"')
|
|
35
|
+
.replace(/'/g, "'");
|
|
36
|
+
}
|
|
13
37
|
---
|
|
14
38
|
|
|
15
|
-
<div class:list={
|
|
16
|
-
<header>
|
|
17
|
-
<div class="tabs">
|
|
18
|
-
<nav role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
39
|
+
<div class:list={classes} data-code-group>
|
|
40
|
+
<header class="flex h-10 items-center gap-2 border-b border-border/50 bg-muted/50 px-3 text-sm font-medium">
|
|
41
|
+
<div class="tabs my-0 min-w-0 max-w-full gap-0">
|
|
42
|
+
<nav class="h-full justify-start rounded-none bg-transparent p-0" role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
19
43
|
{
|
|
20
44
|
normalizedTabs.map((tab, index) => {
|
|
21
45
|
const selected = tab.value === defaultValue;
|
|
@@ -48,7 +72,7 @@ const defaultValue = defaultTab || normalizedTabs[0]?.value;
|
|
|
48
72
|
aria-selected={selected ? "true" : "false"}
|
|
49
73
|
hidden={!selected}
|
|
50
74
|
>
|
|
51
|
-
<
|
|
75
|
+
<Fragment set:html={tab.html} />
|
|
52
76
|
</div>
|
|
53
77
|
);
|
|
54
78
|
})
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
2
|
+
import { highlightCode } from "../../lib/highlight.js";
|
|
3
3
|
|
|
4
4
|
const { code = "", lang = "html", title = "Preview", class: className = "" } = Astro.props;
|
|
5
5
|
const id = `preview-${Math.random().toString(36).slice(2)}`;
|
|
6
|
+
const html = await highlightCode(code, lang);
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
<section
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<section
|
|
10
|
+
class="relative my-6 max-w-full overflow-visible rounded-lg border [&>[role=tabpanel]]:border-t-0 [&>[data-code-panel]_[data-code-block]]:my-0 [&>[data-code-panel]_[data-code-block]]:min-w-0 [&>[data-code-panel]_[data-code-block]]:rounded-none"
|
|
11
|
+
data-preview
|
|
12
|
+
>
|
|
13
|
+
<header class="flex h-10 items-center gap-2 border-b border-border/50 bg-muted/50 px-3 text-sm font-medium">
|
|
14
|
+
<div class="tabs my-0 min-w-0 max-w-full gap-0">
|
|
15
|
+
<nav class="h-full justify-start rounded-none bg-transparent p-0" role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
12
16
|
<button
|
|
13
17
|
type="button"
|
|
14
18
|
role="tab"
|
|
@@ -39,6 +43,7 @@ const id = `preview-${Math.random().toString(36).slice(2)}`;
|
|
|
39
43
|
aria-labelledby={`${id}-preview-tab`}
|
|
40
44
|
tabindex="-1"
|
|
41
45
|
aria-selected="true"
|
|
46
|
+
class="flex min-h-64 w-full items-center justify-center p-6"
|
|
42
47
|
>
|
|
43
48
|
<div class={className}>
|
|
44
49
|
<slot />
|
|
@@ -53,6 +58,11 @@ const id = `preview-${Math.random().toString(36).slice(2)}`;
|
|
|
53
58
|
aria-selected="false"
|
|
54
59
|
hidden
|
|
55
60
|
>
|
|
56
|
-
<
|
|
61
|
+
<div
|
|
62
|
+
class="relative my-6 w-full max-w-full overflow-hidden rounded-lg"
|
|
63
|
+
data-code-block
|
|
64
|
+
>
|
|
65
|
+
<Fragment set:html={html} />
|
|
66
|
+
</div>
|
|
57
67
|
</section>
|
|
58
68
|
</section>
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
const { class: className = "" } = Astro.props;
|
|
3
|
+
const classes = [
|
|
4
|
+
"mb-12 mt-6 [counter-reset:step] md:ml-4 md:border-l md:pl-8 [&>section]:mb-10 [&>section]:[counter-increment:step] [&>section:last-child]:mb-0 [&>section>*+*]:mt-6 [&>section>*]:leading-relaxed [&>section>*:first-child]:mt-0 [&>section>*:last-child]:mb-0 [&>section>h3+*]:mt-4 [&>section>h3]:relative [&>section>h3]:scroll-m-28 [&>section>h3]:text-lg [&>section>h3]:font-medium [&>section>h3]:tracking-tight [&>section>h3]:before:mr-4 [&>section>h3]:before:inline-flex [&>section>h3]:before:size-7 [&>section>h3]:before:items-center [&>section>h3]:before:justify-center [&>section>h3]:before:rounded-full [&>section>h3]:before:bg-muted [&>section>h3]:before:text-sm [&>section>h3]:before:font-medium [&>section>h3]:before:text-muted-foreground [&>section>h3]:before:content-[counter(step)] md:[&>section>h3]:before:absolute md:[&>section>h3]:before:left-0 md:[&>section>h3]:before:top-0 md:[&>section>h3]:before:mr-0 md:[&>section>h3]:before:-translate-x-[2.875rem]",
|
|
5
|
+
className,
|
|
6
|
+
];
|
|
3
7
|
---
|
|
4
8
|
|
|
5
|
-
<div role="list" class:list={
|
|
9
|
+
<div role="list" class:list={classes}>
|
|
6
10
|
<slot />
|
|
7
11
|
</div>
|
|
@@ -9,7 +9,7 @@ const normalizedTabs = tabs.map((tab, index) => ({
|
|
|
9
9
|
const defaultValue = defaultTab || normalizedTabs.find((tab) => !tab.disabled)?.value || normalizedTabs[0]?.value;
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<div class:list={["tabs", className]}>
|
|
12
|
+
<div class:list={["tabs my-6", className]}>
|
|
13
13
|
<nav role="tablist" aria-orientation={orientation} data-variant={variant}>
|
|
14
14
|
{
|
|
15
15
|
normalizedTabs.map((tab, index) => {
|