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
|
@@ -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">
|
|
@@ -5,29 +5,26 @@ 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
7
|
import Menu from "lucide-static/icons/menu.svg?raw";
|
|
8
|
-
import Moon from "lucide-static/icons/moon.svg?raw";
|
|
9
8
|
import PanelLeft from "lucide-static/icons/panel-left.svg?raw";
|
|
10
9
|
import Search from "lucide-static/icons/search.svg?raw";
|
|
11
|
-
import Sun from "lucide-static/icons/sun.svg?raw";
|
|
12
|
-
import X from "lucide-static/icons/x.svg?raw";
|
|
13
10
|
import ClaudeLogo from "../../icons/logos/claude.svg?raw";
|
|
14
11
|
import CursorLogo from "../../icons/logos/cursor.svg?raw";
|
|
15
12
|
import MarkdownLogo from "../../icons/logos/markdown.svg?raw";
|
|
16
13
|
import OpenAILogo from "../../icons/logos/openai.svg?raw";
|
|
17
14
|
import BaseLayout from "./BaseLayout.astro";
|
|
18
15
|
import CommandDialog from "./CommandDialog.astro";
|
|
16
|
+
import ContentHeader from "virtual:reallysimpledocs/components/ContentHeader";
|
|
19
17
|
import DropdownMenu from "./DropdownMenu.astro";
|
|
20
18
|
import Sidebar from "./Sidebar.astro";
|
|
19
|
+
import ThemeToggle from "./ThemeToggle.astro";
|
|
21
20
|
import { buildMenus, getNavigation } from "../lib/docs.js";
|
|
22
21
|
import { addSvgClass } from "../lib/html.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
27
|
const { sidebar } = buildMenus(config, page.slug);
|
|
30
|
-
const searchIndexUrl = routeFilePath(config.routeBase, "search-index.json");
|
|
31
28
|
const panelLeftIcon = addSvgClass(PanelLeft, "size-4");
|
|
32
29
|
const searchIcon = addSvgClass(Search, "size-4");
|
|
33
30
|
const siteUrl = Astro.site?.toString().replace(/\/$/, "") || site.url || "";
|
|
@@ -66,11 +63,13 @@ const actionItems = [
|
|
|
66
63
|
---
|
|
67
64
|
|
|
68
65
|
<BaseLayout title={page.title} description={site.description} pagePath={page.path}>
|
|
69
|
-
<CommandDialog
|
|
66
|
+
<CommandDialog />
|
|
70
67
|
<Sidebar menu={sidebar} />
|
|
71
68
|
|
|
72
69
|
<main id="content">
|
|
73
|
-
<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
|
+
>
|
|
74
73
|
<div class="flex h-14 w-full items-center justify-between gap-2 px-4">
|
|
75
74
|
<button
|
|
76
75
|
type="button"
|
|
@@ -79,78 +78,42 @@ const actionItems = [
|
|
|
79
78
|
data-tooltip="Toggle sidebar"
|
|
80
79
|
data-side="bottom"
|
|
81
80
|
data-align="start"
|
|
82
|
-
class="btn
|
|
81
|
+
class="btn"
|
|
82
|
+
data-variant="ghost"
|
|
83
|
+
data-size="icon-sm"
|
|
83
84
|
>
|
|
84
85
|
<Fragment set:html={panelLeftIcon} />
|
|
85
86
|
</button>
|
|
86
87
|
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
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"
|
|
90
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(); }"
|
|
91
95
|
>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
</
|
|
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>
|
|
96
100
|
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
(site.links || []).map((link) => (
|
|
100
|
-
<a
|
|
101
|
-
class={link.attrs?.class || "btn-sm-outline"}
|
|
102
|
-
href={link.url}
|
|
103
|
-
aria-label={link.label}
|
|
104
|
-
data-tooltip={link.label}
|
|
105
|
-
data-side="bottom"
|
|
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>
|
|
101
|
+
<ContentHeader site={site} page={page} />
|
|
102
|
+
<ThemeToggle />
|
|
127
103
|
|
|
128
104
|
<div class="sm:hidden">
|
|
129
105
|
<button
|
|
130
106
|
type="button"
|
|
131
|
-
class="btn
|
|
132
|
-
|
|
107
|
+
class="btn peer group"
|
|
108
|
+
data-variant="ghost"
|
|
109
|
+
data-size="icon-sm"
|
|
110
|
+
aria-label="Toggle sidebar"
|
|
133
111
|
aria-expanded="false"
|
|
134
|
-
aria-controls="
|
|
135
|
-
onclick="
|
|
112
|
+
aria-controls="sidebar"
|
|
113
|
+
onclick="document.getElementById('sidebar')?.toggle()"
|
|
136
114
|
>
|
|
137
|
-
<
|
|
138
|
-
<span class="hidden group-aria-expanded:block"><Fragment set:html={X} /></span>
|
|
115
|
+
<Fragment set:html={Menu} />
|
|
139
116
|
</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
117
|
</div>
|
|
155
118
|
</div>
|
|
156
119
|
</header>
|
|
@@ -159,19 +122,20 @@ const actionItems = [
|
|
|
159
122
|
<article class="min-w-0 flex-1 wrap-break-word">
|
|
160
123
|
<div class="mx-auto w-full max-w-2xl flex-1 space-y-10">
|
|
161
124
|
<header class="space-y-2">
|
|
162
|
-
<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">
|
|
163
126
|
<h1 class="mr-auto text-3xl font-semibold tracking-tight sm:text-4xl">{page.title}</h1>
|
|
164
|
-
<div class="
|
|
127
|
+
<div class="flex w-full items-center justify-end gap-x-2 sm:mt-1 sm:w-auto">
|
|
165
128
|
<div role="group" class="button-group">
|
|
166
129
|
<button
|
|
167
130
|
type="button"
|
|
168
|
-
class="btn
|
|
131
|
+
class="btn"
|
|
132
|
+
data-variant="secondary"
|
|
133
|
+
data-size="sm"
|
|
169
134
|
data-md-url={page.markdownPath}
|
|
170
135
|
onclick="copyPageMarkdown(this)"
|
|
171
136
|
>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
<span>Copy page</span>
|
|
137
|
+
<Fragment set:html={Copy} />
|
|
138
|
+
Copy page
|
|
175
139
|
</button>
|
|
176
140
|
<hr role="separator" />
|
|
177
141
|
<DropdownMenu items={actionItems} />
|
|
@@ -179,7 +143,7 @@ const actionItems = [
|
|
|
179
143
|
<div role="group" class="button-group">
|
|
180
144
|
{
|
|
181
145
|
navigation.prev && (
|
|
182
|
-
<a href={navigation.prev.path} class="btn-
|
|
146
|
+
<a href={navigation.prev.path} class="btn" data-variant="secondary" data-size="icon-sm">
|
|
183
147
|
<span class="sr-only">{navigation.prev.title}</span>
|
|
184
148
|
<Fragment set:html={ArrowLeft} />
|
|
185
149
|
</a>
|
|
@@ -188,7 +152,7 @@ const actionItems = [
|
|
|
188
152
|
{navigation.prev && navigation.next && <hr role="separator" />}
|
|
189
153
|
{
|
|
190
154
|
navigation.next && (
|
|
191
|
-
<a href={navigation.next.path} class="btn-
|
|
155
|
+
<a href={navigation.next.path} class="btn" data-variant="secondary" data-size="icon-sm">
|
|
192
156
|
<span class="sr-only">{navigation.next.title}</span>
|
|
193
157
|
<Fragment set:html={ArrowRight} />
|
|
194
158
|
</a>
|
|
@@ -204,7 +168,7 @@ const actionItems = [
|
|
|
204
168
|
<footer class="mt-12 flex items-center">
|
|
205
169
|
{
|
|
206
170
|
navigation.prev && (
|
|
207
|
-
<a href={navigation.prev.path} class="btn-
|
|
171
|
+
<a href={navigation.prev.path} class="btn mr-auto" data-variant="secondary" data-size="sm">
|
|
208
172
|
<Fragment set:html={ArrowLeft} />
|
|
209
173
|
{navigation.prev.title}
|
|
210
174
|
</a>
|
|
@@ -212,7 +176,7 @@ const actionItems = [
|
|
|
212
176
|
}
|
|
213
177
|
{
|
|
214
178
|
navigation.next && (
|
|
215
|
-
<a href={navigation.next.path} class="btn-
|
|
179
|
+
<a href={navigation.next.path} class="btn ml-auto" data-variant="secondary" data-size="sm">
|
|
216
180
|
{navigation.next.title}
|
|
217
181
|
<Fragment set:html={ArrowRight} />
|
|
218
182
|
</a>
|
|
@@ -222,7 +186,7 @@ const actionItems = [
|
|
|
222
186
|
</div>
|
|
223
187
|
</article>
|
|
224
188
|
<aside class="hidden w-56 xl:block">
|
|
225
|
-
<div class="docs-toc sticky top-20 space-y-2 text-sm [&_a]:text-muted-foreground [&_a]:hover:text-foreground [&_a[
|
|
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>
|
|
226
190
|
<h2 class="flex items-center gap-1 font-medium">
|
|
227
191
|
On this page
|
|
228
192
|
</h2>
|
|
@@ -240,30 +204,29 @@ const actionItems = [
|
|
|
240
204
|
</div>
|
|
241
205
|
</main>
|
|
242
206
|
|
|
243
|
-
<script is:inline>
|
|
244
|
-
async function copyPageMarkdown(button) {
|
|
207
|
+
<script is:inline define:vars={{ checkIcon: Check }}>
|
|
208
|
+
window.copyPageMarkdown = async function copyPageMarkdown(button) {
|
|
245
209
|
const markdownUrl = button?.dataset?.mdUrl;
|
|
246
210
|
if (!markdownUrl) return;
|
|
247
|
-
const
|
|
248
|
-
const
|
|
211
|
+
const icon = button.querySelector("svg");
|
|
212
|
+
const originalIcon = icon?.outerHTML || "";
|
|
249
213
|
|
|
250
214
|
try {
|
|
251
215
|
const response = await fetch(markdownUrl, { credentials: "same-origin", cache: "no-store" });
|
|
252
216
|
if (!response.ok) throw new Error("Failed to fetch markdown");
|
|
253
217
|
const markdown = await response.text();
|
|
254
218
|
await writeToClipboard(markdown);
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
checkIcon.classList.remove("hidden");
|
|
219
|
+
if (icon && checkIcon) {
|
|
220
|
+
icon.outerHTML = checkIcon;
|
|
258
221
|
window.setTimeout(() => {
|
|
259
|
-
|
|
260
|
-
|
|
222
|
+
const currentIcon = button.querySelector("svg");
|
|
223
|
+
if (currentIcon && originalIcon) currentIcon.outerHTML = originalIcon;
|
|
261
224
|
}, 1200);
|
|
262
225
|
}
|
|
263
226
|
} catch (_) {
|
|
264
227
|
window.open(new URL(markdownUrl, window.location.href).toString(), "_blank", "noopener");
|
|
265
228
|
}
|
|
266
|
-
}
|
|
229
|
+
};
|
|
267
230
|
|
|
268
231
|
async function writeToClipboard(text) {
|
|
269
232
|
if (navigator.clipboard?.writeText && window.isSecureContext) {
|
|
@@ -286,55 +249,60 @@ const actionItems = [
|
|
|
286
249
|
|
|
287
250
|
function initDocsToc() {
|
|
288
251
|
const toc = document.querySelector("[data-docs-toc]");
|
|
289
|
-
|
|
252
|
+
const links = Array.from(toc?.querySelectorAll("[data-toc-link]") || []);
|
|
253
|
+
if (!links.length) return;
|
|
290
254
|
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const headings = Array.from(linkById.keys())
|
|
299
|
-
.map((id) => document.getElementById(id))
|
|
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
|
+
})
|
|
300
261
|
.filter(Boolean);
|
|
301
|
-
if (!
|
|
262
|
+
if (!entries.length) return;
|
|
302
263
|
|
|
303
|
-
const
|
|
304
|
-
for (const
|
|
305
|
-
const active =
|
|
306
|
-
link.
|
|
307
|
-
if (active) link.setAttribute("aria-current", "location");
|
|
308
|
-
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");
|
|
309
270
|
}
|
|
310
271
|
};
|
|
311
272
|
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
let current =
|
|
315
|
-
|
|
316
|
-
|
|
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;
|
|
317
279
|
else break;
|
|
318
280
|
}
|
|
319
|
-
return current;
|
|
320
|
-
};
|
|
321
281
|
|
|
322
|
-
|
|
323
|
-
|
|
282
|
+
activate(current.id);
|
|
283
|
+
};
|
|
324
284
|
|
|
325
|
-
let
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
updateActive();
|
|
285
|
+
let frame = 0;
|
|
286
|
+
const queueActiveUpdate = () => {
|
|
287
|
+
if (frame) return;
|
|
288
|
+
frame = window.requestAnimationFrame(() => {
|
|
289
|
+
frame = 0;
|
|
290
|
+
activeFromScroll();
|
|
332
291
|
});
|
|
333
292
|
};
|
|
334
293
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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));
|
|
338
306
|
}
|
|
339
307
|
|
|
340
308
|
initDocsToc();
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -4,8 +4,7 @@ import { addSvgClass } from "../../lib/html.js";
|
|
|
4
4
|
|
|
5
5
|
const { type, title = "Note", icon: iconName, class: className = "" } = Astro.props;
|
|
6
6
|
const normalizedType = String(type || "").toLowerCase();
|
|
7
|
-
const calloutType = normalizedType === "warning" || normalizedType === "
|
|
8
|
-
const danger = calloutType === "danger";
|
|
7
|
+
const calloutType = normalizedType === "warning" || normalizedType === "destructive" ? normalizedType : "";
|
|
9
8
|
|
|
10
9
|
function resolveNamedIcon(name) {
|
|
11
10
|
if (!name || typeof name !== "string") return "";
|
|
@@ -24,15 +23,9 @@ function resolveNamedIcon(name) {
|
|
|
24
23
|
const iconSource = typeof iconName === "string" ? resolveNamedIcon(iconName) : "";
|
|
25
24
|
const icon = addSvgClass(iconSource, "size-4");
|
|
26
25
|
const heading = title;
|
|
27
|
-
const variantClass =
|
|
28
|
-
calloutType === "warning"
|
|
29
|
-
? "[&>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"
|
|
30
|
-
: danger
|
|
31
|
-
? "[&>svg]:text-destructive [&>h3]:text-destructive [&>section]:text-destructive"
|
|
32
|
-
: "";
|
|
33
26
|
---
|
|
34
27
|
|
|
35
|
-
<div class:list={[
|
|
28
|
+
<div class:list={["alert", className]} data-variant={calloutType || undefined}>
|
|
36
29
|
{icon && <Fragment set:html={icon} />}
|
|
37
30
|
<h3>{heading}</h3>
|
|
38
31
|
<section>
|
|
@@ -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>
|
|
@@ -5,10 +5,6 @@ const { lang = "text", title, class: className = "" } = Astro.props;
|
|
|
5
5
|
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
6
6
|
const rawCode = decodeSlotText(slotHtml);
|
|
7
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
8
|
|
|
13
9
|
function decodeSlotText(value) {
|
|
14
10
|
return String(value || "")
|
|
@@ -23,17 +19,7 @@ function decodeSlotText(value) {
|
|
|
23
19
|
}
|
|
24
20
|
---
|
|
25
21
|
|
|
26
|
-
<div
|
|
27
|
-
|
|
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
|
-
}
|
|
22
|
+
<div class:list={["code-block", className]} data-code-title={title || undefined}>
|
|
23
|
+
{title && <header><span>{title}</span></header>}
|
|
38
24
|
<Fragment set:html={html} />
|
|
39
25
|
</div>
|