sourcey 3.4.1 → 3.4.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/dist/client/scroll-tracker.js +168 -156
- package/dist/client/search.js +186 -179
- package/dist/client/sidebar.js +56 -48
- package/dist/client/tabs.js +154 -146
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +4 -12
- package/dist/components/layout/Header.js +2 -2
- package/dist/components/layout/Page.d.ts.map +1 -1
- package/dist/components/layout/Page.js +9 -9
- package/dist/components/layout/Sidebar.js +6 -6
- package/dist/components/openapi/EndpointBar.js +5 -5
- package/dist/components/openapi/Introduction.js +1 -1
- package/dist/components/openapi/Responses.js +4 -4
- package/dist/components/openapi/Security.d.ts.map +1 -1
- package/dist/components/openapi/Security.js +2 -4
- package/dist/components/ui/SectionLabel.js +1 -1
- package/dist/config.js +3 -3
- package/dist/core/doxygen-loader.d.ts +1 -0
- package/dist/core/doxygen-loader.d.ts.map +1 -1
- package/dist/core/doxygen-loader.js +12 -3
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +111 -12
- package/dist/dev-server.js +2 -1
- package/dist/index.js +1 -1
- package/dist/init.js +1 -1
- package/dist/themes/default/main.css +3 -3
- package/dist/themes/default/sourcey.css +38 -48
- package/dist/utils/markdown.d.ts +10 -0
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +29 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { useContext } from "preact/hooks";
|
|
3
3
|
import { SpecContext } from "../../renderer/context.js";
|
|
4
4
|
import { SectionLabel } from "../ui/SectionLabel.js";
|
|
@@ -26,7 +26,5 @@ export function SecurityDefinitions() {
|
|
|
26
26
|
const schemes = Object.entries(spec.securitySchemes);
|
|
27
27
|
if (!schemes.length)
|
|
28
28
|
return null;
|
|
29
|
-
return (_jsxs("div", { id: "authentication", class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": "authentication", children: [_jsx("h2", { class: "text-xl font-bold text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] mb-4", children: "Authentication" }), _jsx("div", { class: "params-list", children: schemes.map(([name, scheme]) => (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), _jsx("span", { class: "param-type", children: _jsx("span", { class: "json-property-type", children: scheme.type }) })] }), _jsxs("div", { class: "param-description", children: [scheme.description && _jsx(Markdown, { content: scheme.description }), scheme.type === "apiKey" && (_jsxs("p", { class: "mt-1", children: ["API Key: ", _jsx("code", { class: "text-xs font-medium", children: scheme.name }), " in ", scheme.in] })), scheme.type === "http" && (_jsxs("p", { class: "mt-1", children: ["Scheme: ", scheme.scheme, scheme.bearerFormat && ` (${scheme.bearerFormat})`] })), scheme.type === "oauth2" && scheme.flows && (_jsx("div", { class: "mt-2 space-y-
|
|
30
|
-
.map(([s, desc]) => `${s} — ${desc}`)
|
|
31
|
-
.join(", ")] }))] }, flowType))) })), scheme.type === "openIdConnect" && scheme.openIdConnectUrl && (_jsxs("p", { class: "mt-1", children: ["OpenID Connect:", " ", _jsx("a", { href: scheme.openIdConnectUrl, class: "text-[rgb(var(--color-primary))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.openIdConnectUrl })] }))] })] }, name))) })] }));
|
|
29
|
+
return (_jsxs("div", { id: "authentication", class: "py-8 border-t border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))]", "data-traverse-target": "authentication", children: [_jsx("h2", { class: "text-xl font-bold text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))] mb-4", children: "Authentication" }), _jsx("div", { class: "params-list", children: schemes.map(([name, scheme]) => (_jsxs("div", { class: "param-item", children: [_jsxs("div", { class: "param-header", children: [_jsx("code", { class: "param-name", children: name }), _jsx("span", { class: "param-type", children: _jsx("span", { class: "json-property-type", children: scheme.type }) })] }), _jsxs("div", { class: "param-description", children: [scheme.description && _jsx(Markdown, { content: scheme.description }), scheme.type === "apiKey" && (_jsxs("p", { class: "mt-1", children: ["API Key: ", _jsx("code", { class: "text-xs font-medium", children: scheme.name }), " in ", scheme.in] })), scheme.type === "http" && (_jsxs("p", { class: "mt-1", children: ["Scheme: ", scheme.scheme, scheme.bearerFormat && ` (${scheme.bearerFormat})`] })), scheme.type === "oauth2" && scheme.flows && (_jsx("div", { class: "mt-2 space-y-4", children: Object.entries(scheme.flows).map(([flowType, flow]) => (_jsxs("div", { children: [_jsx("strong", { class: "text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))]", children: flowType }), _jsxs("dl", { class: "mt-2 text-sm", children: [flow?.authorizationUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Authorization URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.authorizationUrl }) })] })), flow?.tokenUrl && (_jsxs(_Fragment, { children: [_jsx("dt", { class: "text-[rgb(var(--color-gray-500))] mt-2", children: "Token URL" }), _jsx("dd", { children: _jsx("code", { class: "text-xs", children: flow.tokenUrl }) })] }))] }), flow?.scopes && Object.keys(flow.scopes).length > 0 && (_jsxs("div", { class: "mt-3", children: [_jsx("span", { class: "text-sm text-[rgb(var(--color-gray-500))]", children: "Scopes" }), _jsx("ul", { class: "mt-1 space-y-1", children: Object.entries(flow.scopes).map(([s, desc]) => (_jsxs("li", { class: "text-sm", children: [_jsx("code", { class: "text-xs font-medium text-[rgb(var(--color-gray-800))] dark:text-[rgb(var(--color-gray-200))]", children: s }), _jsxs("span", { class: "text-[rgb(var(--color-gray-500))] ml-2", children: ["\u2014 ", desc] })] }, s))) })] }))] }, flowType))) })), scheme.type === "openIdConnect" && scheme.openIdConnectUrl && (_jsxs("p", { class: "mt-1", children: ["OpenID Connect:", " ", _jsx("a", { href: scheme.openIdConnectUrl, class: "text-[rgb(var(--color-primary-ink))] dark:text-[rgb(var(--color-primary-light))]", children: scheme.openIdConnectUrl })] }))] })] }, name))) })] }));
|
|
32
30
|
}
|
|
@@ -4,5 +4,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
|
4
4
|
* Used for "Body", "Parameters", "Response" sections.
|
|
5
5
|
*/
|
|
6
6
|
export function SectionLabel({ children, meta }) {
|
|
7
|
-
return (_jsxs("div", { class: "flex items-baseline border-b pb-2.5 border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))] w-full mb-4", children: [_jsx("
|
|
7
|
+
return (_jsxs("div", { class: "flex items-baseline border-b pb-2.5 border-[rgb(var(--color-gray-100))] dark:border-[rgb(var(--color-gray-800))] w-full mb-4", children: [_jsx("h3", { class: "flex-1 mb-0 text-sm font-semibold text-[rgb(var(--color-gray-900))] dark:text-[rgb(var(--color-gray-200))]", children: children }), meta && (_jsx("div", { class: "flex items-center gap-2 text-xs font-medium font-mono text-[rgb(var(--color-gray-500))]", children: meta }))] }));
|
|
8
8
|
}
|
package/dist/config.js
CHANGED
|
@@ -13,8 +13,8 @@ const DEFAULT_COLORS = {
|
|
|
13
13
|
dark: "79 70 229",
|
|
14
14
|
};
|
|
15
15
|
const DEFAULT_FONTS = {
|
|
16
|
-
sans: "
|
|
17
|
-
mono: "
|
|
16
|
+
sans: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
17
|
+
mono: "ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, 'Liberation Mono', Menlo, monospace",
|
|
18
18
|
};
|
|
19
19
|
const DEFAULT_LAYOUT = {
|
|
20
20
|
sidebar: "18rem",
|
|
@@ -73,7 +73,7 @@ export async function resolveConfigFromRaw(raw, configDir) {
|
|
|
73
73
|
name: raw.name ?? "",
|
|
74
74
|
theme,
|
|
75
75
|
logo,
|
|
76
|
-
favicon: raw.favicon,
|
|
76
|
+
favicon: raw.favicon && !raw.favicon.startsWith("http") && !raw.favicon.startsWith("data:") ? resolve(configDir, raw.favicon) : raw.favicon,
|
|
77
77
|
repo: raw.repo,
|
|
78
78
|
editBranch: raw.editBranch,
|
|
79
79
|
editBasePath: raw.editBasePath,
|
|
@@ -5,5 +5,6 @@ export interface DoxygenResult {
|
|
|
5
5
|
pages: Map<string, MarkdownPage>;
|
|
6
6
|
navTab: SiteTab;
|
|
7
7
|
}
|
|
8
|
+
export declare function normalizeDoxygenDescription(description: string, markdown: string): string;
|
|
8
9
|
export declare function loadDoxygenTab(config: ResolvedDoxygenConfig, tabSlug: string, tabLabel: string): Promise<DoxygenResult>;
|
|
9
10
|
//# sourceMappingURL=doxygen-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CA6FxB"}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { generate } from "moxygen";
|
|
2
|
-
import { renderMarkdown, extractHeadings } from "../utils/markdown.js";
|
|
2
|
+
import { renderMarkdown, extractHeadings, extractFirstParagraph, stripMarkdownLinks } from "../utils/markdown.js";
|
|
3
|
+
export function normalizeDoxygenDescription(description, markdown) {
|
|
4
|
+
if (!description)
|
|
5
|
+
return "";
|
|
6
|
+
if (!description.includes("{#ref "))
|
|
7
|
+
return description;
|
|
8
|
+
const firstParagraph = extractFirstParagraph(markdown);
|
|
9
|
+
return firstParagraph || description;
|
|
10
|
+
}
|
|
3
11
|
export async function loadDoxygenTab(config, tabSlug, tabLabel) {
|
|
4
12
|
const generated = await generate({
|
|
5
13
|
directory: config.xml,
|
|
@@ -11,11 +19,12 @@ export async function loadDoxygenTab(config, tabSlug, tabLabel) {
|
|
|
11
19
|
for (const page of generated) {
|
|
12
20
|
if (!page.markdown.trim())
|
|
13
21
|
continue;
|
|
22
|
+
const description = normalizeDoxygenDescription(page.description, page.markdown);
|
|
14
23
|
const html = renderMarkdown(page.markdown);
|
|
15
24
|
const headings = extractHeadings(page.markdown);
|
|
16
25
|
pages.set(page.slug, {
|
|
17
26
|
title: page.title,
|
|
18
|
-
description
|
|
27
|
+
description,
|
|
19
28
|
slug: page.slug,
|
|
20
29
|
html,
|
|
21
30
|
headings,
|
|
@@ -130,7 +139,7 @@ function buildRichIndex(groupMap, pages) {
|
|
|
130
139
|
continue;
|
|
131
140
|
const typeCount = items.filter((i) => i.kind !== "group" && i.kind !== "namespace").length;
|
|
132
141
|
const href = `${groupEntry.slug}.html`;
|
|
133
|
-
const desc = page.description || "";
|
|
142
|
+
const desc = stripMarkdownLinks(page.description || "");
|
|
134
143
|
const meta = typeCount > 0
|
|
135
144
|
? `<p style="margin:0.5rem 0 0;font-size:0.8rem;opacity:0.5">${typeCount} type${typeCount !== 1 ? "s" : ""}</p>`
|
|
136
145
|
: "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmC,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMzF,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmC,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMzF,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAmcxD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAmBvB;AAUD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrD"}
|
|
@@ -16,6 +16,47 @@ function parseFrontmatter(raw) {
|
|
|
16
16
|
const meta = parseYaml(match[1]) ?? {};
|
|
17
17
|
return { meta, body: match[2] };
|
|
18
18
|
}
|
|
19
|
+
const FENCED_BLOCK_TOKEN = "@@SOURCEY_FENCED_BLOCK_";
|
|
20
|
+
function protectFencedCodeBlocks(input) {
|
|
21
|
+
const blocks = [];
|
|
22
|
+
const output = [];
|
|
23
|
+
const lines = input.split("\n");
|
|
24
|
+
let fence = null;
|
|
25
|
+
let buffer = [];
|
|
26
|
+
const pushProtectedBlock = () => {
|
|
27
|
+
const index = blocks.push(buffer.join("\n")) - 1;
|
|
28
|
+
output.push(`${FENCED_BLOCK_TOKEN}${index}@@`);
|
|
29
|
+
buffer = [];
|
|
30
|
+
};
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
const stripped = line.replace(/^ {1,3}/, "");
|
|
33
|
+
const fenceMatch = stripped.match(/^(`{3,}|~{3,})(.*)$/);
|
|
34
|
+
if (!fence) {
|
|
35
|
+
if (!fenceMatch) {
|
|
36
|
+
output.push(line);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
fence = { char: fenceMatch[1][0], length: fenceMatch[1].length };
|
|
40
|
+
buffer.push(line);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
buffer.push(line);
|
|
44
|
+
if (fenceMatch &&
|
|
45
|
+
fenceMatch[1][0] === fence.char &&
|
|
46
|
+
fenceMatch[1].length >= fence.length &&
|
|
47
|
+
!fenceMatch[2].trim()) {
|
|
48
|
+
pushProtectedBlock();
|
|
49
|
+
fence = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (buffer.length > 0) {
|
|
53
|
+
output.push(...buffer);
|
|
54
|
+
}
|
|
55
|
+
return { text: output.join("\n"), blocks };
|
|
56
|
+
}
|
|
57
|
+
function restoreFencedCodeBlocks(input, blocks) {
|
|
58
|
+
return blocks.reduce((text, block, index) => text.replaceAll(`${FENCED_BLOCK_TOKEN}${index}@@`, block), input);
|
|
59
|
+
}
|
|
19
60
|
// ---------------------------------------------------------------------------
|
|
20
61
|
// Component preprocessor: transforms MDX-style JSX components into HTML
|
|
21
62
|
// ---------------------------------------------------------------------------
|
|
@@ -24,7 +65,8 @@ function parseFrontmatter(raw) {
|
|
|
24
65
|
* a single rendering path in preprocessDirectives.
|
|
25
66
|
*/
|
|
26
67
|
function preprocessComponents(body) {
|
|
27
|
-
|
|
68
|
+
const { text, blocks } = protectFencedCodeBlocks(body);
|
|
69
|
+
let html = text;
|
|
28
70
|
// <Steps> <Step title="...">content</Step> ... </Steps> → :::steps numbered list
|
|
29
71
|
html = html.replace(/<Steps>\s*([\s\S]*?)\s*<\/Steps>/g, (_m, inner) => {
|
|
30
72
|
const steps = [];
|
|
@@ -56,7 +98,46 @@ function preprocessComponents(body) {
|
|
|
56
98
|
html = html.replace(/<Accordion\s+title="([^"]*)">\s*([\s\S]*?)\s*<\/Accordion>/g, (_m, title, content) => {
|
|
57
99
|
return `:::accordion{title="${title}"}\n${content.trim()}\n:::`;
|
|
58
100
|
});
|
|
59
|
-
|
|
101
|
+
// <Tabs> <Tab title="...">content</Tab> ... </Tabs> → :::tabs with ::tab children
|
|
102
|
+
html = html.replace(/<Tabs>\s*([\s\S]*?)\s*<\/Tabs>/g, (_m, inner) => {
|
|
103
|
+
const tabs = [];
|
|
104
|
+
inner.replace(/\s*<Tab\s+title="([^"]*)">\s*([\s\S]*?)\s*<\/Tab>/g, (_tm, title, content) => {
|
|
105
|
+
tabs.push(`::tab{title="${title}"}\n${content.trim()}\n::`);
|
|
106
|
+
return "";
|
|
107
|
+
});
|
|
108
|
+
return `:::tabs\n${tabs.join("\n")}\n:::`;
|
|
109
|
+
});
|
|
110
|
+
// <CodeGroup> with titled fenced code blocks → :::code-group
|
|
111
|
+
html = html.replace(/<CodeGroup>\s*([\s\S]*?)\s*<\/CodeGroup>/g, (_m, inner) => `:::code-group\n${inner.trim()}\n:::`);
|
|
112
|
+
// <Note>, <Warning>, <Tip>, <Info> → :::callout directives
|
|
113
|
+
for (const type of ["note", "warning", "tip", "info"]) {
|
|
114
|
+
const tag = type.charAt(0).toUpperCase() + type.slice(1);
|
|
115
|
+
html = html.replace(new RegExp(`<${tag}(?:\\s+title="([^"]*)")?\\s*>\\s*([\\s\\S]*?)\\s*<\\/${tag}>`, "g"), (_m, title, content) => {
|
|
116
|
+
const titleSuffix = title ? ` ${title}` : "";
|
|
117
|
+
return `:::${type}${titleSuffix}\n${content.trim()}\n:::`;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// <Video src="..." title="..." /> → ::video[url]{title="..."}
|
|
121
|
+
html = html.replace(/<Video\s+([^>]*?)\s*\/?\s*>/g, (_m, attrs) => {
|
|
122
|
+
const src = attrs.match(/src="([^"]*)"/)?.[1] ?? "";
|
|
123
|
+
const title = attrs.match(/title="([^"]*)"/)?.[1];
|
|
124
|
+
const titleAttr = title ? `{title="${title}"}` : "";
|
|
125
|
+
return `::video[${src}]${titleAttr}`;
|
|
126
|
+
});
|
|
127
|
+
// <Iframe src="..." title="..." height="..." /> → ::iframe[url]{attrs}
|
|
128
|
+
html = html.replace(/<Iframe\s+([^>]*?)\s*\/?\s*>/g, (_m, attrs) => {
|
|
129
|
+
const src = attrs.match(/src="([^"]*)"/)?.[1] ?? "";
|
|
130
|
+
const title = attrs.match(/title="([^"]*)"/)?.[1];
|
|
131
|
+
const height = attrs.match(/height="([^"]*)"/)?.[1];
|
|
132
|
+
const parts = [];
|
|
133
|
+
if (title)
|
|
134
|
+
parts.push(`title="${title}"`);
|
|
135
|
+
if (height)
|
|
136
|
+
parts.push(`height="${height}"`);
|
|
137
|
+
const attrStr = parts.length ? `{${parts.join(" ")}}` : "";
|
|
138
|
+
return `::iframe[${src}]${attrStr}`;
|
|
139
|
+
});
|
|
140
|
+
return restoreFencedCodeBlocks(html, blocks);
|
|
60
141
|
}
|
|
61
142
|
// ---------------------------------------------------------------------------
|
|
62
143
|
// Directive preprocessor: transforms :::directive blocks into HTML
|
|
@@ -128,7 +209,8 @@ function splitChildren(content, marker) {
|
|
|
128
209
|
* from other directive types cannot collide.
|
|
129
210
|
*/
|
|
130
211
|
function preprocessDirectives(body) {
|
|
131
|
-
|
|
212
|
+
const { text, blocks } = protectFencedCodeBlocks(body);
|
|
213
|
+
let html = text;
|
|
132
214
|
// Callouts: :::note, :::warning, :::tip, :::info (optional title)
|
|
133
215
|
html = html.replace(/^:::(note|warning|tip|info)(?:[^\S\n]+(.+))?\s*\n([\s\S]*?)^:::\s*$/gm, (_m, type, title, content) => {
|
|
134
216
|
const label = title?.trim() || type.charAt(0).toUpperCase() + type.slice(1);
|
|
@@ -142,14 +224,30 @@ ${body ? `<div class="callout-content">\n\n${body}\n\n</div>` : ""}
|
|
|
142
224
|
html = html.replace(/^:::steps\s*\n([\s\S]*?)^:::\s*$/gm, (_m, content) => {
|
|
143
225
|
const lines = content.trim().split("\n");
|
|
144
226
|
const steps = [];
|
|
227
|
+
let fenceMarker = "";
|
|
145
228
|
for (const line of lines) {
|
|
146
|
-
|
|
229
|
+
// Track fenced code blocks so we don't mis-detect numbered
|
|
230
|
+
// lines inside them as step titles. A closing fence must use
|
|
231
|
+
// the same character, at least as many repeats, and no info string.
|
|
232
|
+
const stripped = line.replace(/^ {1,3}/, "");
|
|
233
|
+
const fm = stripped.match(/^(`{3,}|~{3,})(.*)/);
|
|
234
|
+
if (fm) {
|
|
235
|
+
if (!fenceMarker) {
|
|
236
|
+
fenceMarker = fm[1];
|
|
237
|
+
}
|
|
238
|
+
else if (fm[1][0] === fenceMarker[0] &&
|
|
239
|
+
fm[1].length >= fenceMarker.length &&
|
|
240
|
+
!fm[2].trim()) {
|
|
241
|
+
fenceMarker = "";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const sm = !fenceMarker && line.match(/^\d+\.\s+(.+)/);
|
|
147
245
|
if (sm) {
|
|
148
246
|
steps.push({ title: sm[1], body: [] });
|
|
149
247
|
}
|
|
150
248
|
else if (steps.length > 0) {
|
|
151
249
|
// Strip up to 3 leading spaces (markdown list indent)
|
|
152
|
-
steps[steps.length - 1].body.push(
|
|
250
|
+
steps[steps.length - 1].body.push(stripped);
|
|
153
251
|
}
|
|
154
252
|
}
|
|
155
253
|
const items = steps
|
|
@@ -177,15 +275,16 @@ ${s.body.join("\n").trim()}
|
|
|
177
275
|
});
|
|
178
276
|
// Code Group: :::code-group with titled fenced code blocks
|
|
179
277
|
html = html.replace(/^:::code-group\s*\n([\s\S]*?)^:::\s*$/gm, (_m, content) => {
|
|
180
|
-
const
|
|
278
|
+
const restoredContent = restoreFencedCodeBlocks(content, blocks);
|
|
279
|
+
const codeBlocks = [];
|
|
181
280
|
const re = /```(\w+)\s+title="([^"]*)"\s*\n([\s\S]*?)```/g;
|
|
182
281
|
let match;
|
|
183
|
-
while ((match = re.exec(
|
|
184
|
-
|
|
282
|
+
while ((match = re.exec(restoredContent)) !== null) {
|
|
283
|
+
codeBlocks.push({ title: match[2], body: `\`\`\`${match[1]}\n${match[3]}\`\`\`` });
|
|
185
284
|
}
|
|
186
|
-
if (
|
|
187
|
-
return
|
|
188
|
-
return `\n\n${buildTabbedHtml(
|
|
285
|
+
if (codeBlocks.length === 0)
|
|
286
|
+
return restoredContent;
|
|
287
|
+
return `\n\n${buildTabbedHtml(codeBlocks, nextId("cg"), "directive-code-group")}\n\n`;
|
|
189
288
|
});
|
|
190
289
|
// Card Group: :::card-group{cols="N"} with ::card children
|
|
191
290
|
html = html.replace(/^:::card-group(?:\{([^}]*)\})?\s*\n([\s\S]*?)^:::\s*$/gm, (_m, attrsRaw, content) => {
|
|
@@ -227,7 +326,7 @@ ${content.trim()}
|
|
|
227
326
|
return match;
|
|
228
327
|
return `<div class="accordion-group not-prose">\n${match.trim()}\n</div>`;
|
|
229
328
|
});
|
|
230
|
-
return html;
|
|
329
|
+
return restoreFencedCodeBlocks(html, blocks);
|
|
231
330
|
}
|
|
232
331
|
// ---------------------------------------------------------------------------
|
|
233
332
|
// Public API
|
package/dist/dev-server.js
CHANGED
|
@@ -439,7 +439,7 @@ async function buildSiteConfig(config) {
|
|
|
439
439
|
name: config.name,
|
|
440
440
|
theme: config.theme,
|
|
441
441
|
logo: logo?.light ? logo : undefined,
|
|
442
|
-
favicon: config.favicon,
|
|
442
|
+
favicon: config.favicon ? await resolveAssetUrl(config.favicon) : undefined,
|
|
443
443
|
repo: config.repo,
|
|
444
444
|
editBranch: config.editBranch,
|
|
445
445
|
editBasePath: config.editBasePath,
|
|
@@ -459,6 +459,7 @@ async function loadCustomCSS(paths) {
|
|
|
459
459
|
const MIME_TYPES = {
|
|
460
460
|
".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
|
|
461
461
|
".gif": "image/gif", ".svg": "image/svg+xml", ".webp": "image/webp",
|
|
462
|
+
".ico": "image/x-icon",
|
|
462
463
|
};
|
|
463
464
|
async function resolveAssetUrl(pathOrUrl) {
|
|
464
465
|
if (!pathOrUrl || pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://") || pathOrUrl.startsWith("data:")) {
|
package/dist/index.js
CHANGED
|
@@ -158,7 +158,7 @@ async function buildSiteConfig(config) {
|
|
|
158
158
|
name: config.name,
|
|
159
159
|
theme: config.theme,
|
|
160
160
|
logo: logo?.light ? logo : undefined,
|
|
161
|
-
favicon: config.favicon,
|
|
161
|
+
favicon: config.favicon ? await resolveAssetUrl(config.favicon) : undefined,
|
|
162
162
|
repo: config.repo,
|
|
163
163
|
editBranch: config.editBranch,
|
|
164
164
|
editBasePath: config.editBasePath,
|
package/dist/init.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
--color-primary: 99 102 241;
|
|
24
24
|
--color-primary-light: 129 140 248;
|
|
25
25
|
--color-primary-dark: 79 70 229;
|
|
26
|
+
--color-primary-ink: 79 70 229;
|
|
26
27
|
|
|
27
28
|
/* Backgrounds */
|
|
28
29
|
--color-background-light: 255 255 255;
|
|
@@ -70,7 +71,6 @@
|
|
|
70
71
|
--method-prompt: #2563eb;
|
|
71
72
|
|
|
72
73
|
/* Typography */
|
|
73
|
-
--font-sans:
|
|
74
|
-
--font-mono:
|
|
74
|
+
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
75
|
+
--font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
75
76
|
}
|
|
76
|
-
|
|
@@ -9,15 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
/* ── Page Description (display font) ─────────────────────────────── */
|
|
11
11
|
|
|
12
|
-
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap');
|
|
13
|
-
|
|
14
12
|
#sourcey .page-description {
|
|
15
|
-
font-family:
|
|
16
|
-
font-size: 1.
|
|
17
|
-
line-height: 1.
|
|
13
|
+
font-family: ui-serif, Georgia, Cambria, "Times New Roman", serif;
|
|
14
|
+
font-size: 1.125rem;
|
|
15
|
+
line-height: 1.35;
|
|
18
16
|
font-style: italic;
|
|
19
17
|
color: rgb(var(--color-gray-600));
|
|
20
18
|
}
|
|
19
|
+
@media (min-width: 1024px) {
|
|
20
|
+
#sourcey .page-description {
|
|
21
|
+
font-size: 1.375rem;
|
|
22
|
+
line-height: 1.4;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
21
25
|
.dark #sourcey .page-description {
|
|
22
26
|
color: rgb(var(--color-gray-400));
|
|
23
27
|
}
|
|
@@ -86,6 +90,12 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
86
90
|
overflow-x: auto;
|
|
87
91
|
-webkit-overflow-scrolling: touch;
|
|
88
92
|
}
|
|
93
|
+
#sourcey .table-wrap table {
|
|
94
|
+
margin: 0;
|
|
95
|
+
}
|
|
96
|
+
#sourcey .prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
|
97
|
+
padding-top: .571429em;
|
|
98
|
+
}
|
|
89
99
|
|
|
90
100
|
/* ── Prose Video (::video directive) ──────────────────────────────── */
|
|
91
101
|
|
|
@@ -245,7 +255,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
245
255
|
background: rgb(var(--color-gray-800) / 0.4);
|
|
246
256
|
}
|
|
247
257
|
#sourcey .nav-link.active {
|
|
248
|
-
color: rgb(var(--color-primary));
|
|
258
|
+
color: rgb(var(--color-primary-ink));
|
|
249
259
|
background: rgb(var(--color-primary) / 0.08);
|
|
250
260
|
font-weight: 500;
|
|
251
261
|
}
|
|
@@ -261,12 +271,15 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
261
271
|
padding-left: 0.75rem;
|
|
262
272
|
transition: color 0.15s, border-color 0.15s;
|
|
263
273
|
}
|
|
274
|
+
#sourcey #toc ul ul .toc-item {
|
|
275
|
+
margin-left: 0.75rem;
|
|
276
|
+
}
|
|
264
277
|
.dark #sourcey #toc .toc-item {
|
|
265
278
|
border-left-color: rgb(var(--color-gray-700));
|
|
266
279
|
}
|
|
267
280
|
#sourcey #toc .toc-item.active {
|
|
268
|
-
color: rgb(var(--color-primary));
|
|
269
|
-
border-left-color: rgb(var(--color-primary));
|
|
281
|
+
color: rgb(var(--color-primary-ink));
|
|
282
|
+
border-left-color: rgb(var(--color-primary-ink));
|
|
270
283
|
}
|
|
271
284
|
.dark #sourcey #toc .toc-item.active {
|
|
272
285
|
color: rgb(var(--color-primary-light));
|
|
@@ -310,7 +323,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
310
323
|
}
|
|
311
324
|
|
|
312
325
|
#sourcey .response-tab.active {
|
|
313
|
-
color: rgb(var(--color-primary));
|
|
326
|
+
color: rgb(var(--color-primary-ink));
|
|
314
327
|
}
|
|
315
328
|
.dark #sourcey .response-tab.active {
|
|
316
329
|
color: rgb(var(--color-primary-light));
|
|
@@ -511,7 +524,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
511
524
|
font-family: var(--font-mono);
|
|
512
525
|
font-size: 0.875rem;
|
|
513
526
|
font-weight: 600;
|
|
514
|
-
color: rgb(var(--color-primary));
|
|
527
|
+
color: rgb(var(--color-primary-ink));
|
|
515
528
|
background: transparent;
|
|
516
529
|
border: none;
|
|
517
530
|
padding: 0;
|
|
@@ -793,16 +806,17 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
793
806
|
|
|
794
807
|
#sourcey .callout {
|
|
795
808
|
margin: 1.25rem 0;
|
|
796
|
-
border
|
|
797
|
-
border: 1px solid
|
|
798
|
-
border-
|
|
809
|
+
border: none;
|
|
810
|
+
border-left: 1px solid;
|
|
811
|
+
border-radius: 0;
|
|
799
812
|
padding: 0.875rem 1rem;
|
|
800
813
|
font-size: 0.875rem;
|
|
801
814
|
line-height: 1.6;
|
|
802
815
|
color: rgb(var(--color-gray-700));
|
|
816
|
+
border-left-color: rgb(var(--color-gray-300));
|
|
803
817
|
}
|
|
804
818
|
.dark #sourcey .callout {
|
|
805
|
-
border-color: rgb(var(--color-gray-
|
|
819
|
+
border-left-color: rgb(var(--color-gray-700));
|
|
806
820
|
color: rgb(var(--color-gray-400));
|
|
807
821
|
}
|
|
808
822
|
|
|
@@ -831,50 +845,26 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
831
845
|
}
|
|
832
846
|
|
|
833
847
|
/* Note — blue */
|
|
834
|
-
#sourcey .callout-note {
|
|
835
|
-
|
|
836
|
-
background: rgb(59 130 246 / 0.04);
|
|
837
|
-
}
|
|
838
|
-
.dark #sourcey .callout-note {
|
|
839
|
-
border-left-color: #60a5fa;
|
|
840
|
-
background: rgb(59 130 246 / 0.06);
|
|
841
|
-
}
|
|
848
|
+
#sourcey .callout-note { border-left-color: #3b82f6; }
|
|
849
|
+
.dark #sourcey .callout-note { border-left-color: #60a5fa; }
|
|
842
850
|
#sourcey .callout-note .callout-title { color: #3b82f6; }
|
|
843
851
|
.dark #sourcey .callout-note .callout-title { color: #60a5fa; }
|
|
844
852
|
|
|
845
853
|
/* Warning — amber */
|
|
846
|
-
#sourcey .callout-warning {
|
|
847
|
-
|
|
848
|
-
background: rgb(245 158 11 / 0.04);
|
|
849
|
-
}
|
|
850
|
-
.dark #sourcey .callout-warning {
|
|
851
|
-
border-left-color: #fbbf24;
|
|
852
|
-
background: rgb(245 158 11 / 0.06);
|
|
853
|
-
}
|
|
854
|
+
#sourcey .callout-warning { border-left-color: #f59e0b; }
|
|
855
|
+
.dark #sourcey .callout-warning { border-left-color: #fbbf24; }
|
|
854
856
|
#sourcey .callout-warning .callout-title { color: #f59e0b; }
|
|
855
857
|
.dark #sourcey .callout-warning .callout-title { color: #fbbf24; }
|
|
856
858
|
|
|
857
859
|
/* Tip — green */
|
|
858
|
-
#sourcey .callout-tip {
|
|
859
|
-
|
|
860
|
-
background: rgb(34 197 94 / 0.04);
|
|
861
|
-
}
|
|
862
|
-
.dark #sourcey .callout-tip {
|
|
863
|
-
border-left-color: #4ade80;
|
|
864
|
-
background: rgb(34 197 94 / 0.06);
|
|
865
|
-
}
|
|
860
|
+
#sourcey .callout-tip { border-left-color: #22c55e; }
|
|
861
|
+
.dark #sourcey .callout-tip { border-left-color: #4ade80; }
|
|
866
862
|
#sourcey .callout-tip .callout-title { color: #22c55e; }
|
|
867
863
|
.dark #sourcey .callout-tip .callout-title { color: #4ade80; }
|
|
868
864
|
|
|
869
865
|
/* Info — violet */
|
|
870
|
-
#sourcey .callout-info {
|
|
871
|
-
|
|
872
|
-
background: rgb(139 92 246 / 0.04);
|
|
873
|
-
}
|
|
874
|
-
.dark #sourcey .callout-info {
|
|
875
|
-
border-left-color: #a78bfa;
|
|
876
|
-
background: rgb(139 92 246 / 0.06);
|
|
877
|
-
}
|
|
866
|
+
#sourcey .callout-info { border-left-color: #8b5cf6; }
|
|
867
|
+
.dark #sourcey .callout-info { border-left-color: #a78bfa; }
|
|
878
868
|
#sourcey .callout-info .callout-title { color: #8b5cf6; }
|
|
879
869
|
.dark #sourcey .callout-info .callout-title { color: #a78bfa; }
|
|
880
870
|
|
|
@@ -921,7 +911,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
921
911
|
}
|
|
922
912
|
|
|
923
913
|
#sourcey .directive-tab.active {
|
|
924
|
-
color: rgb(var(--color-primary));
|
|
914
|
+
color: rgb(var(--color-primary-ink));
|
|
925
915
|
}
|
|
926
916
|
.dark #sourcey .directive-tab.active {
|
|
927
917
|
color: rgb(var(--color-primary-light));
|
|
@@ -1097,7 +1087,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
|
|
1097
1087
|
background: rgb(var(--color-gray-800));
|
|
1098
1088
|
}
|
|
1099
1089
|
.drawer-dropdown-item.active {
|
|
1100
|
-
color: rgb(var(--color-primary));
|
|
1090
|
+
color: rgb(var(--color-primary-ink));
|
|
1101
1091
|
font-weight: 500;
|
|
1102
1092
|
}
|
|
1103
1093
|
.dark .drawer-dropdown-item.active {
|
package/dist/utils/markdown.d.ts
CHANGED
|
@@ -14,6 +14,16 @@ export interface PageHeading {
|
|
|
14
14
|
* No rendering needed; pure token walk.
|
|
15
15
|
*/
|
|
16
16
|
export declare function extractHeadings(input: string): PageHeading[];
|
|
17
|
+
/**
|
|
18
|
+
* Extract the first paragraph token from markdown.
|
|
19
|
+
* Useful for generated docs where a summary paragraph is embedded in the body.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractFirstParagraph(input?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Replace markdown links with their visible labels.
|
|
24
|
+
* Useful when rendering inside an existing clickable container.
|
|
25
|
+
*/
|
|
26
|
+
export declare function stripMarkdownLinks(input?: string): string;
|
|
17
27
|
/**
|
|
18
28
|
* Render Markdown to HTML.
|
|
19
29
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CASnE;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAgJD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAe5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D"}
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CASnE;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAgJD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAe5D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAY5D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D"}
|
package/dist/utils/markdown.js
CHANGED
|
@@ -157,6 +157,35 @@ export function extractHeadings(input) {
|
|
|
157
157
|
}
|
|
158
158
|
return headings;
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Extract the first paragraph token from markdown.
|
|
162
|
+
* Useful for generated docs where a summary paragraph is embedded in the body.
|
|
163
|
+
*/
|
|
164
|
+
export function extractFirstParagraph(input) {
|
|
165
|
+
if (!input)
|
|
166
|
+
return "";
|
|
167
|
+
const tokens = marked.lexer(input);
|
|
168
|
+
for (const token of tokens) {
|
|
169
|
+
if (token.type === "paragraph") {
|
|
170
|
+
const raw = token.raw.trim();
|
|
171
|
+
if (!raw)
|
|
172
|
+
continue;
|
|
173
|
+
if (!raw.replace(/<[^>]+>/g, "").trim())
|
|
174
|
+
continue;
|
|
175
|
+
return raw;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return "";
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Replace markdown links with their visible labels.
|
|
182
|
+
* Useful when rendering inside an existing clickable container.
|
|
183
|
+
*/
|
|
184
|
+
export function stripMarkdownLinks(input) {
|
|
185
|
+
if (!input)
|
|
186
|
+
return "";
|
|
187
|
+
return input.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1");
|
|
188
|
+
}
|
|
160
189
|
/**
|
|
161
190
|
* Render Markdown to HTML.
|
|
162
191
|
*/
|