Skip to content

Commit 9b49ee4

Browse files
committed
docs
1 parent b5bd1ff commit 9b49ee4

File tree

2 files changed

+75
-72
lines changed

2 files changed

+75
-72
lines changed
Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,82 @@
11
---
2-
export interface Props {
3-
content: string;
4-
}
2+
export interface Props { content: string }
53
64
const { content } = Astro.props;
75
8-
function processCallouts(htmlContent: string): string {
9-
// Define callout types and mapping
10-
const CALLOUT_TYPES = "NOTE|TIP|WARNING|IMPORTANT|CAUTION";
11-
const CALLOUT_MAP: Record<string, string> = {
12-
NOTE: "note",
13-
TIP: "tip",
14-
WARNING: "caution",
15-
IMPORTANT: "note",
16-
CAUTION: "danger",
17-
};
18-
19-
// Helper function to generate Starlight aside HTML
20-
const createAside = (type: string, content: string): string => {
21-
const starlightType = CALLOUT_MAP[type.toUpperCase()] ?? "note";
22-
return `<div class="starlight-aside starlight-aside--${starlightType}"><p class="starlight-aside__title">${type}</p><div class="starlight-aside__content"><p>${content}</p></div></div>`;
23-
};
6+
// Precompiled constants & regexes (outside function to avoid reallocation per render)
7+
const CALLOUT_TYPES = ["NOTE", "TIP", "WARNING", "IMPORTANT", "CAUTION"] as const;
8+
type CalloutType = typeof CALLOUT_TYPES[number];
9+
const CALLOUT_TYPE_PATTERN = CALLOUT_TYPES.join("|");
10+
const CALLOUT_MARKER_RE = new RegExp(`\\[!(${CALLOUT_TYPE_PATTERN})\\]`, "i");
11+
const BLOCKQUOTE_RE = new RegExp(`<blockquote[^>]*>([\\s\\S]*?)<\\/blockquote>`, "gi");
12+
// Markdown pattern: > [!TYPE]\n> line ... (stops at first blank or non > line)
13+
const MD_CALLOUT_RE = new RegExp(
14+
`(^|\n)>\\s*\\[!(${CALLOUT_TYPE_PATTERN})\\]\\s*\n((?:>.*(?:\n|$))+)(?=\n[^>]|")?`,
15+
"gi"
16+
);
2417
25-
// Helper function to clean HTML content
26-
const cleanHtmlContent = (match: string): string =>
27-
match
28-
.replace(/<blockquote[^>]*>/gi, "")
29-
.replace(/<\/blockquote>/gi, "")
30-
.replace(
31-
new RegExp(`<p>\\s*\\[!(${CALLOUT_TYPES})\\]\\s*<\\/p>`, "gi"),
32-
"",
33-
)
34-
.replace(new RegExp(`\\[!(${CALLOUT_TYPES})\\]`, "gi"), "")
35-
.replace(/<p>(.*?)<\/p>/gs, "$1 ")
36-
.replace(/<(?!code|\/code)[^>]*>/g, "") // Preserve <code> tags
37-
.replace(/\s+/g, " ")
38-
.trim();
18+
// Mapping to starlight aside variants
19+
const CALLOUT_MAP: Record<string, string> = {
20+
NOTE: "note",
21+
TIP: "tip",
22+
WARNING: "caution",
23+
IMPORTANT: "note",
24+
CAUTION: "danger",
25+
};
3926
40-
// Helper function to clean markdown content
41-
const cleanMarkdownContent = (content: string): string =>
42-
content
43-
.split("\n")
44-
.map((line: string) => line.replace(/^>\s*/, "").trim())
45-
.filter((line: string) => line.length > 0)
46-
.join(" ");
27+
function createAside(rawType: string, innerHtml: string): string {
28+
const type = (rawType || "NOTE").toUpperCase() as CalloutType;
29+
const variant = CALLOUT_MAP[type] ?? "note";
30+
// Avoid double-wrapping if already processed
31+
if (/starlight-aside__title/i.test(innerHtml)) return innerHtml;
32+
return `<div class="starlight-aside starlight-aside--${variant}" role="note" aria-label="${type} callout"><p class="starlight-aside__title">${type}</p><div class="starlight-aside__content">${innerHtml}</div></div>`;
33+
}
4734
48-
let processedContent = htmlContent;
35+
function stripFirstMarkerLine(block: string): { type: string; html: string } {
36+
const markerLineRe = new RegExp(`^\\s*(?:<p>)?\\s*\\[!(${CALLOUT_TYPE_PATTERN})\\]\\s*(?:<\\/p>)?\\s*`, "i");
37+
const match = block.match(CALLOUT_MARKER_RE);
38+
const type = match?.[1] || "NOTE";
39+
const html = block.replace(markerLineRe, "").trim();
40+
return { type, html };
41+
}
4942
50-
// Process blockquote-based callouts
51-
processedContent = processedContent.replace(
52-
new RegExp(
53-
`<blockquote[^>]*>[\\s\\S]*?\\[!(${CALLOUT_TYPES})\\][\\s\\S]*?<\\/blockquote>`,
54-
"gi",
55-
),
56-
(match: string) => {
57-
const typeMatch = match.match(
58-
new RegExp(`\\[!(${CALLOUT_TYPES})\\]`, "i"),
59-
);
60-
const type = typeMatch?.[1] ?? "NOTE";
61-
const cleanContent = cleanHtmlContent(match);
62-
return createAside(type, cleanContent);
63-
},
64-
);
43+
function processBlockquoteCallouts(src: string): string {
44+
return src.replace(BLOCKQUOTE_RE, (full, inner) => {
45+
if (!CALLOUT_MARKER_RE.test(inner)) return full; // leave normal blockquotes
46+
const { type, html } = stripFirstMarkerLine(inner);
47+
return createAside(type, html);
48+
});
49+
}
6550
66-
// Process markdown-style callouts
67-
processedContent = processedContent.replace(
68-
new RegExp(
69-
`>\\s*\\[!(${CALLOUT_TYPES})\\]\\s*\\n((?:>\\s*.*(?:\\n|$))*)`,
70-
"gim",
71-
),
72-
(match: string, type: string, content: string) => {
73-
const cleanContent = cleanMarkdownContent(content);
74-
return createAside(type, cleanContent);
75-
},
76-
);
51+
function processMarkdownCallouts(src: string): string {
52+
return src.replace(MD_CALLOUT_RE, (full, _lead, type, lines) => {
53+
// Remove leading ">" markers & blank lines
54+
const cleaned = lines
55+
.split(/\n/)
56+
.map((l: string) => l.replace(/^>\s?/, ""))
57+
.filter((l: string) => l.trim().length > 0)
58+
.join("\n");
59+
return `\n${createAside(type, cleaned)}\n`;
60+
});
61+
}
7762
78-
return processedContent;
63+
function processCallouts(htmlContent: string): string {
64+
if (!htmlContent || typeof htmlContent !== "string") return htmlContent;
65+
// Quick escape if no markers
66+
if (!CALLOUT_MARKER_RE.test(htmlContent)) return htmlContent;
67+
let out = htmlContent;
68+
out = processBlockquoteCallouts(out);
69+
out = processMarkdownCallouts(out);
70+
return out;
7971
}
8072
81-
const processedContent = processCallouts(content);
73+
let processedContent: string;
74+
try {
75+
processedContent = processCallouts(content);
76+
} catch (e) {
77+
// Fail open: render original content if processing breaks
78+
processedContent = content;
79+
}
8280
---
8381

8482
<div set:html={processedContent} />

docs/src/components/advancedButton.astro

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,24 @@
192192
}
193193

194194
if (input) {
195+
let _reloading = false;
195196
const handler = () => {
196197
const next = input.checked;
197198
try { localStorage.setItem('advanced', next ? 'true' : 'false'); } catch {}
198-
if (next) {
199-
// Ensure dataset reflects the new mode, then reload to fully hydrate late content
199+
if (next) { // turning ON advanced
200+
if (_reloading) return; // guard against duplicate events
201+
_reloading = true;
202+
// Reflect state for a11y just before navigation
203+
input.setAttribute('aria-checked', 'true');
200204
document.documentElement.dataset.advanced = 'true';
205+
// Full reload to allow late-hydrated components to render in advanced mode without manual refresh
201206
window.location.reload();
202207
} else {
203208
setState(next);
204209
}
205210
};
211+
// 'change' covers user interaction; avoid also listening to 'click' to prevent double firing
206212
input.addEventListener('change', handler);
207-
input.addEventListener('click', handler, { passive: true });
208213
}
209214
};
210215

0 commit comments

Comments
 (0)