reallysimpledocs 1.0.0-beta.4 → 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 +35 -35
- package/package.json +10 -13
- package/src/astro/index.js +46 -98
- package/src/astro/preview-source.js +90 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +252 -33
- 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 +30 -32
- package/src/runtime/components/DefaultContentHeader.astro +0 -17
- package/src/runtime/components/DefaultHead.astro +120 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +114 -165
- package/src/runtime/components/DropdownMenu.astro +1 -1
- package/src/runtime/components/ThemeToggle.astro +21 -0
- package/src/runtime/components/mdx/Callout.astro +2 -32
- 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 -13
- 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 +130 -102
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/macros.js +10 -5
- package/src/runtime/lib/markdown-export.js +134 -171
- package/src/runtime/pages/doc.astro +1 -3
- 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 +12 -3
- package/CHANGELOG.md +0 -35
- package/src/css/overrides.css +0 -1
- package/src/css/sources.css +0 -1
- package/src/runtime/lib/markdown-exporters/index.js +0 -144
- package/src/runtime/lib/mdx-source.js +0 -19
- package/src/runtime/lib/preview-source-plugin.js +0 -33
- package/src/runtime/scripts/copy-code.js +0 -130
- package/src/site/ContentHeader.astro +0 -33
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Copy from "lucide-static/icons/copy.svg?raw";
|
|
3
|
+
import Check from "lucide-static/icons/check.svg?raw";
|
|
4
|
+
import basecoatJsUrl from "basecoat-css/all.min?url";
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<script is:inline>
|
|
8
|
+
(() => {
|
|
9
|
+
try {
|
|
10
|
+
const stored = localStorage.getItem("themeMode");
|
|
11
|
+
if (stored ? stored === "dark" : matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
12
|
+
document.documentElement.classList.add("dark");
|
|
13
|
+
}
|
|
14
|
+
} catch (_) {}
|
|
15
|
+
})();
|
|
16
|
+
</script>
|
|
17
|
+
<script src={basecoatJsUrl}></script>
|
|
18
|
+
<script is:inline define:vars={{ copyIcon: Copy, checkIcon: Check }}>
|
|
19
|
+
const copyText = async (text) => {
|
|
20
|
+
if (window.isSecureContext && navigator.clipboard?.writeText) {
|
|
21
|
+
return navigator.clipboard.writeText(text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const textarea = document.createElement("textarea");
|
|
25
|
+
textarea.value = text;
|
|
26
|
+
textarea.setAttribute("readonly", "");
|
|
27
|
+
textarea.style.position = "fixed";
|
|
28
|
+
textarea.style.top = "-9999px";
|
|
29
|
+
textarea.style.left = "-9999px";
|
|
30
|
+
document.body.appendChild(textarea);
|
|
31
|
+
textarea.select();
|
|
32
|
+
const ok = document.execCommand("copy");
|
|
33
|
+
textarea.remove();
|
|
34
|
+
if (!ok) throw new Error("Copy failed");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const activePanelCode = (root) => root.querySelector(':scope > [role="tabpanel"]:not([hidden]) code');
|
|
38
|
+
|
|
39
|
+
const initCopyCode = (codeBlock) => {
|
|
40
|
+
if (codeBlock.dataset.copyCodeInitialized) return;
|
|
41
|
+
const codeEl = codeBlock.querySelector(":scope > code");
|
|
42
|
+
if (!codeEl) return;
|
|
43
|
+
|
|
44
|
+
let target = null;
|
|
45
|
+
let frame = codeBlock.closest(".code-group");
|
|
46
|
+
let getCode = frame ? () => activePanelCode(frame) || codeEl : () => codeEl;
|
|
47
|
+
if (frame) target = frame.querySelector(":scope > header");
|
|
48
|
+
|
|
49
|
+
const preview = codeBlock.closest(".preview");
|
|
50
|
+
const previewCodePanel = preview?.querySelector(":scope > [data-code-panel]");
|
|
51
|
+
if (!frame && previewCodePanel?.contains(codeBlock)) {
|
|
52
|
+
frame = preview;
|
|
53
|
+
target = frame.querySelector(":scope > header");
|
|
54
|
+
getCode = () => codeEl;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!frame) {
|
|
58
|
+
frame = codeBlock.parentElement?.classList.contains("code-block") ? codeBlock.parentElement : null;
|
|
59
|
+
if (!frame) {
|
|
60
|
+
frame = document.createElement("div");
|
|
61
|
+
frame.className = "code-block";
|
|
62
|
+
codeBlock.parentNode.insertBefore(frame, codeBlock);
|
|
63
|
+
frame.appendChild(codeBlock);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
target ||= frame.querySelector(":scope > header") || frame;
|
|
68
|
+
if (target.querySelector(":scope > button[aria-label='Copy code']")) {
|
|
69
|
+
codeBlock.dataset.copyCodeInitialized = "true";
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const button = document.createElement("button");
|
|
74
|
+
button.type = "button";
|
|
75
|
+
button.className = "btn";
|
|
76
|
+
button.dataset.variant = "ghost";
|
|
77
|
+
button.dataset.size = "icon-xs";
|
|
78
|
+
button.setAttribute("aria-label", "Copy code");
|
|
79
|
+
button.innerHTML = copyIcon;
|
|
80
|
+
|
|
81
|
+
button.addEventListener("click", async () => {
|
|
82
|
+
const text = (getCode()?.textContent || "").replace(/\n$/, "");
|
|
83
|
+
if (!text) return;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await copyText(text);
|
|
87
|
+
button.innerHTML = checkIcon;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error("[copy] Failed to copy:", error);
|
|
90
|
+
button.textContent = "Failed";
|
|
91
|
+
}
|
|
92
|
+
window.setTimeout(() => {
|
|
93
|
+
button.innerHTML = copyIcon;
|
|
94
|
+
}, 1200);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
target.appendChild(button);
|
|
98
|
+
codeBlock.dataset.copyCodeInitialized = "true";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const initCopyButtons = (root = document) => {
|
|
102
|
+
root.querySelectorAll("pre:has(> code):not([data-copy-code-initialized])").forEach(initCopyCode);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (document.readyState === "loading") {
|
|
106
|
+
document.addEventListener("DOMContentLoaded", () => initCopyButtons(), { once: true });
|
|
107
|
+
} else {
|
|
108
|
+
initCopyButtons();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
new MutationObserver((mutations) => {
|
|
112
|
+
for (const mutation of mutations) {
|
|
113
|
+
mutation.addedNodes.forEach((node) => {
|
|
114
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
115
|
+
if (node.matches?.("pre:has(> code)")) initCopyCode(node);
|
|
116
|
+
initCopyButtons(node);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}).observe(document.documentElement, { childList: true, subtree: true });
|
|
120
|
+
</script>
|
|
@@ -9,7 +9,7 @@ const logo = site.logo?.svg
|
|
|
9
9
|
: "";
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<a href="/" class="btn
|
|
12
|
+
<a href="/" class="btn h-12 w-full justify-start p-2" data-variant="ghost" data-size="lg">
|
|
13
13
|
{
|
|
14
14
|
logo && (
|
|
15
15
|
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
|
@@ -4,49 +4,29 @@ 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";
|
|
7
8
|
import PanelLeft from "lucide-static/icons/panel-left.svg?raw";
|
|
8
9
|
import Search from "lucide-static/icons/search.svg?raw";
|
|
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";
|
|
12
10
|
import ClaudeLogo from "../../icons/logos/claude.svg?raw";
|
|
13
11
|
import CursorLogo from "../../icons/logos/cursor.svg?raw";
|
|
14
12
|
import MarkdownLogo from "../../icons/logos/markdown.svg?raw";
|
|
15
13
|
import OpenAILogo from "../../icons/logos/openai.svg?raw";
|
|
16
14
|
import BaseLayout from "./BaseLayout.astro";
|
|
17
15
|
import CommandDialog from "./CommandDialog.astro";
|
|
16
|
+
import ContentHeader from "virtual:reallysimpledocs/components/ContentHeader";
|
|
18
17
|
import DropdownMenu from "./DropdownMenu.astro";
|
|
19
18
|
import Sidebar from "./Sidebar.astro";
|
|
20
|
-
import
|
|
19
|
+
import ThemeToggle from "./ThemeToggle.astro";
|
|
20
|
+
import { buildMenus, getNavigation } from "../lib/docs.js";
|
|
21
21
|
import { addSvgClass } from "../lib/html.js";
|
|
22
|
-
import { renderDropdownMenu } from "../lib/macros.js";
|
|
23
|
-
import { routeFilePath } from "../lib/paths.js";
|
|
24
22
|
import { getSite } from "../lib/site.js";
|
|
25
23
|
|
|
26
24
|
const { page, headings } = Astro.props;
|
|
27
25
|
const site = getSite(config);
|
|
28
26
|
const navigation = getNavigation(config, page);
|
|
29
|
-
const breadcrumbs = getBreadcrumbs(config, page);
|
|
30
27
|
const { sidebar } = buildMenus(config, page.slug);
|
|
31
|
-
const hasToc = headings.length > 0;
|
|
32
|
-
const searchIndexUrl = routeFilePath(config.routeBase, "search-index.json");
|
|
33
|
-
const chevronRightIcon = addSvgClass(ChevronRight, "size-3.5");
|
|
34
|
-
const ellipsisIcon = addSvgClass(Ellipsis, "size-4");
|
|
35
28
|
const panelLeftIcon = addSvgClass(PanelLeft, "size-4");
|
|
36
29
|
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;
|
|
50
30
|
const siteUrl = Astro.site?.toString().replace(/\/$/, "") || site.url || "";
|
|
51
31
|
const pageUrl = `${siteUrl}${page.path}`;
|
|
52
32
|
const llmPrompt = `Read from ${pageUrl} so I can ask questions about it.`;
|
|
@@ -83,11 +63,13 @@ const actionItems = [
|
|
|
83
63
|
---
|
|
84
64
|
|
|
85
65
|
<BaseLayout title={page.title} description={site.description} pagePath={page.path}>
|
|
86
|
-
<CommandDialog
|
|
66
|
+
<CommandDialog />
|
|
87
67
|
<Sidebar menu={sidebar} />
|
|
88
68
|
|
|
89
69
|
<main id="content">
|
|
90
|
-
<header
|
|
70
|
+
<header
|
|
71
|
+
class="sticky inset-x-0 top-0 isolate z-30 flex shrink-0 items-center gap-2 border-b bg-background"
|
|
72
|
+
>
|
|
91
73
|
<div class="flex h-14 w-full items-center justify-between gap-2 px-4">
|
|
92
74
|
<button
|
|
93
75
|
type="button"
|
|
@@ -96,79 +78,43 @@ const actionItems = [
|
|
|
96
78
|
data-tooltip="Toggle sidebar"
|
|
97
79
|
data-side="bottom"
|
|
98
80
|
data-align="start"
|
|
99
|
-
class="btn
|
|
81
|
+
class="btn"
|
|
82
|
+
data-variant="ghost"
|
|
83
|
+
data-size="icon-sm"
|
|
100
84
|
>
|
|
101
85
|
<Fragment set:html={panelLeftIcon} />
|
|
102
86
|
</button>
|
|
103
87
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
150
|
-
<button
|
|
151
|
-
type="button"
|
|
152
|
-
class="btn-sm-icon-ghost ml-auto sm:hidden"
|
|
153
|
-
aria-label="Search"
|
|
154
|
-
data-tooltip="Search"
|
|
155
|
-
data-side="bottom"
|
|
88
|
+
<div
|
|
89
|
+
class="input-group h-8 w-full min-w-0 max-w-72 cursor-text sm:ml-auto"
|
|
90
|
+
role="button"
|
|
91
|
+
tabindex="0"
|
|
92
|
+
aria-label="Search docs"
|
|
156
93
|
onclick="window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal()"
|
|
94
|
+
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal(); }"
|
|
157
95
|
>
|
|
158
|
-
<
|
|
159
|
-
|
|
96
|
+
<input type="text" placeholder="Search..." readonly tabindex="-1" aria-hidden="true" />
|
|
97
|
+
<span data-align="start" aria-hidden="true"><Fragment set:html={searchIcon} /></span>
|
|
98
|
+
<span data-align="end" aria-hidden="true"><kbd class="kbd">⌘K</kbd></span>
|
|
99
|
+
</div>
|
|
160
100
|
|
|
161
|
-
<
|
|
162
|
-
|
|
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>
|
|
101
|
+
<ContentHeader site={site} page={page} />
|
|
102
|
+
<ThemeToggle />
|
|
170
103
|
|
|
171
|
-
<
|
|
104
|
+
<div class="sm:hidden">
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
class="btn peer group"
|
|
108
|
+
data-variant="ghost"
|
|
109
|
+
data-size="icon-sm"
|
|
110
|
+
aria-label="Toggle sidebar"
|
|
111
|
+
aria-expanded="false"
|
|
112
|
+
aria-controls="sidebar"
|
|
113
|
+
onclick="document.getElementById('sidebar')?.toggle()"
|
|
114
|
+
>
|
|
115
|
+
<Fragment set:html={Menu} />
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
172
118
|
</div>
|
|
173
119
|
</header>
|
|
174
120
|
|
|
@@ -176,19 +122,20 @@ const actionItems = [
|
|
|
176
122
|
<article class="min-w-0 flex-1 wrap-break-word">
|
|
177
123
|
<div class="mx-auto w-full max-w-2xl flex-1 space-y-10">
|
|
178
124
|
<header class="space-y-2">
|
|
179
|
-
<div class="flex items-start gap-x-2">
|
|
125
|
+
<div class="flex flex-col-reverse items-start gap-3 sm:flex-row sm:gap-x-2">
|
|
180
126
|
<h1 class="mr-auto text-3xl font-semibold tracking-tight sm:text-4xl">{page.title}</h1>
|
|
181
|
-
<div class="
|
|
127
|
+
<div class="flex w-full items-center justify-end gap-x-2 sm:mt-1 sm:w-auto">
|
|
182
128
|
<div role="group" class="button-group">
|
|
183
129
|
<button
|
|
184
130
|
type="button"
|
|
185
|
-
class="btn
|
|
131
|
+
class="btn"
|
|
132
|
+
data-variant="secondary"
|
|
133
|
+
data-size="sm"
|
|
186
134
|
data-md-url={page.markdownPath}
|
|
187
135
|
onclick="copyPageMarkdown(this)"
|
|
188
136
|
>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
<span>Copy page</span>
|
|
137
|
+
<Fragment set:html={Copy} />
|
|
138
|
+
Copy page
|
|
192
139
|
</button>
|
|
193
140
|
<hr role="separator" />
|
|
194
141
|
<DropdownMenu items={actionItems} />
|
|
@@ -196,7 +143,7 @@ const actionItems = [
|
|
|
196
143
|
<div role="group" class="button-group">
|
|
197
144
|
{
|
|
198
145
|
navigation.prev && (
|
|
199
|
-
<a href={navigation.prev.path} class="btn-
|
|
146
|
+
<a href={navigation.prev.path} class="btn" data-variant="secondary" data-size="icon-sm">
|
|
200
147
|
<span class="sr-only">{navigation.prev.title}</span>
|
|
201
148
|
<Fragment set:html={ArrowLeft} />
|
|
202
149
|
</a>
|
|
@@ -205,7 +152,7 @@ const actionItems = [
|
|
|
205
152
|
{navigation.prev && navigation.next && <hr role="separator" />}
|
|
206
153
|
{
|
|
207
154
|
navigation.next && (
|
|
208
|
-
<a href={navigation.next.path} class="btn-
|
|
155
|
+
<a href={navigation.next.path} class="btn" data-variant="secondary" data-size="icon-sm">
|
|
209
156
|
<span class="sr-only">{navigation.next.title}</span>
|
|
210
157
|
<Fragment set:html={ArrowRight} />
|
|
211
158
|
</a>
|
|
@@ -221,7 +168,7 @@ const actionItems = [
|
|
|
221
168
|
<footer class="mt-12 flex items-center">
|
|
222
169
|
{
|
|
223
170
|
navigation.prev && (
|
|
224
|
-
<a href={navigation.prev.path} class="btn-
|
|
171
|
+
<a href={navigation.prev.path} class="btn mr-auto" data-variant="secondary" data-size="sm">
|
|
225
172
|
<Fragment set:html={ArrowLeft} />
|
|
226
173
|
{navigation.prev.title}
|
|
227
174
|
</a>
|
|
@@ -229,7 +176,7 @@ const actionItems = [
|
|
|
229
176
|
}
|
|
230
177
|
{
|
|
231
178
|
navigation.next && (
|
|
232
|
-
<a href={navigation.next.path} class="btn-
|
|
179
|
+
<a href={navigation.next.path} class="btn ml-auto" data-variant="secondary" data-size="sm">
|
|
233
180
|
{navigation.next.title}
|
|
234
181
|
<Fragment set:html={ArrowRight} />
|
|
235
182
|
</a>
|
|
@@ -238,51 +185,48 @@ const actionItems = [
|
|
|
238
185
|
</footer>
|
|
239
186
|
</div>
|
|
240
187
|
</article>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
)
|
|
258
|
-
}
|
|
188
|
+
<aside class="hidden w-56 xl:block">
|
|
189
|
+
<div class="docs-toc sticky top-20 space-y-2 text-sm [&_a]:text-muted-foreground [&_a]:hover:text-foreground [&_a[aria-current='location']]:text-foreground [&_ol]:space-y-2 [&_ol_ol]:mt-2 [&_ol_ol]:pl-3" data-docs-toc>
|
|
190
|
+
<h2 class="flex items-center gap-1 font-medium">
|
|
191
|
+
On this page
|
|
192
|
+
</h2>
|
|
193
|
+
<ol>
|
|
194
|
+
{
|
|
195
|
+
headings.map((heading) => (
|
|
196
|
+
<li class={heading.depth === 3 ? "pl-3" : ""}>
|
|
197
|
+
<a href={`#${heading.id}`} data-toc-link>{heading.text}</a>
|
|
198
|
+
</li>
|
|
199
|
+
))
|
|
200
|
+
}
|
|
201
|
+
</ol>
|
|
202
|
+
</div>
|
|
203
|
+
</aside>
|
|
259
204
|
</div>
|
|
260
205
|
</main>
|
|
261
206
|
|
|
262
|
-
<script is:inline>
|
|
263
|
-
async function copyPageMarkdown(button) {
|
|
207
|
+
<script is:inline define:vars={{ checkIcon: Check }}>
|
|
208
|
+
window.copyPageMarkdown = async function copyPageMarkdown(button) {
|
|
264
209
|
const markdownUrl = button?.dataset?.mdUrl;
|
|
265
210
|
if (!markdownUrl) return;
|
|
266
|
-
const
|
|
267
|
-
const
|
|
211
|
+
const icon = button.querySelector("svg");
|
|
212
|
+
const originalIcon = icon?.outerHTML || "";
|
|
268
213
|
|
|
269
214
|
try {
|
|
270
215
|
const response = await fetch(markdownUrl, { credentials: "same-origin", cache: "no-store" });
|
|
271
216
|
if (!response.ok) throw new Error("Failed to fetch markdown");
|
|
272
217
|
const markdown = await response.text();
|
|
273
218
|
await writeToClipboard(markdown);
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
checkIcon.classList.remove("hidden");
|
|
219
|
+
if (icon && checkIcon) {
|
|
220
|
+
icon.outerHTML = checkIcon;
|
|
277
221
|
window.setTimeout(() => {
|
|
278
|
-
|
|
279
|
-
|
|
222
|
+
const currentIcon = button.querySelector("svg");
|
|
223
|
+
if (currentIcon && originalIcon) currentIcon.outerHTML = originalIcon;
|
|
280
224
|
}, 1200);
|
|
281
225
|
}
|
|
282
226
|
} catch (_) {
|
|
283
227
|
window.open(new URL(markdownUrl, window.location.href).toString(), "_blank", "noopener");
|
|
284
228
|
}
|
|
285
|
-
}
|
|
229
|
+
};
|
|
286
230
|
|
|
287
231
|
async function writeToClipboard(text) {
|
|
288
232
|
if (navigator.clipboard?.writeText && window.isSecureContext) {
|
|
@@ -305,55 +249,60 @@ const actionItems = [
|
|
|
305
249
|
|
|
306
250
|
function initDocsToc() {
|
|
307
251
|
const toc = document.querySelector("[data-docs-toc]");
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const links = Array.from(toc.querySelectorAll("[data-toc-link]"));
|
|
311
|
-
const linkById = new Map();
|
|
312
|
-
for (const link of links) {
|
|
313
|
-
const id = decodeURIComponent(link.hash.slice(1));
|
|
314
|
-
if (id) linkById.set(id, link);
|
|
315
|
-
}
|
|
252
|
+
const links = Array.from(toc?.querySelectorAll("[data-toc-link]") || []);
|
|
253
|
+
if (!links.length) return;
|
|
316
254
|
|
|
317
|
-
const
|
|
318
|
-
.map((
|
|
255
|
+
const entries = links
|
|
256
|
+
.map((link) => {
|
|
257
|
+
const id = decodeURIComponent(link.hash.slice(1));
|
|
258
|
+
const heading = id ? document.getElementById(id) : null;
|
|
259
|
+
return heading ? { id, heading, link } : null;
|
|
260
|
+
})
|
|
319
261
|
.filter(Boolean);
|
|
320
|
-
if (!
|
|
262
|
+
if (!entries.length) return;
|
|
321
263
|
|
|
322
|
-
const
|
|
323
|
-
for (const
|
|
324
|
-
const active =
|
|
325
|
-
link.
|
|
326
|
-
if (active) link.setAttribute("aria-current", "location");
|
|
327
|
-
else link.removeAttribute("aria-current");
|
|
264
|
+
const activate = (id) => {
|
|
265
|
+
for (const entry of entries) {
|
|
266
|
+
const active = entry.id === id;
|
|
267
|
+
entry.link.toggleAttribute("data-active", active);
|
|
268
|
+
if (active) entry.link.setAttribute("aria-current", "location");
|
|
269
|
+
else entry.link.removeAttribute("aria-current");
|
|
328
270
|
}
|
|
329
271
|
};
|
|
330
272
|
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
let current =
|
|
334
|
-
|
|
335
|
-
|
|
273
|
+
const activeFromScroll = () => {
|
|
274
|
+
const marker = Math.min(window.innerHeight * 0.35, 220);
|
|
275
|
+
let current = entries[0];
|
|
276
|
+
|
|
277
|
+
for (const entry of entries) {
|
|
278
|
+
if (entry.heading.getBoundingClientRect().top <= marker) current = entry;
|
|
336
279
|
else break;
|
|
337
280
|
}
|
|
338
|
-
return current;
|
|
339
|
-
};
|
|
340
281
|
|
|
341
|
-
|
|
342
|
-
|
|
282
|
+
activate(current.id);
|
|
283
|
+
};
|
|
343
284
|
|
|
344
|
-
let
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
updateActive();
|
|
285
|
+
let frame = 0;
|
|
286
|
+
const queueActiveUpdate = () => {
|
|
287
|
+
if (frame) return;
|
|
288
|
+
frame = window.requestAnimationFrame(() => {
|
|
289
|
+
frame = 0;
|
|
290
|
+
activeFromScroll();
|
|
351
291
|
});
|
|
352
292
|
};
|
|
353
293
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
294
|
+
if ("IntersectionObserver" in window) {
|
|
295
|
+
const observer = new IntersectionObserver(queueActiveUpdate, {
|
|
296
|
+
rootMargin: "-20% 0px -65% 0px",
|
|
297
|
+
threshold: [0, 1],
|
|
298
|
+
});
|
|
299
|
+
for (const entry of entries) observer.observe(entry.heading);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
activeFromScroll();
|
|
303
|
+
window.addEventListener("scroll", queueActiveUpdate, { passive: true });
|
|
304
|
+
window.addEventListener("resize", queueActiveUpdate);
|
|
305
|
+
window.addEventListener("hashchange", () => window.setTimeout(activeFromScroll, 0));
|
|
357
306
|
}
|
|
358
307
|
|
|
359
308
|
initDocsToc();
|
|
@@ -0,0 +1,21 @@
|
|
|
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="window.basecoat?.theme.toggle()"
|
|
14
|
+
class="btn"
|
|
15
|
+
data-variant="ghost"
|
|
16
|
+
data-size="icon-sm"
|
|
17
|
+
>
|
|
18
|
+
<span class="hidden dark:block"><Fragment set:html={Sun} /></span>
|
|
19
|
+
<span class="block dark:hidden"><Fragment set:html={Moon} /></span>
|
|
20
|
+
</button>
|
|
21
|
+
</nav>
|
|
@@ -2,22 +2,9 @@
|
|
|
2
2
|
import * as lucideIcons from "lucide-static";
|
|
3
3
|
import { addSvgClass } from "../../lib/html.js";
|
|
4
4
|
|
|
5
|
-
const { type, title = "Note", icon: iconName,
|
|
5
|
+
const { type, title = "Note", icon: iconName, class: className = "" } = Astro.props;
|
|
6
6
|
const normalizedType = String(type || "").toLowerCase();
|
|
7
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
|
-
: {};
|
|
21
8
|
|
|
22
9
|
function resolveNamedIcon(name) {
|
|
23
10
|
if (!name || typeof name !== "string") return "";
|
|
@@ -36,29 +23,12 @@ function resolveNamedIcon(name) {
|
|
|
36
23
|
const iconSource = typeof iconName === "string" ? resolveNamedIcon(iconName) : "";
|
|
37
24
|
const icon = addSvgClass(iconSource, "size-4");
|
|
38
25
|
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
|
-
: "";
|
|
45
26
|
---
|
|
46
27
|
|
|
47
|
-
<div class:list={[
|
|
28
|
+
<div class:list={["alert", className]} data-variant={calloutType || undefined}>
|
|
48
29
|
{icon && <Fragment set:html={icon} />}
|
|
49
30
|
<h3>{heading}</h3>
|
|
50
31
|
<section>
|
|
51
32
|
<slot />
|
|
52
33
|
</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
|
-
}
|
|
64
34
|
</div>
|
|
@@ -1,14 +1,9 @@
|
|
|
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
|
-
];
|
|
9
4
|
---
|
|
10
5
|
|
|
11
|
-
<Tag href={href} class:list={
|
|
12
|
-
{title && <h3
|
|
13
|
-
{description && <p
|
|
6
|
+
<Tag href={href} class:list={["docs-card", href && "docs-card-link", className]}>
|
|
7
|
+
{title && <h3>{title}</h3>}
|
|
8
|
+
{description && <p>{description}</p>}
|
|
14
9
|
</Tag>
|