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.
@@ -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-2", 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 }), flow?.authorizationUrl && (_jsxs("p", { class: "mt-1", children: ["Authorization: ", _jsx("code", { class: "text-xs", children: flow.authorizationUrl })] })), flow?.tokenUrl && (_jsxs("p", { class: "mt-1", children: ["Token: ", _jsx("code", { class: "text-xs", children: flow.tokenUrl })] })), flow?.scopes && Object.keys(flow.scopes).length > 0 && (_jsxs("p", { class: "mt-1", children: ["Scopes:", " ", Object.entries(flow.scopes)
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("h4", { 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 }))] }));
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: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
17
- mono: "'Geist Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace",
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,CA4FxB"}
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: page.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;AA4TxD;;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"}
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
- let html = body;
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
- return html;
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
- let html = body;
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
- const sm = line.match(/^\d+\.\s+(.+)/);
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(line.replace(/^ {1,3}/, ""));
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 blocks = [];
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(content)) !== null) {
184
- blocks.push({ title: match[2], body: `\`\`\`${match[1]}\n${match[3]}\`\`\`` });
282
+ while ((match = re.exec(restoredContent)) !== null) {
283
+ codeBlocks.push({ title: match[2], body: `\`\`\`${match[1]}\n${match[3]}\`\`\`` });
185
284
  }
186
- if (blocks.length === 0)
187
- return content;
188
- return `\n\n${buildTabbedHtml(blocks, nextId("cg"), "directive-code-group")}\n\n`;
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
@@ -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
@@ -243,7 +243,7 @@ import { myFunction } from "my-package";
243
243
  build: "sourcey build",
244
244
  },
245
245
  dependencies: {
246
- sourcey: "^3.3.5",
246
+ sourcey: "^3.4.3",
247
247
  },
248
248
  }, null, 2);
249
249
  writeFileSync(resolve(targetDir, "package.json"), pkg + "\n");
@@ -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: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
74
- --font-mono: 'Geist Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
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: 'Playfair Display', Georgia, serif;
16
- font-size: 1.375rem;
17
- line-height: 1.4;
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-radius: var(--radius);
797
- border: 1px solid rgb(var(--color-gray-200) / 0.7);
798
- border-left-width: 3px;
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-800) / 0.5);
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
- border-left-color: #3b82f6;
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
- border-left-color: #f59e0b;
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
- border-left-color: #22c55e;
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
- border-left-color: #8b5cf6;
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 {
@@ -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"}
@@ -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
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sourcey",
3
- "version": "3.4.1",
3
+ "version": "3.4.4",
4
4
  "description": "Open source documentation platform. API references, guides, static output.",
5
5
  "type": "module",
6
6
  "engines": {