reallysimpledocs 1.0.0-beta.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +59 -39
  2. package/package.json +11 -11
  3. package/src/astro/index.js +64 -98
  4. package/src/astro/preview-scripts.js +140 -0
  5. package/src/astro/preview-source.js +150 -0
  6. package/src/astro/rehype-basecoat-tables.js +62 -0
  7. package/src/components/index.js +1 -0
  8. package/src/css/custom.css +197 -58
  9. package/src/css/styles/luma.css +9 -0
  10. package/src/css/styles/lyra.css +9 -0
  11. package/src/css/styles/maia.css +9 -0
  12. package/src/css/styles/mira.css +9 -0
  13. package/src/css/styles/nova.css +9 -0
  14. package/src/css/styles/rhea.css +9 -0
  15. package/src/css/styles/sera.css +9 -0
  16. package/src/css/styles/vega.css +9 -0
  17. package/src/runtime/components/BaseLayout.astro +22 -28
  18. package/src/runtime/components/CommandDialog.astro +52 -35
  19. package/src/runtime/components/DefaultContentHeader.astro +0 -17
  20. package/src/runtime/components/DefaultHead.astro +110 -0
  21. package/src/runtime/components/DefaultHeadContent.astro +2 -0
  22. package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
  23. package/src/runtime/components/DocsLayout.astro +99 -166
  24. package/src/runtime/components/DropdownMenu.astro +1 -1
  25. package/src/runtime/components/ThemeToggle.astro +21 -0
  26. package/src/runtime/components/mdx/Callout.astro +2 -32
  27. package/src/runtime/components/mdx/Card.astro +3 -8
  28. package/src/runtime/components/mdx/CardGrid.astro +1 -1
  29. package/src/runtime/components/mdx/Code.astro +21 -27
  30. package/src/runtime/components/mdx/CodeGroup.astro +114 -24
  31. package/src/runtime/components/mdx/Preview.astro +7 -13
  32. package/src/runtime/components/mdx/Steps.astro +1 -5
  33. package/src/runtime/components/mdx/Tab.astro +6 -0
  34. package/src/runtime/components/mdx/Tabs.astro +7 -7
  35. package/src/runtime/lib/code-meta.js +8 -0
  36. package/src/runtime/lib/docs.js +141 -119
  37. package/src/runtime/lib/highlight.js +1 -14
  38. package/src/runtime/lib/html.js +9 -0
  39. package/src/runtime/lib/macros.js +10 -5
  40. package/src/runtime/lib/markdown-export.js +240 -169
  41. package/src/runtime/pages/doc.astro +1 -3
  42. package/src/runtime/pages/llms-full.txt.ts +2 -2
  43. package/src/runtime/pages/llms.txt.ts +2 -2
  44. package/src/runtime/pages/markdown.ts +2 -2
  45. package/src/runtime/pages/search-index.ts +12 -3
  46. package/CHANGELOG.md +0 -35
  47. package/src/css/overrides.css +0 -1
  48. package/src/css/sources.css +0 -1
  49. package/src/runtime/lib/markdown-exporters/index.js +0 -144
  50. package/src/runtime/lib/mdx-source.js +0 -19
  51. package/src/runtime/lib/preview-source-plugin.js +0 -33
  52. package/src/runtime/scripts/copy-code.js +0 -130
  53. package/src/site/ContentHeader.astro +0 -33
@@ -6,47 +6,26 @@ import Check from "lucide-static/icons/check.svg?raw";
6
6
  import Copy from "lucide-static/icons/copy.svg?raw";
7
7
  import PanelLeft from "lucide-static/icons/panel-left.svg?raw";
8
8
  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
9
  import ClaudeLogo from "../../icons/logos/claude.svg?raw";
13
10
  import CursorLogo from "../../icons/logos/cursor.svg?raw";
14
11
  import MarkdownLogo from "../../icons/logos/markdown.svg?raw";
15
12
  import OpenAILogo from "../../icons/logos/openai.svg?raw";
16
13
  import BaseLayout from "./BaseLayout.astro";
17
14
  import CommandDialog from "./CommandDialog.astro";
15
+ import ContentHeader from "virtual:reallysimpledocs/components/ContentHeader";
18
16
  import DropdownMenu from "./DropdownMenu.astro";
19
17
  import Sidebar from "./Sidebar.astro";
20
- import { buildMenus, getBreadcrumbs, getNavigation } from "../lib/docs.js";
18
+ import ThemeToggle from "./ThemeToggle.astro";
19
+ import { buildMenus, getNavigation } from "../lib/docs.js";
21
20
  import { addSvgClass } from "../lib/html.js";
22
- import { renderDropdownMenu } from "../lib/macros.js";
23
- import { routeFilePath } from "../lib/paths.js";
24
21
  import { getSite } from "../lib/site.js";
25
22
 
26
23
  const { page, headings } = Astro.props;
27
24
  const site = getSite(config);
28
25
  const navigation = getNavigation(config, page);
29
- const breadcrumbs = getBreadcrumbs(config, page);
30
26
  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
27
  const panelLeftIcon = addSvgClass(PanelLeft, "size-4");
36
28
  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
29
  const siteUrl = Astro.site?.toString().replace(/\/$/, "") || site.url || "";
51
30
  const pageUrl = `${siteUrl}${page.path}`;
52
31
  const llmPrompt = `Read from ${pageUrl} so I can ask questions about it.`;
@@ -83,11 +62,13 @@ const actionItems = [
83
62
  ---
84
63
 
85
64
  <BaseLayout title={page.title} description={site.description} pagePath={page.path}>
86
- <CommandDialog indexUrl={searchIndexUrl} />
65
+ <CommandDialog />
87
66
  <Sidebar menu={sidebar} />
88
67
 
89
68
  <main id="content">
90
- <header class="bg-background sticky inset-x-0 top-0 isolate z-10 flex shrink-0 items-center gap-2 border-b">
69
+ <header
70
+ class="sticky inset-x-0 top-0 isolate z-30 flex shrink-0 items-center gap-2 border-b bg-background"
71
+ >
91
72
  <div class="flex h-14 w-full items-center justify-between gap-2 px-4">
92
73
  <button
93
74
  type="button"
@@ -96,79 +77,28 @@ const actionItems = [
96
77
  data-tooltip="Toggle sidebar"
97
78
  data-side="bottom"
98
79
  data-align="start"
99
- class="btn-sm-icon-ghost"
80
+ class="btn"
81
+ data-variant="ghost"
82
+ data-size="icon-sm"
100
83
  >
101
84
  <Fragment set:html={panelLeftIcon} />
102
85
  </button>
103
86
 
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
-
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"
87
+ <div
88
+ class="input-group h-8 w-full min-w-0 max-w-72 cursor-text sm:ml-auto"
89
+ role="button"
90
+ tabindex="0"
91
+ aria-label="Search docs"
156
92
  onclick="window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal()"
93
+ onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.rsdOpenCommandSearch ? window.rsdOpenCommandSearch() : document.getElementById('command-search')?.showModal(); }"
157
94
  >
158
- <Fragment set:html={searchIcon} />
159
- </button>
160
-
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>
95
+ <input type="text" placeholder="Search..." readonly tabindex="-1" aria-hidden="true" />
96
+ <span data-align="start" aria-hidden="true"><Fragment set:html={searchIcon} /></span>
97
+ <span data-align="end" aria-hidden="true"><kbd class="kbd">⌘K</kbd></span>
98
+ </div>
170
99
 
171
- <ContentHeader site={site} page={page} navigation={navigation} config={config} />
100
+ <ContentHeader site={site} page={page} />
101
+ <ThemeToggle />
172
102
  </div>
173
103
  </header>
174
104
 
@@ -176,19 +106,20 @@ const actionItems = [
176
106
  <article class="min-w-0 flex-1 wrap-break-word">
177
107
  <div class="mx-auto w-full max-w-2xl flex-1 space-y-10">
178
108
  <header class="space-y-2">
179
- <div class="flex items-start gap-x-2">
109
+ <div class="flex flex-col-reverse items-start gap-3 sm:flex-row sm:gap-x-2">
180
110
  <h1 class="mr-auto text-3xl font-semibold tracking-tight sm:text-4xl">{page.title}</h1>
181
- <div class="mt-1 flex items-center gap-x-2">
111
+ <div class="flex w-full items-center justify-end gap-x-2 sm:mt-1 sm:w-auto">
182
112
  <div role="group" class="button-group">
183
113
  <button
184
114
  type="button"
185
- class="btn-sm-secondary"
115
+ class="btn"
116
+ data-variant="secondary"
117
+ data-size="sm"
186
118
  data-md-url={page.markdownPath}
187
119
  onclick="copyPageMarkdown(this)"
188
120
  >
189
- <span data-copy-icon><Fragment set:html={Copy} /></span>
190
- <span data-check-icon class="hidden"><Fragment set:html={Check} /></span>
191
- <span>Copy page</span>
121
+ <Fragment set:html={Copy} />
122
+ Copy page
192
123
  </button>
193
124
  <hr role="separator" />
194
125
  <DropdownMenu items={actionItems} />
@@ -196,7 +127,7 @@ const actionItems = [
196
127
  <div role="group" class="button-group">
197
128
  {
198
129
  navigation.prev && (
199
- <a href={navigation.prev.path} class="btn-sm-icon-secondary">
130
+ <a href={navigation.prev.path} class="btn" data-variant="secondary" data-size="icon-sm">
200
131
  <span class="sr-only">{navigation.prev.title}</span>
201
132
  <Fragment set:html={ArrowLeft} />
202
133
  </a>
@@ -205,7 +136,7 @@ const actionItems = [
205
136
  {navigation.prev && navigation.next && <hr role="separator" />}
206
137
  {
207
138
  navigation.next && (
208
- <a href={navigation.next.path} class="btn-sm-icon-secondary">
139
+ <a href={navigation.next.path} class="btn" data-variant="secondary" data-size="icon-sm">
209
140
  <span class="sr-only">{navigation.next.title}</span>
210
141
  <Fragment set:html={ArrowRight} />
211
142
  </a>
@@ -221,7 +152,7 @@ const actionItems = [
221
152
  <footer class="mt-12 flex items-center">
222
153
  {
223
154
  navigation.prev && (
224
- <a href={navigation.prev.path} class="btn-sm-secondary mr-auto">
155
+ <a href={navigation.prev.path} class="btn mr-auto" data-variant="secondary" data-size="sm">
225
156
  <Fragment set:html={ArrowLeft} />
226
157
  {navigation.prev.title}
227
158
  </a>
@@ -229,7 +160,7 @@ const actionItems = [
229
160
  }
230
161
  {
231
162
  navigation.next && (
232
- <a href={navigation.next.path} class="btn-sm-secondary ml-auto">
163
+ <a href={navigation.next.path} class="btn ml-auto" data-variant="secondary" data-size="sm">
233
164
  {navigation.next.title}
234
165
  <Fragment set:html={ArrowRight} />
235
166
  </a>
@@ -238,51 +169,48 @@ const actionItems = [
238
169
  </footer>
239
170
  </div>
240
171
  </article>
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
- }
172
+ <aside class="hidden w-56 xl:block">
173
+ <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>
174
+ <h2 class="flex items-center gap-1 font-medium">
175
+ On this page
176
+ </h2>
177
+ <ol>
178
+ {
179
+ headings.map((heading) => (
180
+ <li class={heading.depth === 3 ? "pl-3" : ""}>
181
+ <a href={`#${heading.id}`} data-toc-link>{heading.text}</a>
182
+ </li>
183
+ ))
184
+ }
185
+ </ol>
186
+ </div>
187
+ </aside>
259
188
  </div>
260
189
  </main>
261
190
 
262
- <script is:inline>
263
- async function copyPageMarkdown(button) {
191
+ <script is:inline define:vars={{ checkIcon: Check }}>
192
+ window.copyPageMarkdown = async function copyPageMarkdown(button) {
264
193
  const markdownUrl = button?.dataset?.mdUrl;
265
194
  if (!markdownUrl) return;
266
- const copyIcon = button.querySelector("[data-copy-icon]");
267
- const checkIcon = button.querySelector("[data-check-icon]");
195
+ const icon = button.querySelector("svg");
196
+ const originalIcon = icon?.outerHTML || "";
268
197
 
269
198
  try {
270
199
  const response = await fetch(markdownUrl, { credentials: "same-origin", cache: "no-store" });
271
200
  if (!response.ok) throw new Error("Failed to fetch markdown");
272
201
  const markdown = await response.text();
273
202
  await writeToClipboard(markdown);
274
- if (copyIcon && checkIcon) {
275
- copyIcon.classList.add("hidden");
276
- checkIcon.classList.remove("hidden");
203
+ if (icon && checkIcon) {
204
+ icon.outerHTML = checkIcon;
277
205
  window.setTimeout(() => {
278
- copyIcon.classList.remove("hidden");
279
- checkIcon.classList.add("hidden");
206
+ const currentIcon = button.querySelector("svg");
207
+ if (currentIcon && originalIcon) currentIcon.outerHTML = originalIcon;
280
208
  }, 1200);
281
209
  }
282
210
  } catch (_) {
283
211
  window.open(new URL(markdownUrl, window.location.href).toString(), "_blank", "noopener");
284
212
  }
285
- }
213
+ };
286
214
 
287
215
  async function writeToClipboard(text) {
288
216
  if (navigator.clipboard?.writeText && window.isSecureContext) {
@@ -305,55 +233,60 @@ const actionItems = [
305
233
 
306
234
  function initDocsToc() {
307
235
  const toc = document.querySelector("[data-docs-toc]");
308
- if (!toc) return;
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
- }
236
+ const links = Array.from(toc?.querySelectorAll("[data-toc-link]") || []);
237
+ if (!links.length) return;
316
238
 
317
- const headings = Array.from(linkById.keys())
318
- .map((id) => document.getElementById(id))
239
+ const entries = links
240
+ .map((link) => {
241
+ const id = decodeURIComponent(link.hash.slice(1));
242
+ const heading = id ? document.getElementById(id) : null;
243
+ return heading ? { id, heading, link } : null;
244
+ })
319
245
  .filter(Boolean);
320
- if (!headings.length) return;
246
+ if (!entries.length) return;
321
247
 
322
- const setActive = (id) => {
323
- for (const link of links) {
324
- const active = decodeURIComponent(link.hash.slice(1)) === id;
325
- link.dataset.active = active ? "true" : "false";
326
- if (active) link.setAttribute("aria-current", "location");
327
- else link.removeAttribute("aria-current");
248
+ const activate = (id) => {
249
+ for (const entry of entries) {
250
+ const active = entry.id === id;
251
+ entry.link.toggleAttribute("data-active", active);
252
+ if (active) entry.link.setAttribute("aria-current", "location");
253
+ else entry.link.removeAttribute("aria-current");
328
254
  }
329
255
  };
330
256
 
331
- const currentHeading = () => {
332
- const offset = 160;
333
- let current = headings[0];
334
- for (const heading of headings) {
335
- if (heading.getBoundingClientRect().top <= offset) current = heading;
257
+ const activeFromScroll = () => {
258
+ const marker = Math.min(window.innerHeight * 0.35, 220);
259
+ let current = entries[0];
260
+
261
+ for (const entry of entries) {
262
+ if (entry.heading.getBoundingClientRect().top <= marker) current = entry;
336
263
  else break;
337
264
  }
338
- return current;
339
- };
340
265
 
341
- const updateActive = () => setActive(currentHeading().id);
342
- updateActive();
266
+ activate(current.id);
267
+ };
343
268
 
344
- let queued = false;
345
- const queueUpdate = () => {
346
- if (queued) return;
347
- queued = true;
348
- window.requestAnimationFrame(() => {
349
- queued = false;
350
- updateActive();
269
+ let frame = 0;
270
+ const queueActiveUpdate = () => {
271
+ if (frame) return;
272
+ frame = window.requestAnimationFrame(() => {
273
+ frame = 0;
274
+ activeFromScroll();
351
275
  });
352
276
  };
353
277
 
354
- window.addEventListener("scroll", queueUpdate, { passive: true });
355
- window.addEventListener("resize", queueUpdate);
356
- window.addEventListener("hashchange", () => window.setTimeout(updateActive, 0));
278
+ if ("IntersectionObserver" in window) {
279
+ const observer = new IntersectionObserver(queueActiveUpdate, {
280
+ rootMargin: "-20% 0px -65% 0px",
281
+ threshold: [0, 1],
282
+ });
283
+ for (const entry of entries) observer.observe(entry.heading);
284
+ }
285
+
286
+ activeFromScroll();
287
+ window.addEventListener("scroll", queueActiveUpdate, { passive: true });
288
+ window.addEventListener("resize", queueActiveUpdate);
289
+ window.addEventListener("hashchange", () => window.setTimeout(activeFromScroll, 0));
357
290
  }
358
291
 
359
292
  initDocsToc();
@@ -16,7 +16,7 @@ const { items } = Astro.props;
16
16
  rel: item.rel,
17
17
  },
18
18
  })),
19
- triggerAttrs: { class: "btn-sm-icon-secondary" },
19
+ triggerAttrs: { class: "btn", "data-variant": "secondary", "data-size": "icon-sm" },
20
20
  popoverAttrs: { "data-align": "end" },
21
21
  })}
22
22
  />
@@ -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="flex items-center gap-2">
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, action, class: className = "" } = Astro.props;
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={[destructive ? "alert-destructive" : "alert", "my-6", variantClass, className]}>
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={classes}>
12
- {title && <h3 class="font-medium">{title}</h3>}
13
- {description && <p class="mt-1 text-sm text-muted-foreground">{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>
@@ -2,6 +2,6 @@
2
2
  const { class: className = "" } = Astro.props;
3
3
  ---
4
4
 
5
- <div class:list={["my-6 grid w-full gap-4 sm:grid-cols-2 [&>*]:my-0", className]}>
5
+ <div class:list={["grid w-full gap-4 sm:grid-cols-2", className]}>
6
6
  <slot />
7
7
  </div>
@@ -1,39 +1,33 @@
1
1
  ---
2
2
  import { highlightCode } from "../../lib/highlight.js";
3
+ import { decodeHtml } from "../../lib/html.js";
3
4
 
4
- const { lang = "text", title, class: className = "" } = Astro.props;
5
+ const { lang = "text", title, code, class: className = "" } = Astro.props;
5
6
  const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
6
- const rawCode = decodeSlotText(slotHtml);
7
+ const rawCode = typeof code === "string" ? code.trim() : decodeSlotText(slotHtml);
7
8
  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
9
 
13
10
  function decodeSlotText(value) {
14
- return String(value || "")
11
+ const text = String(value || "")
15
12
  .replace(/^<p>/, "")
16
- .replace(/<\/p>$/, "")
17
- .replace(/&lt;/g, "<")
18
- .replace(/&gt;/g, ">")
19
- .replace(/&amp;/g, "&")
20
- .replace(/&quot;/g, '"')
21
- .replace(/&#39;/g, "'")
22
- .trim();
13
+ .replace(/<\/p>$/, "");
14
+ return decodeHtml(text).trim();
23
15
  }
24
16
  ---
25
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>
18
+ {
19
+ title ? (
20
+ <div class:list={["code-frame", className]} data-code-title={title}>
21
+ <header>
22
+ <span>{title}</span>
35
23
  </header>
36
- )
37
- }
38
- <Fragment set:html={html} />
39
- </div>
24
+ <div class="code-block">
25
+ <Fragment set:html={html} />
26
+ </div>
27
+ </div>
28
+ ) : (
29
+ <div class:list={["code-block", className]}>
30
+ <Fragment set:html={html} />
31
+ </div>
32
+ )
33
+ }