@ox-content/vite-plugin 1.1.0 → 2.1.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.
package/dist/index.mjs CHANGED
@@ -6700,51 +6700,105 @@ const highlighterCache = /* @__PURE__ */ new Map();
6700
6700
  * Get or create the Shiki highlighter.
6701
6701
  */
6702
6702
  async function getHighlighter(theme, customLangs = []) {
6703
+ const { themeInput } = normalizeThemeInput(theme);
6703
6704
  const cacheKey = JSON.stringify({
6704
- theme,
6705
+ theme: themeInput,
6705
6706
  langs: customLangs
6706
6707
  });
6707
6708
  let highlighterPromise = highlighterCache.get(cacheKey);
6708
6709
  if (!highlighterPromise) {
6709
6710
  highlighterPromise = createHighlighter({
6710
- themes: [theme],
6711
+ themes: [themeInput],
6711
6712
  langs: [...BUILTIN_LANGS, ...customLangs]
6712
6713
  });
6713
6714
  highlighterCache.set(cacheKey, highlighterPromise);
6714
6715
  }
6715
6716
  return highlighterPromise;
6716
6717
  }
6718
+ function normalizeThemeInput(theme) {
6719
+ if (typeof theme === "string") return {
6720
+ themeInput: theme,
6721
+ themeName: theme
6722
+ };
6723
+ const themeName = theme.name || "ox-content-custom-theme";
6724
+ return {
6725
+ themeInput: theme.name ? theme : {
6726
+ ...theme,
6727
+ name: themeName
6728
+ },
6729
+ themeName
6730
+ };
6731
+ }
6717
6732
  /**
6718
6733
  * Rehype plugin for syntax highlighting with Shiki.
6719
6734
  */
6720
6735
  function rehypeShikiHighlight(options) {
6721
6736
  const { theme, langs } = options;
6722
6737
  return async (tree) => {
6738
+ const { themeName } = normalizeThemeInput(theme);
6723
6739
  const highlighter = await getHighlighter(theme, langs);
6740
+ const highlightBlockCode = (codeElement) => {
6741
+ let lang = "text";
6742
+ const langClass = normalizeClassName(codeElement.properties?.className).find((value) => value.startsWith("language-"));
6743
+ if (langClass) lang = langClass.replace("language-", "");
6744
+ const codeText = getTextContent(codeElement);
6745
+ try {
6746
+ const highlighted = highlighter.codeToHtml(codeText, {
6747
+ lang,
6748
+ theme: themeName
6749
+ });
6750
+ const parsed = unified().use(rehypeParse, { fragment: true }).parse(highlighted);
6751
+ if (parsed.children[0]?.type === "element") {
6752
+ const highlightedPre = parsed.children[0];
6753
+ highlightedPre.properties ??= {};
6754
+ highlightedPre.properties["data-language"] = lang;
6755
+ return highlightedPre;
6756
+ }
6757
+ } catch {}
6758
+ return null;
6759
+ };
6760
+ const highlightInlineCode = (codeElement) => {
6761
+ let lang = "text";
6762
+ const originalCodeClasses = normalizeClassName(codeElement.properties?.className);
6763
+ const langClass = originalCodeClasses.find((value) => value.startsWith("language-"));
6764
+ if (!langClass) return null;
6765
+ lang = langClass.replace("language-", "");
6766
+ const codeText = getTextContent(codeElement);
6767
+ try {
6768
+ const highlighted = highlighter.codeToHtml(codeText, {
6769
+ lang,
6770
+ theme: themeName
6771
+ });
6772
+ const parsed = unified().use(rehypeParse, { fragment: true }).parse(highlighted);
6773
+ if (parsed.children[0]?.type === "element") {
6774
+ const highlightedCode = parsed.children[0].children.find((child) => child.type === "element" && child.tagName === "code");
6775
+ if (highlightedCode) {
6776
+ highlightedCode.properties ??= {};
6777
+ const highlightedClasses = normalizeClassName(highlightedCode.properties.className);
6778
+ highlightedCode.properties.className = [...new Set([
6779
+ ...originalCodeClasses,
6780
+ ...highlightedClasses,
6781
+ "shiki-inline"
6782
+ ])];
6783
+ highlightedCode.properties["data-language"] = lang;
6784
+ return highlightedCode;
6785
+ }
6786
+ }
6787
+ } catch {}
6788
+ return null;
6789
+ };
6724
6790
  const visit = async (node) => {
6725
6791
  if ("children" in node) for (let i = 0; i < node.children.length; i++) {
6726
6792
  const child = node.children[i];
6727
6793
  if (child.type === "element" && child.tagName === "pre") {
6728
6794
  const codeElement = child.children.find((c) => c.type === "element" && c.tagName === "code");
6729
6795
  if (codeElement) {
6730
- let lang = "text";
6731
- const langClass = normalizeClassName(codeElement.properties?.className).find((value) => value.startsWith("language-"));
6732
- if (langClass) lang = langClass.replace("language-", "");
6733
- const codeText = getTextContent(codeElement);
6734
- try {
6735
- const highlighted = highlighter.codeToHtml(codeText, {
6736
- lang,
6737
- theme
6738
- });
6739
- const parsed = unified().use(rehypeParse, { fragment: true }).parse(highlighted);
6740
- if (parsed.children[0]?.type === "element") {
6741
- const highlightedPre = parsed.children[0];
6742
- highlightedPre.properties ??= {};
6743
- highlightedPre.properties["data-language"] = lang;
6744
- node.children[i] = highlightedPre;
6745
- }
6746
- } catch {}
6796
+ const highlightedPre = highlightBlockCode(codeElement);
6797
+ if (highlightedPre) node.children[i] = highlightedPre;
6747
6798
  }
6799
+ } else if (child.type === "element" && child.tagName === "code") {
6800
+ const highlightedCode = highlightInlineCode(child);
6801
+ if (highlightedCode) node.children[i] = highlightedCode;
6748
6802
  } else if (child.type === "element") await visit(child);
6749
6803
  }
6750
6804
  };
@@ -7361,23 +7415,137 @@ function cleanSummaryText(text, maxLength = 120) {
7361
7415
  function renderInlineHtml(text) {
7362
7416
  let html = "";
7363
7417
  let lastIndex = 0;
7364
- const tokenPattern = /`([^`]+)`|\[([^\]]+)\]\(([^)]+)\)/g;
7418
+ const tokenPattern = /`([^`]+)`|\[([^\]]+)\]\(([^)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*]+)\*|_([^_]+)_/g;
7365
7419
  let match;
7366
7420
  while ((match = tokenPattern.exec(text)) !== null) {
7367
7421
  html += escapeHtml$3(text.slice(lastIndex, match.index));
7368
7422
  if (match[1]) html += `<code>${escapeHtml$3(match[1])}</code>`;
7369
- else if (match[2] && match[3]) html += `<a href="${escapeHtml$3(match[3])}">${escapeHtml$3(match[2])}</a>`;
7423
+ else if (match[2] && match[3]) html += `<a href="${escapeHtml$3(match[3])}">${renderInlineHtml(match[2])}</a>`;
7424
+ else if (match[4] || match[5]) {
7425
+ const strongText = match[4] ?? match[5] ?? "";
7426
+ html += `<strong>${renderInlineHtml(strongText)}</strong>`;
7427
+ } else if (match[6] || match[7]) {
7428
+ const emphasisText = match[6] ?? match[7] ?? "";
7429
+ html += `<em>${renderInlineHtml(emphasisText)}</em>`;
7430
+ }
7370
7431
  lastIndex = match.index + match[0].length;
7371
7432
  }
7372
7433
  html += escapeHtml$3(text.slice(lastIndex));
7373
7434
  return html.replace(/\n/g, "<br>");
7374
7435
  }
7375
- function renderParagraphsHtml(text) {
7376
- return text.split(/\n\s*\n/).map((paragraph) => paragraph.trim()).filter(Boolean).map((paragraph) => `<p>${renderInlineHtml(paragraph)}</p>`).join("\n");
7436
+ function isFenceStart(line) {
7437
+ return /^```([\w-]+)?\s*$/.exec(line.trim());
7438
+ }
7439
+ function isHeading(line) {
7440
+ return /^(#{1,6})\s+(.*)$/.exec(line.trim());
7441
+ }
7442
+ function isOrderedListItem(line) {
7443
+ return /^\d+\.\s+(.*)$/.exec(line.trim());
7444
+ }
7445
+ function isUnorderedListItem(line) {
7446
+ return /^[-*+]\s+(.*)$/.exec(line.trim());
7447
+ }
7448
+ function isMarkdownBlockStart(line) {
7449
+ return Boolean(isFenceStart(line) || isHeading(line) || isOrderedListItem(line) || isUnorderedListItem(line));
7450
+ }
7451
+ function renderMarkdownBlocksHtml(text) {
7452
+ const lines = text.split(/\r?\n/);
7453
+ const blocks = [];
7454
+ let index = 0;
7455
+ while (index < lines.length) {
7456
+ const line = lines[index];
7457
+ const trimmed = line.trim();
7458
+ if (!trimmed) {
7459
+ index++;
7460
+ continue;
7461
+ }
7462
+ const fenceMatch = isFenceStart(line);
7463
+ if (fenceMatch) {
7464
+ const language = fenceMatch[1] || "text";
7465
+ const codeLines = [];
7466
+ index++;
7467
+ while (index < lines.length && !lines[index].trim().startsWith("```")) {
7468
+ codeLines.push(lines[index]);
7469
+ index++;
7470
+ }
7471
+ if (index < lines.length) index++;
7472
+ blocks.push(renderCodeBlockHtml(codeLines.join("\n"), language));
7473
+ continue;
7474
+ }
7475
+ const headingMatch = isHeading(line);
7476
+ if (headingMatch) {
7477
+ const level = Math.min(headingMatch[1].length, 6);
7478
+ blocks.push(`<h${level}>${renderInlineHtml(headingMatch[2].trim())}</h${level}>`);
7479
+ index++;
7480
+ continue;
7481
+ }
7482
+ if (isOrderedListItem(line)) {
7483
+ const items = [];
7484
+ while (index < lines.length) {
7485
+ const currentLine = lines[index];
7486
+ const currentMatch = isOrderedListItem(currentLine);
7487
+ if (!currentMatch) break;
7488
+ const itemLines = [currentMatch[1].trim()];
7489
+ index++;
7490
+ while (index < lines.length) {
7491
+ const continuation = lines[index];
7492
+ const continuationTrimmed = continuation.trim();
7493
+ if (!continuationTrimmed || isMarkdownBlockStart(continuation) || /^ {0,1}\d+\.\s+/.test(continuationTrimmed)) break;
7494
+ itemLines.push(continuationTrimmed);
7495
+ index++;
7496
+ }
7497
+ items.push(`<li>${renderInlineHtml(itemLines.join(" "))}</li>`);
7498
+ if (index < lines.length && !lines[index].trim()) break;
7499
+ }
7500
+ blocks.push(`<ol>\n${items.join("\n")}\n</ol>`);
7501
+ continue;
7502
+ }
7503
+ if (isUnorderedListItem(line)) {
7504
+ const items = [];
7505
+ while (index < lines.length) {
7506
+ const currentLine = lines[index];
7507
+ const currentMatch = isUnorderedListItem(currentLine);
7508
+ if (!currentMatch) break;
7509
+ const itemLines = [currentMatch[1].trim()];
7510
+ index++;
7511
+ while (index < lines.length) {
7512
+ const continuation = lines[index];
7513
+ const continuationTrimmed = continuation.trim();
7514
+ if (!continuationTrimmed || isMarkdownBlockStart(continuation) || /^[-*+]\s+/.test(continuationTrimmed)) break;
7515
+ itemLines.push(continuationTrimmed);
7516
+ index++;
7517
+ }
7518
+ items.push(`<li>${renderInlineHtml(itemLines.join(" "))}</li>`);
7519
+ if (index < lines.length && !lines[index].trim()) break;
7520
+ }
7521
+ blocks.push(`<ul>\n${items.join("\n")}\n</ul>`);
7522
+ continue;
7523
+ }
7524
+ const paragraphLines = [trimmed];
7525
+ index++;
7526
+ while (index < lines.length) {
7527
+ const nextLine = lines[index];
7528
+ const nextTrimmed = nextLine.trim();
7529
+ if (!nextTrimmed || isMarkdownBlockStart(nextLine)) break;
7530
+ paragraphLines.push(nextTrimmed);
7531
+ index++;
7532
+ }
7533
+ blocks.push(`<p>${renderInlineHtml(paragraphLines.join(" "))}</p>`);
7534
+ }
7535
+ return `<div class="ox-api-entry__prose">\n${blocks.join("\n")}\n</div>`;
7377
7536
  }
7378
7537
  function renderCodeBlockHtml(code, language = "typescript") {
7379
7538
  return `<pre><code class="language-${language}">${escapeHtml$3(code)}</code></pre>`;
7380
7539
  }
7540
+ function renderHighlightedInlineCodeHtml(code, className, language = "typescript") {
7541
+ return `<code class="${escapeHtml$3(className)} language-${language}">${escapeHtml$3(code)}</code>`;
7542
+ }
7543
+ function renderDetailsControlsHtml(targetSelector) {
7544
+ return `<div class="ox-api-controls" data-ox-api-target="${targetSelector}" role="toolbar" aria-label="Reference display controls">
7545
+ <button type="button" class="ox-api-controls__button" data-ox-api-toggle="expand">Open all</button>
7546
+ <button type="button" class="ox-api-controls__button" data-ox-api-toggle="collapse">Close all</button>
7547
+ </div>`;
7548
+ }
7381
7549
  function buildDocsData(docs) {
7382
7550
  return {
7383
7551
  version: 1,
@@ -7386,16 +7554,6 @@ function buildDocsData(docs) {
7386
7554
  };
7387
7555
  }
7388
7556
  /**
7389
- * Regex pattern for matching JSDoc comment blocks.
7390
- *
7391
- * Matches block comments that start at the beginning of a line
7392
- * (with optional leading whitespace). This pattern avoids false matches
7393
- * with block comments inside strings like glob patterns.
7394
- *
7395
- * @internal
7396
- */
7397
- const JSDOC_BLOCK = /^[ \t]*\/\*\*\s*([\s\S]*?)\s*\*\//gm;
7398
- /**
7399
7557
  * Extracts JSDoc documentation from source files in specified directories.
7400
7558
  *
7401
7559
  * This function recursively searches directories for source files matching
@@ -7461,11 +7619,13 @@ const JSDOC_BLOCK = /^[ \t]*\/\*\*\s*([\s\S]*?)\s*\*\//gm;
7461
7619
  * ```
7462
7620
  */
7463
7621
  async function extractDocs(srcDirs, options) {
7622
+ const extractFileDocs = (await importNapiModule()).extractFileDocs;
7623
+ if (!extractFileDocs) throw new Error("[ox-content] extractFileDocs is not available from @ox-content/napi.");
7464
7624
  const results = [];
7465
7625
  for (const srcDir of srcDirs) {
7466
7626
  const files = await findFiles(srcDir, options);
7467
7627
  for (const file of files) {
7468
- const entries = extractFromContent(await fs$1.promises.readFile(file, "utf-8"), file, options);
7628
+ const entries = extractFileDocs(file, options.private).map(parseNapiDocItem).filter((entry) => Boolean(entry));
7469
7629
  if (entries.length > 0) results.push({
7470
7630
  file,
7471
7631
  entries
@@ -7516,127 +7676,21 @@ function isExcluded(file, patterns) {
7516
7676
  return false;
7517
7677
  });
7518
7678
  }
7519
- /**
7520
- * Extracts documentation entries from file content.
7521
- */
7522
- function extractFromContent(content, file, options) {
7523
- const entries = [];
7524
- let match;
7525
- JSDOC_BLOCK.lastIndex = 0;
7526
- while ((match = JSDOC_BLOCK.exec(content)) !== null) {
7527
- const jsdocContent = match[1];
7528
- const jsdocEnd = match.index + match[0].length;
7529
- const afterJsdoc = content.slice(jsdocEnd).trim();
7530
- const lineNumber = content.slice(0, match.index).split("\n").length;
7531
- const entry = parseJsdocBlock(jsdocContent, afterJsdoc, file, lineNumber);
7532
- if (entry && (options.private || !entry.private)) entries.push(entry);
7533
- }
7534
- return entries;
7535
- }
7536
- /**
7537
- * Extracts the complete function signature for display.
7538
- *
7539
- * Captures the full function declaration from `export/async/function name(...): ReturnType`
7540
- * or `export const name = (...): ReturnType => {}`, handling multi-line signatures.
7541
- *
7542
- * @param signature - Multi-line function declaration text
7543
- * @returns Cleaned function signature or undefined if not found
7544
- *
7545
- * @internal
7546
- */
7547
- function extractFunctionSignature(signature) {
7548
- const match = signature.match(/(?:export\s+)?(?:async\s+)?(?:function\s+\w+|\w+\s*=\s*(?:async\s*)?\()\([^{]*?\)(?:\s*:\s*[^{;]+)?/s);
7549
- if (match) {
7550
- let sig = match[0].trim();
7551
- sig = sig.split("\n").map((line) => line.trim()).filter((line) => line).join("\n ");
7552
- return sig;
7553
- }
7554
- }
7555
- /**
7556
- * Extracts parameter and return types from a TypeScript function signature.
7557
- *
7558
- * Parses function signatures to extract:
7559
- * - Parameter names and their type annotations
7560
- * - Return type annotation
7561
- *
7562
- * Handles various function declaration styles:
7563
- * - `function name(param: type): ReturnType`
7564
- * - `const name = (param: type): ReturnType => {}`
7565
- * - `export async function name(param: type): Promise<ReturnType>`
7566
- *
7567
- * @param signature - Multi-line function signature text
7568
- * @param params - Array of parameter docs with names already extracted
7569
- * @returns Object with extracted parameter types and return type
7570
- *
7571
- * @internal
7572
- */
7573
- function extractTypesFromSignature(signature, _params) {
7574
- const paramTypes = [];
7575
- const paramListMatch = signature.match(/\(([^)]*)\)(?:\s*:\s*([^{=>]+))?/s);
7576
- if (paramListMatch && paramListMatch[1]) {
7577
- const paramListStr = paramListMatch[1];
7578
- const paramParts = splitParameters(paramListStr);
7579
- for (const part of paramParts) {
7580
- const trimmed = part.trim();
7581
- if (!trimmed) continue;
7582
- const typeMatch = /:\s*(.+?)(?:\s*=|$)/.exec(trimmed);
7583
- if (typeMatch) {
7584
- let typeStr = typeMatch[1].trim();
7585
- if (typeStr.includes("=")) typeStr = typeStr.split("=")[0].trim();
7586
- paramTypes.push(typeStr);
7587
- }
7588
- }
7589
- }
7590
- let returnType;
7591
- const returnTypeMatch = signature.match(/\)\s*:\s*(.+?)(?={|$)/);
7592
- if (returnTypeMatch) returnType = returnTypeMatch[1].trim();
7593
- return {
7594
- paramTypes,
7595
- returnType
7596
- };
7597
- }
7598
- /**
7599
- * Splits function parameters while respecting nested angle brackets (generics).
7600
- *
7601
- * Handles cases like:
7602
- * - `a: string, b: number` → `["a: string", "b: number"]`
7603
- * - `a: Promise<string>, b: Record<string, any>` → `["a: Promise<string>", "b: Record<string, any>"]`
7604
- *
7605
- * @param paramListStr - String containing all parameters
7606
- * @returns Array of individual parameter strings
7607
- *
7608
- * @internal
7609
- */
7610
- function splitParameters(paramListStr) {
7611
- const parts = [];
7612
- let current = "";
7613
- let depth = 0;
7614
- for (const char of paramListStr) if (char === "<") {
7615
- depth++;
7616
- current += char;
7617
- } else if (char === ">") {
7618
- depth--;
7619
- current += char;
7620
- } else if (char === "," && depth === 0) {
7621
- parts.push(current);
7622
- current = "";
7623
- } else current += char;
7624
- if (current) parts.push(current);
7625
- return parts;
7626
- }
7627
- /**
7628
- * Parses a JSDoc block and the following declaration.
7629
- * Only matches if the declaration is immediately after the JSDoc (with only whitespace/keywords between).
7630
- */
7631
- function parseJsdocBlock(jsdoc, declaration, file, line) {
7679
+ function parseNapiDocItem(item) {
7680
+ const kind = normalizeNapiKind(item.kind);
7681
+ if (!kind) return null;
7632
7682
  const params = [];
7633
7683
  const examples = [];
7634
7684
  const tags = {};
7635
7685
  let description = "";
7636
7686
  let returns;
7637
7687
  let isPrivate = false;
7638
- const rawLines = jsdoc.split("\n").map((l) => l.replace(/^\s*\*\s?/, ""));
7639
- const cleanedLines = rawLines.map((l) => l.trim()).filter((l) => l);
7688
+ const rawLines = (item.jsdoc ?? "").split("\n").map((line) => {
7689
+ const trimmedStart = line.trimStart();
7690
+ const withoutStar = trimmedStart.startsWith("*") ? trimmedStart.slice(1) : trimmedStart;
7691
+ return withoutStar.startsWith(" ") ? withoutStar.slice(1) : withoutStar;
7692
+ });
7693
+ const cleanedLines = rawLines.map((line) => line.trim()).filter(Boolean);
7640
7694
  let currentExample = "";
7641
7695
  let inExample = false;
7642
7696
  let rawLineIndex = 0;
@@ -7683,47 +7737,40 @@ function parseJsdocBlock(jsdoc, declaration, file, line) {
7683
7737
  else description += "\n" + lineText;
7684
7738
  }
7685
7739
  if (inExample && currentExample) examples.push(currentExample.trim());
7686
- const firstFewLines = declaration.split("\n").slice(0, 5).join("\n");
7687
- let name = "";
7688
- let kind = "function";
7689
- const ANCHORED_FUNCTION = /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/;
7690
- const ANCHORED_CONST_FUNC = /^(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s*)?\(/;
7691
- const ANCHORED_CLASS = /^(?:export\s+)?class\s+(\w+)/;
7692
- const ANCHORED_INTERFACE = /^(?:export\s+)?interface\s+(\w+)/;
7693
- const ANCHORED_TYPE = /^(?:export\s+)?type\s+(\w+)/;
7694
- let declMatch;
7695
- if (declMatch = ANCHORED_FUNCTION.exec(firstFewLines)) {
7696
- name = declMatch[1];
7697
- kind = "function";
7698
- } else if (declMatch = ANCHORED_CONST_FUNC.exec(firstFewLines)) {
7699
- name = declMatch[1];
7700
- kind = "function";
7701
- } else if (declMatch = ANCHORED_CLASS.exec(firstFewLines)) {
7702
- name = declMatch[1];
7703
- kind = "class";
7704
- } else if (declMatch = ANCHORED_INTERFACE.exec(firstFewLines)) {
7705
- name = declMatch[1];
7706
- kind = "interface";
7707
- } else if (declMatch = ANCHORED_TYPE.exec(firstFewLines)) {
7708
- name = declMatch[1];
7709
- kind = "type";
7710
- }
7711
- if (!name) return null;
7712
- let signature;
7713
- if (kind === "function") {
7714
- const signatureTypes = extractTypesFromSignature(firstFewLines, params);
7715
- if (signatureTypes.paramTypes.length > 0) {
7716
- for (let i = 0; i < params.length && i < signatureTypes.paramTypes.length; i++) if (params[i].type === "unknown") params[i].type = signatureTypes.paramTypes[i];
7717
- }
7718
- if (signatureTypes.returnType && (!returns || returns.type === "unknown")) if (returns) returns.type = signatureTypes.returnType;
7719
- else returns = {
7720
- type: signatureTypes.returnType,
7721
- description: ""
7722
- };
7723
- signature = extractFunctionSignature(firstFewLines);
7740
+ if (params.length === 0 && item.params.length > 0) params.push(...item.params.map((param) => ({
7741
+ name: param.name,
7742
+ type: param.typeAnnotation ?? "unknown",
7743
+ description: param.description ?? "",
7744
+ optional: param.optional || void 0,
7745
+ default: param.defaultValue
7746
+ })));
7747
+ else if (item.params.length > 0) {
7748
+ const paramMap = new Map(item.params.map((param) => [param.name, param]));
7749
+ for (const param of params) {
7750
+ const rustParam = paramMap.get(param.name);
7751
+ if (!rustParam) continue;
7752
+ if (param.type === "unknown" && rustParam.typeAnnotation) param.type = rustParam.typeAnnotation;
7753
+ if (!param.description && rustParam.description) param.description = rustParam.description;
7754
+ if (param.optional === void 0 && rustParam.optional) param.optional = true;
7755
+ if (!param.default && rustParam.defaultValue) param.default = rustParam.defaultValue;
7756
+ }
7757
+ }
7758
+ if (!returns && item.returnType) returns = {
7759
+ type: item.returnType,
7760
+ description: ""
7761
+ };
7762
+ else if (returns && returns.type === "unknown" && item.returnType) returns.type = item.returnType;
7763
+ if (!description) description = item.doc ?? "";
7764
+ for (const tag of item.tags) {
7765
+ if (tag.tag === "param" || tag.tag === "returns" || tag.tag === "return" || tag.tag === "example") continue;
7766
+ if (tag.tag === "private") {
7767
+ isPrivate = true;
7768
+ continue;
7769
+ }
7770
+ if (!tags[tag.tag]) tags[tag.tag] = tag.value;
7724
7771
  }
7725
7772
  return {
7726
- name,
7773
+ name: item.name,
7727
7774
  kind,
7728
7775
  description,
7729
7776
  params: params.length > 0 ? params : void 0,
@@ -7731,39 +7778,69 @@ function parseJsdocBlock(jsdoc, declaration, file, line) {
7731
7778
  examples: examples.length > 0 ? examples : void 0,
7732
7779
  tags: Object.keys(tags).length > 0 ? tags : void 0,
7733
7780
  private: isPrivate,
7734
- file,
7735
- line,
7736
- signature
7781
+ file: item.sourcePath,
7782
+ line: item.line,
7783
+ endLine: item.endLine,
7784
+ signature: item.signature
7737
7785
  };
7738
7786
  }
7787
+ function normalizeNapiKind(kind) {
7788
+ switch (kind) {
7789
+ case "function":
7790
+ case "class":
7791
+ case "interface":
7792
+ case "type":
7793
+ case "variable":
7794
+ case "module": return kind;
7795
+ case "enum": return "type";
7796
+ default: return null;
7797
+ }
7798
+ }
7739
7799
  /**
7740
7800
  * Generates Markdown documentation from extracted docs.
7741
7801
  */
7742
7802
  function generateMarkdown(docs, options) {
7743
7803
  const result = {};
7744
- const symbolMap = buildSymbolMap(docs);
7804
+ const sortedDocs = sortExtractedDocs(docs);
7805
+ const symbolMap = buildSymbolMap(sortedDocs);
7745
7806
  if (options.groupBy === "file") {
7746
7807
  const docToFile = /* @__PURE__ */ new Map();
7747
- for (const doc of docs) {
7808
+ for (const doc of sortedDocs) {
7748
7809
  let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
7749
7810
  if (fileName === "index") fileName = "index-module";
7750
7811
  docToFile.set(doc, fileName);
7751
7812
  const markdown = generateFileMarkdown(doc, options, fileName, symbolMap);
7752
7813
  result[`${fileName}.md`] = markdown;
7753
7814
  }
7754
- result["index.md"] = generateIndex(docs, docToFile);
7815
+ result["index.md"] = generateIndex(sortedDocs, docToFile);
7755
7816
  } else {
7756
7817
  const byKind = /* @__PURE__ */ new Map();
7757
- for (const doc of docs) for (const entry of doc.entries) {
7818
+ for (const doc of sortedDocs) for (const entry of doc.entries) {
7758
7819
  const existing = byKind.get(entry.kind) || [];
7759
7820
  existing.push(entry);
7760
7821
  byKind.set(entry.kind, existing);
7761
7822
  }
7762
- for (const [kind, entries] of byKind) result[`${kind}s.md`] = generateCategoryMarkdown(kind, entries, options, symbolMap);
7823
+ for (const entries of byKind.values()) entries.sort(compareEntriesByName);
7824
+ for (const [kind, entries] of [...byKind.entries()].sort(([a], [b]) => compareStrings(a, b))) result[`${kind}s.md`] = generateCategoryMarkdown(kind, entries, options, symbolMap);
7763
7825
  result["index.md"] = generateCategoryIndex(byKind);
7764
7826
  }
7765
7827
  return result;
7766
7828
  }
7829
+ function compareStrings(a, b) {
7830
+ return a.localeCompare(b, "en", {
7831
+ numeric: true,
7832
+ sensitivity: "base"
7833
+ });
7834
+ }
7835
+ function compareEntriesByName(a, b) {
7836
+ return compareStrings(a.name, b.name);
7837
+ }
7838
+ function sortExtractedDocs(docs) {
7839
+ return [...docs].map((doc) => ({
7840
+ ...doc,
7841
+ entries: [...doc.entries].sort(compareEntriesByName)
7842
+ })).sort((a, b) => compareStrings(path$1.basename(a.file), path$1.basename(b.file)));
7843
+ }
7767
7844
  function generateFileMarkdown(doc, options, currentFileName, symbolMap) {
7768
7845
  let md = `# ${path$1.basename(doc.file)}\n\n`;
7769
7846
  if (options.githubUrl) {
@@ -7771,16 +7848,25 @@ function generateFileMarkdown(doc, options, currentFileName, symbolMap) {
7771
7848
  if (sourceLink) md += sourceLink + "\n\n";
7772
7849
  }
7773
7850
  md += `> ${doc.entries.length} documented symbol${doc.entries.length === 1 ? "" : "s"}. `;
7774
- md += "Skim the one-line surface first, then expand the accordions for details.\n\n";
7775
- md += "## Overview\n\n";
7776
- for (const entry of doc.entries) md += renderOverviewLine(entry, `#${entryAnchor(entry.name)}`);
7777
- md += "\n## Reference\n\n";
7851
+ md += "Read the signatures first, then expand each item for parameters, return types, and examples.\n\n";
7852
+ md += "## Reference\n\n";
7853
+ if (doc.entries.length > 1) md += renderDetailsControlsHtml(".ox-api-entry") + "\n\n";
7778
7854
  for (const entry of doc.entries) md += generateEntryMarkdown(entry, options, currentFileName, symbolMap);
7779
7855
  return md;
7780
7856
  }
7781
7857
  function normalizeSignature(signature) {
7782
7858
  if (!signature) return;
7783
- return signature.replace(/\s+/g, " ").replace(/^export\s+/, "").replace(/^async\s+function\s+/, "").replace(/^function\s+/, "").replace(/^class\s+/, "").trim();
7859
+ return signature.replace(/\s+/g, " ").replace(/^export\s+/, "").replace(/^declare\s+/, "").replace(/^abstract\s+/, "").replace(/^async\s+function\s+/, "").replace(/^function\s+/, "").replace(/^class\s+/, "").replace(/^interface\s+/, "").replace(/^type\s+/, "").trim();
7860
+ }
7861
+ function formatKindLabel(kind) {
7862
+ switch (kind) {
7863
+ case "function": return "fn";
7864
+ case "interface": return "interface";
7865
+ case "class": return "class";
7866
+ case "type": return "type";
7867
+ case "const": return "const";
7868
+ default: return kind;
7869
+ }
7784
7870
  }
7785
7871
  function renderOverviewLine(entry, href) {
7786
7872
  const signature = normalizeSignature(entry.signature);
@@ -7793,60 +7879,56 @@ function renderOverviewLine(entry, href) {
7793
7879
  function renderOverviewHtmlItem(entry, href) {
7794
7880
  const signature = normalizeSignature(entry.signature);
7795
7881
  const summary = cleanSummaryText(entry.description, 88);
7796
- const fragments = [`<a href="${escapeHtml$3(href)}"><code>${escapeHtml$3(entry.name)}</code></a>`, `<span class="ox-api-module__kind">${escapeHtml$3(entry.kind)}</span>`];
7797
- if (signature) fragments.push(`<code>${escapeHtml$3(signature)}</code>`);
7798
- if (summary) fragments.push(`<span>${renderInlineHtml(summary)}</span>`);
7799
- return `<li>${fragments.join("")}</li>`;
7882
+ const heading = signature ? `<a href="${escapeHtml$3(href)}" class="ox-api-module__link">${renderHighlightedInlineCodeHtml(signature, "ox-api-module__signature ox-api-module__signature--highlighted")}</a>` : `<a href="${escapeHtml$3(href)}" class="ox-api-module__link"><code class="ox-api-module__name">${escapeHtml$3(entry.name)}</code></a>`;
7883
+ return `<li><span class="ox-api-module__kind">${escapeHtml$3(formatKindLabel(entry.kind))}</span><div class="ox-api-module__item">${heading}${summary ? `<span class="ox-api-module__summary">${renderInlineHtml(summary)}</span>` : ""}</div></li>`;
7800
7884
  }
7801
- function renderParamsTableHtml(params) {
7802
- return `<div class="ox-api-entry__section">
7885
+ function renderParamsListHtml(params) {
7886
+ return `<div class="ox-api-entry__section ox-api-entry__section--params">
7803
7887
  <h4>Parameters</h4>
7804
- <table>
7805
- <thead>
7806
- <tr><th>Name</th><th>Type</th><th>Description</th></tr>
7807
- </thead>
7808
- <tbody>
7888
+ <ul class="ox-api-entry__params">
7809
7889
  ${params.map((param) => {
7810
7890
  const flags = [param.optional ? "optional" : "", param.default ? `default: ${param.default}` : ""].filter(Boolean);
7811
7891
  const description = [param.description, flags.join(" · ")].filter(Boolean).join(" — ");
7812
- return `<tr>
7813
- <td><code>${escapeHtml$3(param.name)}</code></td>
7814
- <td><code>${escapeHtml$3(param.type)}</code></td>
7815
- <td>${renderInlineHtml(description)}</td>
7816
- </tr>`;
7892
+ return `<li class="ox-api-entry__param">
7893
+ <div class="ox-api-entry__param-heading">
7894
+ <code class="ox-api-entry__param-name">${escapeHtml$3(param.name)}</code>
7895
+ <code class="ox-api-entry__param-type">${escapeHtml$3(param.type)}</code>
7896
+ </div>
7897
+ ${description ? `<p class="ox-api-entry__param-description">${renderInlineHtml(description)}</p>` : ""}
7898
+ </li>`;
7817
7899
  }).join("\n")}
7818
- </tbody>
7819
- </table>
7900
+ </ul>
7820
7901
  </div>`;
7821
7902
  }
7822
7903
  function renderTagListHtml(tags) {
7823
- return `<div class="ox-api-entry__section">
7904
+ return `<div class="ox-api-entry__section ox-api-entry__section--tags">
7824
7905
  <h4>Tags</h4>
7825
- <ul class="ox-api-entry__tags">${Object.entries(tags).map(([tag, value]) => `<li><span class="ox-api-entry__tag-name">@${escapeHtml$3(tag)}</span><span>${renderInlineHtml(value)}</span></li>`).join("")}</ul>
7906
+ <ul class="ox-api-entry__tags">${Object.entries(tags).map(([tag, value]) => `<li><span class="ox-api-entry__tag-name">@${escapeHtml$3(tag)}</span><span class="ox-api-entry__tag-value">${renderInlineHtml(value)}</span></li>`).join("")}</ul>
7826
7907
  </div>`;
7827
7908
  }
7828
7909
  function generateEntryMarkdown(entry, options, currentFileName, symbolMap) {
7829
7910
  const processedDescription = entry.description && currentFileName && symbolMap ? convertSymbolLinks(entry.description, currentFileName, symbolMap) : entry.description;
7830
7911
  const summarySignature = normalizeSignature(entry.signature);
7831
- const sourceHref = options?.githubUrl ? generateSourceHref(entry.file, options.githubUrl, entry.line) : void 0;
7912
+ const sourceHref = options?.githubUrl ? generateSourceHref(entry.file, options.githubUrl, entry.line, entry.endLine) : void 0;
7832
7913
  let body = "";
7833
- if (processedDescription) body += renderParagraphsHtml(processedDescription) + "\n";
7914
+ if (processedDescription) body += renderMarkdownBlocksHtml(processedDescription) + "\n";
7834
7915
  if (sourceHref) body += `<p class="ox-api-entry__source"><a href="${escapeHtml$3(sourceHref)}">View source</a></p>\n`;
7835
- if (entry.signature) body += `<div class="ox-api-entry__section">\n<h4>Signature</h4>\n${renderCodeBlockHtml(entry.signature)}\n</div>\n`;
7836
- if (entry.params && entry.params.length > 0) body += renderParamsTableHtml(entry.params) + "\n";
7837
- if (entry.returns) body += `<div class="ox-api-entry__section">
7916
+ if (entry.params && entry.params.length > 0) body += renderParamsListHtml(entry.params) + "\n";
7917
+ if (entry.returns) body += `<div class="ox-api-entry__section ox-api-entry__section--returns">
7838
7918
  <h4>Returns</h4>
7839
- <p><code>${escapeHtml$3(entry.returns.type)}</code>${entry.returns.description ? ` — ${renderInlineHtml(entry.returns.description)}` : ""}</p>
7919
+ <div class="ox-api-entry__return">
7920
+ <code class="ox-api-entry__return-type">${escapeHtml$3(entry.returns.type)}</code>
7921
+ ${entry.returns.description ? `<p class="ox-api-entry__return-description">${renderInlineHtml(entry.returns.description)}</p>` : ""}
7922
+ </div>
7840
7923
  </div>\n`;
7841
7924
  if (entry.examples && entry.examples.length > 0) {
7842
7925
  const examplesHtml = entry.examples.map((example) => example.replace(/^```\w*\n?/, "").replace(/\n?```$/, "")).map((example) => renderCodeBlockHtml(example, "ts")).join("\n");
7843
- body += `<div class="ox-api-entry__section">\n<h4>Examples</h4>\n${examplesHtml}\n</div>\n`;
7926
+ body += `<div class="ox-api-entry__section ox-api-entry__section--examples">\n<h4>Examples</h4>\n${examplesHtml}\n</div>\n`;
7844
7927
  }
7845
7928
  if (entry.tags && Object.keys(entry.tags).length > 0) body += renderTagListHtml(entry.tags) + "\n";
7846
7929
  const summaryDescription = cleanSummaryText(processedDescription, summarySignature ? 80 : 120);
7847
- const summaryParts = [`<span class="ox-api-entry__kind">${escapeHtml$3(entry.kind)}</span>`, `<code class="ox-api-entry__name">${escapeHtml$3(entry.name)}</code>`];
7848
- if (summarySignature) summaryParts.push(`<code class="ox-api-entry__signature">${escapeHtml$3(summarySignature)}</code>`);
7849
- if (summaryDescription) summaryParts.push(`<span class="ox-api-entry__description">${renderInlineHtml(summaryDescription)}</span>`);
7930
+ const summaryHeading = summarySignature ? renderHighlightedInlineCodeHtml(summarySignature, "ox-api-entry__signature ox-api-entry__signature--highlighted") : `<code class="ox-api-entry__name">${escapeHtml$3(entry.name)}</code>`;
7931
+ const summaryParts = [`<span class="ox-api-entry__kind">${escapeHtml$3(formatKindLabel(entry.kind))}</span>`, `<span class="ox-api-entry__summary-main">${summaryHeading}${summaryDescription ? `<span class="ox-api-entry__description">${renderInlineHtml(summaryDescription)}</span>` : ""}</span>`];
7850
7932
  return `<details id="${entryAnchor(entry.name)}" class="ox-api-entry">
7851
7933
  <summary>${summaryParts.join("")}</summary>
7852
7934
  <div class="ox-api-entry__body">
@@ -7861,6 +7943,7 @@ function generateIndex(docs, docToFile) {
7861
7943
  md += "Generated by [Ox Content](https://github.com/ubugeeei/ox-content)\n\n";
7862
7944
  md += "> Use search scopes like `@api transform` to limit results to the generated API reference.\n\n";
7863
7945
  md += "## Modules\n\n";
7946
+ if (docs.length > 1) md += renderDetailsControlsHtml(".ox-api-module") + "\n\n";
7864
7947
  for (const doc of docs) {
7865
7948
  const displayName = path$1.basename(doc.file, path$1.extname(doc.file));
7866
7949
  let fileName = displayName;
@@ -7891,13 +7974,14 @@ function generateCategoryMarkdown(kind, entries, options, symbolMap) {
7891
7974
  md += "## Overview\n\n";
7892
7975
  for (const entry of entries) md += renderOverviewLine(entry, `#${entryAnchor(entry.name)}`);
7893
7976
  md += "\n## Reference\n\n";
7977
+ if (entries.length > 1) md += renderDetailsControlsHtml(".ox-api-entry") + "\n\n";
7894
7978
  for (const entry of entries) md += generateEntryMarkdown(entry, options, categoryFileName, symbolMap);
7895
7979
  return md;
7896
7980
  }
7897
7981
  function generateCategoryIndex(byKind) {
7898
7982
  let md = "# API Documentation\n\n";
7899
7983
  md += "Generated by [Ox Content](https://github.com/ubugeeei/ox-content)\n\n";
7900
- for (const [kind, entries] of byKind) {
7984
+ for (const [kind, entries] of [...byKind.entries()].sort(([a], [b]) => compareStrings(a, b))) {
7901
7985
  const kindTitle = kind.charAt(0).toUpperCase() + kind.slice(1) + "s";
7902
7986
  md += `## [${kindTitle}](./${kind}s.md)\n\n`;
7903
7987
  md += `> ${entries.length} item${entries.length === 1 ? "" : "s"}.\n\n`;
@@ -7987,18 +8071,19 @@ async function writeDocs(docs, outDir, extractedDocs, options) {
7987
8071
  * Resolves docs options with defaults.
7988
8072
  */
7989
8073
  /**
7990
- * Generates a GitHub source link for a file and optional line number.
8074
+ * Generates a GitHub source link for a file and optional line range.
7991
8075
  *
7992
8076
  * @param filePath - Full path to the source file
7993
8077
  * @param githubUrl - Base GitHub repository URL
7994
- * @param lineNumber - Optional line number to link to
8078
+ * @param lineNumber - Optional start line number to link to
8079
+ * @param endLineNumber - Optional end line number to link to
7995
8080
  * @returns Absolute GitHub URL to source code
7996
8081
  */
7997
- function generateSourceHref(filePath, githubUrl, lineNumber) {
7998
- return `${githubUrl}/blob/main/${filePath.replace(/^.*?\/(npm|packages|crates|src)\//, "$1/")}${lineNumber ? `#L${lineNumber}` : ""}`;
8082
+ function generateSourceHref(filePath, githubUrl, lineNumber, endLineNumber) {
8083
+ return `${githubUrl}/blob/main/${filePath.replace(/^.*?\/(npm|packages|crates|src)\//, "$1/")}${lineNumber ? endLineNumber && endLineNumber > lineNumber ? `#L${lineNumber}-L${endLineNumber}` : `#L${lineNumber}` : ""}`;
7999
8084
  }
8000
- function generateSourceLink(filePath, githubUrl, lineNumber) {
8001
- return `**[Source](${generateSourceHref(filePath, githubUrl, lineNumber)})**`;
8085
+ function generateSourceLink(filePath, githubUrl, lineNumber, endLineNumber) {
8086
+ return `**[Source](${generateSourceHref(filePath, githubUrl, lineNumber, endLineNumber)})**`;
8002
8087
  }
8003
8088
  function resolveDocsOptions(options) {
8004
8089
  if (options === false) return false;
@@ -8156,23 +8241,74 @@ async function openBrowser() {
8156
8241
  function escapeHtml$2(str) {
8157
8242
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
8158
8243
  }
8244
+ function normalizeBrandValue(str) {
8245
+ return str.replace(/\s+/g, "").toLowerCase();
8246
+ }
8247
+ function renderWordmarkSvg() {
8248
+ return `<svg width="430" height="102" viewBox="0 0 270 64" fill="none" xmlns="http://www.w3.org/2000/svg">
8249
+ <defs>
8250
+ <linearGradient id="ogWordmarkGradient" x1="286" y1="10" x2="320" y2="54" gradientUnits="userSpaceOnUse">
8251
+ <stop offset="0%" stop-color="#355cff"/>
8252
+ <stop offset="100%" stop-color="#74c7ff"/>
8253
+ </linearGradient>
8254
+ </defs>
8255
+ <text
8256
+ x="2"
8257
+ y="43"
8258
+ fill="#eff6ff"
8259
+ font-family="IBM Plex Sans, IBM Plex Mono, Avenir Next, Segoe UI, sans-serif"
8260
+ font-size="34"
8261
+ font-weight="700"
8262
+ letter-spacing="-1.4"
8263
+ >
8264
+ OXCONTENT
8265
+ </text>
8266
+ <text
8267
+ x="213"
8268
+ y="43.5"
8269
+ fill="#eff6ff"
8270
+ font-family="IBM Plex Sans, IBM Plex Mono, Avenir Next, Segoe UI, sans-serif"
8271
+ font-size="40"
8272
+ font-weight="400"
8273
+ >
8274
+ (
8275
+ </text>
8276
+ <g transform="translate(216 9) scale(0.089) rotate(-7 256 256)">
8277
+ <path
8278
+ d="M161 96H286C298 96 309 101 318 110L352 144C361 153 366 164 366 176V386C366 399 355 410 342 410H161C148 410 138 399 138 386V120C138 107 148 96 161 96Z"
8279
+ fill="url(#ogWordmarkGradient)"
8280
+ />
8281
+ </g>
8282
+ <text
8283
+ x="252"
8284
+ y="43.5"
8285
+ fill="#eff6ff"
8286
+ font-family="IBM Plex Sans, IBM Plex Mono, Avenir Next, Segoe UI, sans-serif"
8287
+ font-size="40"
8288
+ font-weight="400"
8289
+ >
8290
+ )
8291
+ </text>
8292
+ </svg>`;
8293
+ }
8159
8294
  /**
8160
8295
  * Returns the built-in default template function.
8161
8296
  */
8162
8297
  function getDefaultTemplate() {
8163
8298
  return function defaultTemplate(props) {
8164
- const { title, description, siteName, tags } = props;
8165
- const tagsHtml = tags?.length ? `<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:auto;">
8166
- ${tags.map((tag) => `<span style="background:rgba(255,255,255,0.15);color:#e2e8f0;padding:4px 12px;border-radius:16px;font-size:14px;">${escapeHtml$2(tag)}</span>`).join("")}
8167
- </div>` : "";
8168
- return `<div style="width:100%;height:100%;display:flex;flex-direction:column;justify-content:center;padding:60px 80px;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 50%,#0f3460 100%);font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
8169
- <div style="display:flex;flex-direction:column;gap:16px;flex:1;justify-content:center;">
8170
- <h1 style="font-size:56px;font-weight:700;color:#ffffff;line-height:1.2;margin:0;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;">${escapeHtml$2(title)}</h1>
8171
- ${description ? `<p style="font-size:24px;color:#94a3b8;line-height:1.5;margin:0;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;">${escapeHtml$2(description)}</p>` : ""}
8172
- </div>
8173
- <div style="display:flex;align-items:flex-end;justify-content:space-between;margin-top:auto;">
8174
- ${siteName ? `<span style="font-size:20px;color:#64748b;font-weight:500;">${escapeHtml$2(siteName)}</span>` : ""}
8175
- ${tagsHtml}
8299
+ const { title, description, siteName } = props;
8300
+ const rawBrand = siteName?.trim() ? siteName : "Ox Content";
8301
+ const isBrandCard = normalizeBrandValue(title) === normalizeBrandValue(rawBrand);
8302
+ const heroTitle = isBrandCard ? "cargo doc for JavaScript" : title;
8303
+ const heroDescription = isBrandCard ? "Rust-powered docs and high-performance Markdown tooling." : description && description.trim().length > 0 ? description : "Rust-powered docs and Markdown tooling.";
8304
+ const descriptionHtml = heroDescription.trim().length > 0 ? `<p style="max-width:760px;font-size:28px;color:#93a4c3;line-height:1.45;margin:0;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;">${escapeHtml$2(heroDescription)}</p>` : "";
8305
+ return `<div style="width:100%;height:100%;position:relative;overflow:hidden;box-sizing:border-box;padding:56px 64px 52px;background:#0b1220;font-family:'IBM Plex Sans','Avenir Next','Segoe UI',system-ui,sans-serif;color:#eff6ff;border:1px solid #223252;border-top:4px solid #4f6fae;">
8306
+ <div style="position:relative;z-index:1;display:flex;flex-direction:column;height:100%;">
8307
+ <div style="display:flex;align-items:flex-start;">${renderWordmarkSvg()}</div>
8308
+ <div style="display:flex;flex-direction:column;justify-content:center;gap:24px;max-width:860px;flex:1;padding:22px 0 0;">
8309
+ <h1 style="font-size:78px;font-weight:700;color:#eff6ff;line-height:1.02;letter-spacing:-0.055em;margin:0;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;">${escapeHtml$2(heroTitle)}</h1>
8310
+ ${descriptionHtml}
8311
+ </div>
8176
8312
  </div>
8177
8313
  </div>`;
8178
8314
  };
@@ -8886,31 +9022,32 @@ initIslands((el, props) => {
8886
9022
  const defaultTheme = {
8887
9023
  name: "default",
8888
9024
  colors: {
8889
- primary: "#e04d0a",
8890
- primaryHover: "#f5602a",
9025
+ primary: "#4f6fae",
9026
+ primaryHover: "#425f96",
8891
9027
  background: "#ffffff",
8892
- backgroundAlt: "#f8f9fa",
8893
- text: "#1a1a1a",
8894
- textMuted: "#666666",
8895
- border: "#e5e7eb",
8896
- codeBackground: "#1e293b",
8897
- codeText: "#e2e8f0"
9028
+ backgroundAlt: "#f5f7fb",
9029
+ text: "#131a30",
9030
+ textMuted: "#4f607b",
9031
+ border: "#d2dbea",
9032
+ codeBackground: "#101a31",
9033
+ codeText: "#edf3ff"
8898
9034
  },
8899
9035
  darkColors: {
8900
- primary: "#f5714a",
8901
- primaryHover: "#ff8a66",
8902
- background: "#141414",
8903
- backgroundAlt: "#141414",
8904
- text: "#e5e5e5",
8905
- textMuted: "#a3a3a3",
8906
- border: "#2a2a2a",
8907
- codeBackground: "#1a1a1a",
8908
- codeText: "#e5e5e5"
9036
+ primary: "#86a4da",
9037
+ primaryHover: "#a3bbe8",
9038
+ background: "#060816",
9039
+ backgroundAlt: "#0d1528",
9040
+ text: "#ebf2ff",
9041
+ textMuted: "#8ea0bf",
9042
+ border: "#223252",
9043
+ codeBackground: "#0a1020",
9044
+ codeText: "#e7f0ff"
8909
9045
  },
8910
9046
  fonts: {
8911
- sans: "system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif",
8912
- mono: "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace"
9047
+ sans: "\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif",
9048
+ mono: "\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace"
8913
9049
  },
9050
+ entryPage: { mode: "default" },
8914
9051
  layout: {
8915
9052
  sidebarWidth: "260px",
8916
9053
  headerHeight: "60px",
@@ -8918,6 +9055,9 @@ const defaultTheme = {
8918
9055
  },
8919
9056
  header: {
8920
9057
  logo: void 0,
9058
+ logoLight: void 0,
9059
+ logoDark: void 0,
9060
+ showSiteNameText: true,
8921
9061
  logoWidth: 28,
8922
9062
  logoHeight: 28
8923
9063
  },
@@ -8995,6 +9135,7 @@ function resolveTheme(config) {
8995
9135
  colors: merged.colors ?? defaultTheme.colors,
8996
9136
  darkColors: merged.darkColors ?? defaultTheme.darkColors,
8997
9137
  fonts: merged.fonts ?? defaultTheme.fonts,
9138
+ entryPage: merged.entryPage ?? defaultTheme.entryPage,
8998
9139
  layout: merged.layout ?? defaultTheme.layout,
8999
9140
  header: merged.header ?? defaultTheme.header,
9000
9141
  footer: merged.footer ?? defaultTheme.footer,
@@ -9035,13 +9176,17 @@ function themeToNapi(theme) {
9035
9176
  sans: theme.fonts.sans,
9036
9177
  mono: theme.fonts.mono
9037
9178
  } : void 0,
9179
+ entryPage: theme.entryPage.mode ? { mode: theme.entryPage.mode } : void 0,
9038
9180
  layout: theme.layout.sidebarWidth ? {
9039
9181
  sidebarWidth: theme.layout.sidebarWidth,
9040
9182
  headerHeight: theme.layout.headerHeight,
9041
9183
  maxContentWidth: theme.layout.maxContentWidth
9042
9184
  } : void 0,
9043
- header: theme.header.logo ? {
9185
+ header: theme.header.logo || theme.header.logoLight || theme.header.logoDark ? {
9044
9186
  logo: theme.header.logo,
9187
+ logoLight: theme.header.logoLight,
9188
+ logoDark: theme.header.logoDark,
9189
+ showSiteNameText: theme.header.showSiteNameText,
9045
9190
  logoWidth: theme.header.logoWidth,
9046
9191
  logoHeight: theme.header.logoHeight
9047
9192
  } : void 0,
@@ -9090,55 +9235,65 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9090
9235
  --sidebar-width: 260px;
9091
9236
  --header-height: 60px;
9092
9237
  --max-content-width: 960px;
9093
- --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9094
- --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
9238
+ --font-sans: 'IBM Plex Sans', 'Avenir Next', 'Segoe UI Variable', 'Segoe UI', sans-serif;
9239
+ --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
9095
9240
  --color-bg: #ffffff;
9096
- --color-bg-alt: #f8f9fa;
9097
- --color-text: #1a1a1a;
9098
- --color-text-muted: #666666;
9099
- --color-border: #e5e7eb;
9100
- --color-primary: #b7410e;
9101
- --color-primary-hover: #ce5937;
9102
- --color-code-bg: #1e293b;
9103
- --color-code-text: #e2e8f0;
9241
+ --color-bg-alt: #f5f7fb;
9242
+ --color-text: #131a30;
9243
+ --color-text-muted: #4f607b;
9244
+ --color-border: #d2dbea;
9245
+ --color-primary: #4f6fae;
9246
+ --color-primary-hover: #425f96;
9247
+ --color-code-bg: #101a31;
9248
+ --color-code-bg-top: #18264a;
9249
+ --color-code-text: #edf3ff;
9104
9250
  --color-code-line-highlight: rgba(56, 189, 248, 0.16);
9105
9251
  --color-code-line-warning: rgba(245, 158, 11, 0.18);
9106
9252
  --color-code-line-warning-border: #f59e0b;
9107
9253
  --color-code-line-error: rgba(239, 68, 68, 0.18);
9108
9254
  --color-code-line-error-border: #ef4444;
9255
+ --color-code-frame-border: rgba(147, 166, 200, 0.46);
9256
+ --surface-noise-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23noise)' opacity='0.062'/%3E%3C/svg%3E");
9257
+ --surface-noise-size: 164px 164px;
9109
9258
  }
9110
9259
  [data-theme="dark"] {
9111
- --color-bg: #141414;
9112
- --color-bg-alt: #141414;
9113
- --color-text: #e5e5e5;
9114
- --color-text-muted: #a3a3a3;
9115
- --color-border: #2a2a2a;
9116
- --color-primary: #c9714a;
9117
- --color-primary-hover: #d4845f;
9118
- --color-code-bg: #1a1a1a;
9119
- --color-code-text: #e5e5e5;
9260
+ --color-bg: #060816;
9261
+ --color-bg-alt: #0d1528;
9262
+ --color-text: #ebf2ff;
9263
+ --color-text-muted: #8ea0bf;
9264
+ --color-border: #223252;
9265
+ --color-primary: #86a4da;
9266
+ --color-primary-hover: #a3bbe8;
9267
+ --color-code-bg: #0a1020;
9268
+ --color-code-bg-top: #0a1020;
9269
+ --color-code-text: #e7f0ff;
9120
9270
  --color-code-line-highlight: rgba(14, 165, 233, 0.2);
9121
9271
  --color-code-line-warning: rgba(245, 158, 11, 0.2);
9122
9272
  --color-code-line-warning-border: #f59e0b;
9123
9273
  --color-code-line-error: rgba(239, 68, 68, 0.22);
9124
9274
  --color-code-line-error-border: #f87171;
9275
+ --color-code-frame-border: rgba(34, 50, 82, 0.92);
9276
+ --surface-noise-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.25' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23noise)' opacity='0.098'/%3E%3C/svg%3E");
9125
9277
  }
9126
9278
  @media (prefers-color-scheme: dark) {
9127
9279
  :root:not([data-theme="light"]) {
9128
- --color-bg: #141414;
9129
- --color-bg-alt: #141414;
9130
- --color-text: #e5e5e5;
9131
- --color-text-muted: #a3a3a3;
9132
- --color-border: #2a2a2a;
9133
- --color-primary: #c9714a;
9134
- --color-primary-hover: #d4845f;
9135
- --color-code-bg: #1a1a1a;
9136
- --color-code-text: #e5e5e5;
9280
+ --color-bg: #060816;
9281
+ --color-bg-alt: #0d1528;
9282
+ --color-text: #ebf2ff;
9283
+ --color-text-muted: #8ea0bf;
9284
+ --color-border: #223252;
9285
+ --color-primary: #86a4da;
9286
+ --color-primary-hover: #a3bbe8;
9287
+ --color-code-bg: #0a1020;
9288
+ --color-code-bg-top: #0a1020;
9289
+ --color-code-text: #e7f0ff;
9137
9290
  --color-code-line-highlight: rgba(14, 165, 233, 0.2);
9138
9291
  --color-code-line-warning: rgba(245, 158, 11, 0.2);
9139
9292
  --color-code-line-warning-border: #f59e0b;
9140
9293
  --color-code-line-error: rgba(239, 68, 68, 0.22);
9141
9294
  --color-code-line-error-border: #f87171;
9295
+ --color-code-frame-border: rgba(34, 50, 82, 0.92);
9296
+ --surface-noise-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.25' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23noise)' opacity='0.098'/%3E%3C/svg%3E");
9142
9297
  }
9143
9298
  }
9144
9299
  * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -9148,6 +9303,10 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9148
9303
  line-height: 1.7;
9149
9304
  color: var(--color-text);
9150
9305
  background: var(--color-bg);
9306
+ background-image: var(--surface-noise-image);
9307
+ background-size: var(--surface-noise-size);
9308
+ background-repeat: repeat;
9309
+ background-blend-mode: soft-light;
9151
9310
  }
9152
9311
  a { color: var(--color-primary); text-decoration: none; }
9153
9312
  a:hover { color: var(--color-primary-hover); text-decoration: underline; }
@@ -9166,6 +9325,18 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9166
9325
  padding: 0 1.5rem;
9167
9326
  z-index: 100;
9168
9327
  }
9328
+ .header,
9329
+ .sidebar,
9330
+ .search-modal,
9331
+ .mobile-footer,
9332
+ .content .ox-api-entry,
9333
+ .content .ox-api-module,
9334
+ .content blockquote.ox-callout {
9335
+ background-image: var(--surface-noise-image);
9336
+ background-size: var(--surface-noise-size);
9337
+ background-repeat: repeat;
9338
+ background-blend-mode: soft-light;
9339
+ }
9169
9340
  .header-title {
9170
9341
  font-size: 1.25rem;
9171
9342
  font-weight: 600;
@@ -9190,7 +9361,7 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9190
9361
  padding: 0.5rem 0.75rem;
9191
9362
  background: var(--color-bg-alt);
9192
9363
  border: 1px solid var(--color-border);
9193
- border-radius: 6px;
9364
+ border-radius: 4px;
9194
9365
  color: var(--color-text-muted);
9195
9366
  cursor: pointer;
9196
9367
  font-size: 0.875rem;
@@ -9216,7 +9387,6 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9216
9387
  inset: 0;
9217
9388
  z-index: 200;
9218
9389
  background: rgba(0,0,0,0.6);
9219
- backdrop-filter: blur(4px);
9220
9390
  justify-content: center;
9221
9391
  padding-top: 10vh;
9222
9392
  }
@@ -9227,9 +9397,8 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9227
9397
  margin: 0 1rem;
9228
9398
  background: var(--color-bg);
9229
9399
  border: 1px solid var(--color-border);
9230
- border-radius: 12px;
9400
+ border-radius: 4px;
9231
9401
  overflow: hidden;
9232
- box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4);
9233
9402
  max-height: 70vh;
9234
9403
  display: flex;
9235
9404
  flex-direction: column;
@@ -9269,7 +9438,7 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9269
9438
  .search-result {
9270
9439
  display: block;
9271
9440
  padding: 0.75rem 1rem;
9272
- border-radius: 8px;
9441
+ border-radius: 4px;
9273
9442
  color: var(--color-text);
9274
9443
  text-decoration: none;
9275
9444
  }
@@ -9300,7 +9469,7 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9300
9469
  border: none;
9301
9470
  cursor: pointer;
9302
9471
  padding: 0.5rem;
9303
- border-radius: 6px;
9472
+ border-radius: 4px;
9304
9473
  color: var(--color-text-muted);
9305
9474
  transition: background 0.15s, color 0.15s;
9306
9475
  }
@@ -9329,38 +9498,50 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9329
9498
  left: 0;
9330
9499
  bottom: 0;
9331
9500
  width: var(--sidebar-width);
9332
- background: var(--color-bg-alt);
9333
- border-right: 1px solid var(--color-border);
9501
+ background: color-mix(in srgb, var(--color-bg-alt) 16%, var(--color-bg));
9502
+ border-right: 1px solid color-mix(in srgb, var(--color-border) 48%, transparent);
9334
9503
  overflow-y: auto;
9335
- padding: 1.5rem 1rem;
9504
+ padding: 1rem 0.875rem 1.5rem;
9505
+ }
9506
+ .sidebar--entry { display: none; }
9507
+ .sidebar nav {
9508
+ display: flex;
9509
+ flex-direction: column;
9510
+ gap: 1rem;
9336
9511
  }
9337
- .nav-section { margin-bottom: 1.5rem; }
9512
+ .nav-section { margin-bottom: 0; }
9338
9513
  .nav-title {
9339
- font-size: 0.75rem;
9514
+ font-size: 0.6875rem;
9340
9515
  font-weight: 600;
9341
9516
  text-transform: uppercase;
9342
- letter-spacing: 0.05em;
9517
+ letter-spacing: 0.08em;
9343
9518
  color: var(--color-text-muted);
9344
- margin-bottom: 0.5rem;
9345
- padding: 0 0.75rem;
9519
+ margin-bottom: 0.4rem;
9520
+ padding: 0 0.625rem;
9521
+ }
9522
+ .nav-list {
9523
+ list-style: none;
9524
+ display: flex;
9525
+ flex-direction: column;
9526
+ gap: 0.125rem;
9346
9527
  }
9347
- .nav-list { list-style: none; }
9348
- .nav-item { margin: 0.125rem 0; }
9528
+ .nav-item { margin: 0; }
9349
9529
  .nav-link {
9350
9530
  display: block;
9351
- padding: 0.5rem 0.75rem;
9352
- border-radius: 6px;
9353
- color: var(--color-text);
9531
+ padding: 0.45rem 0.625rem;
9532
+ border-radius: 0;
9533
+ color: color-mix(in srgb, var(--color-text) 92%, var(--color-text-muted));
9354
9534
  font-size: 0.875rem;
9355
- transition: background 0.15s;
9356
9535
  }
9357
9536
  .nav-link:hover {
9358
- background: var(--color-border);
9537
+ background: color-mix(in srgb, var(--color-bg-alt) 58%, transparent);
9538
+ color: var(--color-text);
9359
9539
  text-decoration: none;
9360
9540
  }
9361
9541
  .nav-link.active {
9362
- background: var(--color-primary);
9363
- color: white;
9542
+ background: color-mix(in srgb, var(--color-bg-alt) 72%, transparent);
9543
+ color: var(--color-text);
9544
+ font-weight: 600;
9364
9545
  }
9365
9546
 
9366
9547
  /* Main content */
@@ -9415,8 +9596,31 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9415
9596
  padding: 0.5rem 1rem;
9416
9597
  margin: 1rem 0;
9417
9598
  background: var(--color-bg-alt);
9418
- border-radius: 0 6px 6px 0;
9599
+ border-radius: 0 4px 4px 0;
9600
+ }
9601
+ .content blockquote.ox-callout {
9602
+ --callout-accent: var(--color-primary);
9603
+ border-left-width: 3px;
9604
+ border-left-color: var(--callout-accent);
9605
+ padding: 0.9rem 1rem;
9606
+ border-radius: 4px;
9607
+ background: color-mix(in srgb, var(--color-bg-alt) 92%, var(--callout-accent) 8%);
9419
9608
  }
9609
+ .content blockquote.ox-callout.ox-callout--note,
9610
+ .content blockquote.ox-callout.ox-callout--important { --callout-accent: var(--color-primary); }
9611
+ .content blockquote.ox-callout.ox-callout--tip { --callout-accent: #0891b2; }
9612
+ .content blockquote.ox-callout.ox-callout--warning { --callout-accent: #d97706; }
9613
+ .content blockquote.ox-callout.ox-callout--caution { --callout-accent: #dc2626; }
9614
+ .content .ox-callout-title {
9615
+ margin: 0 0 0.5rem;
9616
+ font-size: 0.75rem;
9617
+ font-weight: 700;
9618
+ letter-spacing: 0.08em;
9619
+ text-transform: uppercase;
9620
+ color: var(--callout-accent, var(--color-primary));
9621
+ }
9622
+ .content blockquote.ox-callout > :last-child { margin-bottom: 0; }
9623
+ .content blockquote.ox-callout > :not(.ox-callout-title):first-of-type { margin-top: 0; }
9420
9624
  .content code {
9421
9625
  font-family: var(--font-mono);
9422
9626
  font-size: 0.875em;
@@ -9426,18 +9630,26 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9426
9630
  word-break: break-all;
9427
9631
  }
9428
9632
  .content pre {
9429
- background: var(--color-code-bg);
9633
+ background: linear-gradient(
9634
+ 180deg,
9635
+ var(--color-code-bg-top) 0,
9636
+ var(--color-code-bg) 3.5rem
9637
+ ) !important;
9430
9638
  color: var(--color-code-text);
9431
9639
  padding: 1rem 1.25rem;
9432
- border-radius: 8px;
9640
+ border-radius: 4px;
9641
+ border: 1px solid var(--color-code-frame-border);
9433
9642
  overflow-x: auto;
9434
9643
  margin: 1.5rem 0;
9435
9644
  line-height: 1.5;
9436
9645
  }
9437
9646
  .content pre code {
9438
9647
  background: transparent;
9648
+ border: 0;
9439
9649
  padding: 0;
9650
+ border-radius: 0;
9440
9651
  font-size: 0.8125rem;
9652
+ word-break: normal;
9441
9653
  }
9442
9654
  .content pre.ox-code-block code {
9443
9655
  display: block;
@@ -9452,11 +9664,11 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9452
9664
  }
9453
9665
  .content pre.ox-code-block .ox-code-line--warning {
9454
9666
  background: var(--color-code-line-warning);
9455
- box-shadow: inset 3px 0 0 var(--color-code-line-warning-border);
9667
+ border-left: 3px solid var(--color-code-line-warning-border);
9456
9668
  }
9457
9669
  .content pre.ox-code-block .ox-code-line--error {
9458
9670
  background: var(--color-code-line-error);
9459
- box-shadow: inset 3px 0 0 var(--color-code-line-error-border);
9671
+ border-left: 3px solid var(--color-code-line-error-border);
9460
9672
  }
9461
9673
  .content table {
9462
9674
  width: 100%;
@@ -9470,21 +9682,355 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9470
9682
  text-align: left;
9471
9683
  }
9472
9684
  .content th { background: var(--color-bg-alt); font-weight: 600; }
9473
- .content img { max-width: 100%; height: auto; border-radius: 8px; display: block; }
9685
+ .content img { max-width: 100%; height: auto; border-radius: 4px; display: block; }
9474
9686
  .content img[alt*="Logo"] { max-width: 200px; display: block; margin: 1rem 0; }
9475
9687
  .content img[alt*="Architecture"] { max-width: 600px; }
9476
9688
  .content img[alt*="Benchmark"] { max-width: 680px; }
9477
9689
  .content hr { border: none; border-top: 1px solid var(--color-border); margin: 2rem 0; }
9690
+ .content .ox-api-controls {
9691
+ display: flex;
9692
+ justify-content: flex-end;
9693
+ align-items: center;
9694
+ gap: 0.5rem;
9695
+ margin: 0 0 1rem;
9696
+ }
9697
+ .content .ox-api-controls__button {
9698
+ appearance: none;
9699
+ border: 1px solid color-mix(in srgb, var(--color-border) 82%, transparent);
9700
+ background: color-mix(in srgb, var(--color-bg-alt) 82%, var(--color-bg));
9701
+ padding: 0.4rem 0.7rem;
9702
+ border-radius: 4px;
9703
+ color: color-mix(in srgb, var(--color-text) 82%, var(--color-text-muted));
9704
+ font-family: var(--font-mono);
9705
+ font-size: 0.78rem;
9706
+ font-weight: 600;
9707
+ line-height: 1.4;
9708
+ cursor: pointer;
9709
+ }
9710
+ .content .ox-api-controls__button:hover {
9711
+ color: var(--color-primary);
9712
+ border-color: color-mix(in srgb, var(--color-primary) 38%, var(--color-border));
9713
+ background: color-mix(in srgb, var(--color-bg-alt) 68%, var(--color-primary) 6%);
9714
+ }
9715
+ .content .ox-api-entry,
9716
+ .content .ox-api-module {
9717
+ margin: 0;
9718
+ border: 0;
9719
+ border-top: 1px solid color-mix(in srgb, var(--color-border) 74%, transparent);
9720
+ border-radius: 0;
9721
+ background: transparent;
9722
+ overflow: visible;
9723
+ }
9724
+ .content .ox-api-entry:last-child,
9725
+ .content .ox-api-module:last-child {
9726
+ border-bottom: 1px solid color-mix(in srgb, var(--color-border) 74%, transparent);
9727
+ }
9728
+ .content .ox-api-entry summary,
9729
+ .content .ox-api-module summary {
9730
+ list-style: none;
9731
+ cursor: pointer;
9732
+ padding: 1rem 0;
9733
+ position: relative;
9734
+ }
9735
+ .content .ox-api-entry summary::-webkit-details-marker,
9736
+ .content .ox-api-module summary::-webkit-details-marker { display: none; }
9737
+ .content .ox-api-entry summary {
9738
+ display: grid;
9739
+ grid-template-columns: var(--octc-api-kind-width, 6.5rem) minmax(0, 1fr) auto;
9740
+ align-items: start;
9741
+ gap: 0.95rem;
9742
+ }
9743
+ .content .ox-api-entry summary::after,
9744
+ .content .ox-api-module summary::after {
9745
+ content: "+";
9746
+ align-self: center;
9747
+ color: var(--color-text-muted);
9748
+ font-family: var(--font-mono);
9749
+ font-size: 0.95rem;
9750
+ font-weight: 600;
9751
+ line-height: 1;
9752
+ }
9753
+ .content .ox-api-entry[open] summary::after,
9754
+ .content .ox-api-module[open] summary::after {
9755
+ content: "−";
9756
+ color: var(--color-primary);
9757
+ }
9758
+ .content .ox-api-entry[open] summary,
9759
+ .content .ox-api-module[open] summary {
9760
+ border-bottom: 1px solid color-mix(in srgb, var(--color-border) 72%, transparent);
9761
+ }
9762
+ .content .ox-api-entry__kind,
9763
+ .content .ox-api-module__kind {
9764
+ display: block;
9765
+ width: var(--octc-api-kind-width, 6.5rem);
9766
+ padding: 0.3rem 0 0;
9767
+ background: transparent;
9768
+ border: 0;
9769
+ font-family: var(--font-mono);
9770
+ font-size: 0.76rem;
9771
+ font-weight: 600;
9772
+ letter-spacing: 0.01em;
9773
+ text-align: left;
9774
+ white-space: nowrap;
9775
+ color: var(--color-text-muted);
9776
+ }
9777
+ .content .ox-api-module__count {
9778
+ display: inline-flex;
9779
+ align-items: center;
9780
+ padding: 0.2rem 0.48rem;
9781
+ border-radius: 4px;
9782
+ background: color-mix(in srgb, var(--color-bg-alt) 84%, var(--color-primary) 8%);
9783
+ border: 1px solid color-mix(in srgb, var(--color-border) 82%, transparent);
9784
+ color: var(--color-text-muted);
9785
+ font-family: var(--font-mono);
9786
+ font-size: 0.72rem;
9787
+ font-weight: 600;
9788
+ letter-spacing: 0.03em;
9789
+ white-space: nowrap;
9790
+ }
9791
+ .content .ox-api-entry__name {
9792
+ display: block;
9793
+ font-family: var(--font-mono);
9794
+ font-size: 0.95rem;
9795
+ font-weight: 600;
9796
+ line-height: 1.55;
9797
+ }
9798
+ .content .ox-api-entry__signature,
9799
+ .content .ox-api-module__signature {
9800
+ display: block;
9801
+ width: 100%;
9802
+ min-width: 0;
9803
+ font-family: var(--font-mono);
9804
+ font-size: 0.95rem;
9805
+ line-height: 1.55;
9806
+ white-space: nowrap;
9807
+ overflow-x: auto;
9808
+ overflow-y: hidden;
9809
+ -webkit-overflow-scrolling: touch;
9810
+ }
9811
+ .content .ox-api-entry__description {
9812
+ display: block;
9813
+ color: color-mix(in srgb, var(--color-text) 78%, var(--color-text-muted));
9814
+ font-size: 0.9rem;
9815
+ line-height: 1.6;
9816
+ max-width: 72ch;
9817
+ }
9818
+ .content .ox-api-entry__summary-main {
9819
+ min-width: 0;
9820
+ display: flex;
9821
+ flex-direction: column;
9822
+ gap: 0.55rem;
9823
+ }
9824
+ .content .ox-api-entry__name,
9825
+ .content .ox-api-entry__signature,
9826
+ .content .ox-api-module__name,
9827
+ .content .ox-api-module__signature {
9828
+ background: transparent;
9829
+ padding: 0;
9830
+ border-radius: 0;
9831
+ word-break: normal;
9832
+ }
9833
+ .content code.shiki-inline.ox-api-entry__signature--highlighted,
9834
+ .content code.shiki-inline.ox-api-module__signature--highlighted {
9835
+ display: block;
9836
+ width: 100%;
9837
+ max-width: 100%;
9838
+ background: linear-gradient(
9839
+ 180deg,
9840
+ var(--color-code-bg-top) 0,
9841
+ var(--color-code-bg) 2.75rem
9842
+ ) !important;
9843
+ border: 1px solid var(--color-code-frame-border) !important;
9844
+ padding: 0.55rem 0.7rem !important;
9845
+ border-radius: 4px !important;
9846
+ white-space: nowrap;
9847
+ overflow-x: auto;
9848
+ overflow-y: hidden;
9849
+ -webkit-overflow-scrolling: touch;
9850
+ }
9851
+ .content code.shiki-inline.ox-api-entry__signature--highlighted .line,
9852
+ .content code.shiki-inline.ox-api-module__signature--highlighted .line {
9853
+ display: block;
9854
+ width: max-content;
9855
+ min-width: 100%;
9856
+ }
9857
+ .content .ox-api-entry__body,
9858
+ .content .ox-api-module__body { padding: 0.7rem 0 1.9rem; }
9859
+ .content .ox-api-entry__body {
9860
+ margin-left: calc(var(--octc-api-kind-width, 6.5rem) + 0.95rem);
9861
+ margin-top: 0.7rem;
9862
+ padding: 1.45rem 1rem 2.1rem 1.1rem;
9863
+ border: 1px solid color-mix(in srgb, var(--color-border) 72%, transparent);
9864
+ border-radius: 4px;
9865
+ background: color-mix(in srgb, var(--color-bg-alt) 68%, transparent);
9866
+ }
9867
+ .content .ox-api-entry__body > :first-child { margin-top: 0; }
9868
+ .content .ox-api-entry__body > :last-child { margin-bottom: 0; }
9869
+ .content .ox-api-entry[open] summary {
9870
+ padding-bottom: 0.35rem;
9871
+ }
9872
+ .content .ox-api-entry__section {
9873
+ display: grid;
9874
+ grid-template-columns: 6.5rem minmax(0, 1fr);
9875
+ gap: 0.2rem 1.25rem;
9876
+ align-items: start;
9877
+ margin-top: 1rem;
9878
+ padding-top: 1rem;
9879
+ border-top: 1px solid color-mix(in srgb, var(--color-border) 72%, transparent);
9880
+ }
9881
+ .content .ox-api-entry__section h4 {
9882
+ margin-top: 0;
9883
+ margin-bottom: 0;
9884
+ padding-top: 0.25rem;
9885
+ font-family: var(--font-mono);
9886
+ font-size: 0.78rem;
9887
+ font-weight: 700;
9888
+ letter-spacing: 0.04em;
9889
+ text-transform: uppercase;
9890
+ color: var(--color-text-muted);
9891
+ }
9892
+ .content .ox-api-entry__section > :not(h4) { min-width: 0; }
9893
+ .content .ox-api-entry__source {
9894
+ margin: 0 0 0.15rem;
9895
+ font-family: var(--font-mono);
9896
+ font-size: 0.78rem;
9897
+ color: var(--color-text-muted);
9898
+ }
9899
+ .content .ox-api-entry__source a {
9900
+ color: inherit;
9901
+ text-decoration-color: color-mix(in srgb, var(--color-text-muted) 38%, transparent);
9902
+ }
9903
+ .content .ox-api-entry__tags,
9904
+ .content .ox-api-module__list {
9905
+ list-style: none;
9906
+ padding-left: 0;
9907
+ margin: 0;
9908
+ }
9909
+ .content .ox-api-entry__tags {
9910
+ display: flex;
9911
+ flex-wrap: wrap;
9912
+ gap: 0.6rem;
9913
+ }
9914
+ .content .ox-api-entry__tags li {
9915
+ display: inline-flex;
9916
+ align-items: center;
9917
+ gap: 0.45rem;
9918
+ padding: 0.4rem 0.55rem;
9919
+ border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent);
9920
+ border-radius: 4px;
9921
+ background: color-mix(in srgb, var(--color-bg-alt) 72%, transparent);
9922
+ }
9923
+ .content .ox-api-module__list li {
9924
+ display: grid;
9925
+ grid-template-columns: var(--octc-api-kind-width, 6.5rem) minmax(0, 1fr);
9926
+ align-items: start;
9927
+ gap: 1rem;
9928
+ padding: 0.85rem 0;
9929
+ border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
9930
+ }
9931
+ .content .ox-api-module__list li:first-child { border-top: none; }
9932
+ .content .ox-api-entry__tag-name,
9933
+ .content .ox-api-module__title { font-weight: 700; }
9934
+ .content .ox-api-entry__tag-name {
9935
+ color: var(--color-primary);
9936
+ font-family: var(--font-mono);
9937
+ font-size: 0.74rem;
9938
+ }
9939
+ .content .ox-api-entry__tag-value {
9940
+ color: var(--color-text);
9941
+ font-size: 0.84rem;
9942
+ line-height: 1.45;
9943
+ }
9944
+ .content .ox-api-module summary {
9945
+ display: grid;
9946
+ grid-template-columns: minmax(0, 1fr) auto auto;
9947
+ align-items: center;
9948
+ gap: 0.9rem;
9949
+ }
9950
+ .content .ox-api-module__body { padding-top: 0.15rem; }
9951
+ .content .ox-api-module__item { min-width: 0; }
9952
+ .content .ox-api-module__link {
9953
+ display: block;
9954
+ text-decoration: none;
9955
+ }
9956
+ .content .ox-api-module__link:hover { text-decoration: none; }
9957
+ .content .ox-api-module__name,
9958
+ .content .ox-api-module__signature {
9959
+ display: block;
9960
+ font-family: var(--font-mono);
9961
+ font-size: 0.91rem;
9962
+ line-height: 1.55;
9963
+ color: var(--color-text);
9964
+ }
9965
+ .content .ox-api-module__summary {
9966
+ display: block;
9967
+ margin-top: 0.45rem;
9968
+ color: color-mix(in srgb, var(--color-text) 76%, var(--color-text-muted));
9969
+ font-size: 0.88rem;
9970
+ line-height: 1.55;
9971
+ }
9972
+ .content .ox-api-entry__section--examples pre {
9973
+ margin: 0;
9974
+ border: 1px solid var(--color-code-frame-border);
9975
+ border-radius: 4px;
9976
+ }
9977
+ .content .ox-api-entry__params {
9978
+ list-style: none;
9979
+ padding: 0;
9980
+ margin: 0;
9981
+ }
9982
+ .content .ox-api-entry__param {
9983
+ padding: 0.8rem 0;
9984
+ border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
9985
+ }
9986
+ .content .ox-api-entry__param:first-child {
9987
+ padding-top: 0;
9988
+ border-top: 0;
9989
+ }
9990
+ .content .ox-api-entry__param-heading {
9991
+ display: flex;
9992
+ flex-wrap: wrap;
9993
+ align-items: center;
9994
+ gap: 0.5rem;
9995
+ }
9996
+ .content .ox-api-entry__param-name,
9997
+ .content .ox-api-entry__return-type {
9998
+ font-family: var(--font-mono);
9999
+ font-size: 0.84rem;
10000
+ font-weight: 600;
10001
+ color: var(--color-text);
10002
+ background: color-mix(in srgb, var(--color-bg-alt) 84%, transparent);
10003
+ border: 1px solid color-mix(in srgb, var(--color-border) 82%, transparent);
10004
+ padding: 0.28rem 0.42rem;
10005
+ border-radius: 4px;
10006
+ }
10007
+ .content .ox-api-entry__param-type {
10008
+ font-family: var(--font-mono);
10009
+ font-size: 0.78rem;
10010
+ color: var(--color-code-text);
10011
+ background: color-mix(in srgb, var(--color-code-bg) 96%, #243556);
10012
+ border: 1px solid color-mix(in srgb, var(--color-code-bg) 82%, var(--color-border));
10013
+ padding: 0.26rem 0.42rem;
10014
+ border-radius: 4px;
10015
+ }
10016
+ .content .ox-api-entry__param-description,
10017
+ .content .ox-api-entry__return-description {
10018
+ margin: 0.55rem 0 0;
10019
+ font-size: 0.88rem;
10020
+ line-height: 1.6;
10021
+ color: color-mix(in srgb, var(--color-text) 78%, var(--color-text-muted));
10022
+ }
10023
+ .content .ox-api-entry__return { margin: 0; }
9478
10024
 
9479
10025
  /* Responsive */
9480
10026
  @media (max-width: 768px) {
9481
10027
  .menu-toggle { display: block; }
9482
10028
  .sidebar {
9483
10029
  transform: translateX(-100%);
9484
- transition: transform 0.3s ease;
9485
10030
  z-index: 99;
9486
10031
  width: 280px;
9487
10032
  }
10033
+ .sidebar--entry { display: block; }
9488
10034
  .sidebar.open { transform: translateX(0); }
9489
10035
  .main { margin-left: 0; padding: 1rem 0.75rem; }
9490
10036
  .content { padding: 0 0.25rem; }
@@ -9501,6 +10047,30 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9501
10047
  overflow-x: auto;
9502
10048
  -webkit-overflow-scrolling: touch;
9503
10049
  }
10050
+ .content .ox-api-controls {
10051
+ justify-content: flex-start;
10052
+ gap: 0.75rem;
10053
+ }
10054
+ .content .ox-api-entry__body {
10055
+ margin-left: 0;
10056
+ margin-top: 0.55rem;
10057
+ padding: 1.2rem 0 1.7rem;
10058
+ border-left: 0;
10059
+ border-right: 0;
10060
+ border-bottom: 0;
10061
+ border-radius: 0;
10062
+ background: transparent;
10063
+ }
10064
+ .content .ox-api-entry__section {
10065
+ grid-template-columns: 1fr;
10066
+ gap: 0.5rem;
10067
+ }
10068
+ .content .ox-api-entry__tags li,
10069
+ .content .ox-api-module__list li,
10070
+ .content .ox-api-module summary {
10071
+ grid-template-columns: 1fr;
10072
+ }
10073
+ .content .ox-api-entry__signature { width: 100%; }
9504
10074
  .content pre.ox-code-block .line {
9505
10075
  margin: 0 -0.75rem;
9506
10076
  padding: 0 0.75rem;
@@ -9508,13 +10078,17 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9508
10078
  .content code { font-size: 0.8125em; }
9509
10079
  .content table {
9510
10080
  display: block;
10081
+ width: max-content;
10082
+ min-width: 100%;
10083
+ max-width: calc(100vw - 1.5rem);
9511
10084
  overflow-x: auto;
9512
10085
  -webkit-overflow-scrolling: touch;
9513
10086
  font-size: 0.8125rem;
9514
- margin: 1rem -0.75rem;
9515
- width: calc(100% + 1.5rem);
10087
+ margin: 1rem 0;
10088
+ border-collapse: separate;
10089
+ border-spacing: 0;
9516
10090
  }
9517
- .content th, .content td { padding: 0.5rem 0.75rem; white-space: nowrap; }
10091
+ .content th, .content td { padding: 0.5rem 0.75rem; white-space: nowrap; vertical-align: top; }
9518
10092
  .content img { margin: 1rem 0; }
9519
10093
  .content img[alt*="Logo"] { max-width: 150px; }
9520
10094
  .content img[alt*="Architecture"] { max-width: 100%; }
@@ -9522,12 +10096,13 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9522
10096
  .content blockquote { padding: 0.5rem 0.75rem; margin: 1rem 0; font-size: 0.9375rem; }
9523
10097
  .header { padding: 0 1rem; }
9524
10098
  .header-title { font-size: 1rem; }
9525
- .header-title img { width: 24px; height: 24px; }
10099
+ .header-title:not(.header-title--logo-only) img { width: 24px; height: 24px; }
10100
+ .header-title--logo-only .header-logo { width: 152px; height: auto; }
9526
10101
  .overlay {
9527
10102
  display: none;
9528
10103
  position: fixed;
9529
10104
  inset: 0;
9530
- background: rgba(0,0,0,0.5);
10105
+ background: transparent;
9531
10106
  z-index: 98;
9532
10107
  }
9533
10108
  .overlay.open { display: block; }
@@ -9538,7 +10113,7 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9538
10113
  .main { padding: 0.75rem 0.5rem; }
9539
10114
  .content h1 { font-size: 1.35rem; }
9540
10115
  .content pre { font-size: 0.6875rem; padding: 0.625rem; }
9541
- .content table { font-size: 0.75rem; }
10116
+ .content table { max-width: calc(100vw - 1rem); font-size: 0.75rem; }
9542
10117
  .content th, .content td { padding: 0.375rem 0.5rem; }
9543
10118
  }
9544
10119
  </style>
@@ -9591,7 +10166,7 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9591
10166
  </div>
9592
10167
  <div class="overlay"></div>
9593
10168
  <div class="layout">
9594
- <aside class="sidebar">
10169
+ <aside class="sidebar{{#entryPage}} sidebar--entry{{/entryPage}}">
9595
10170
  <nav>
9596
10171
  {{navigation}}
9597
10172
  </nav>
@@ -9645,6 +10220,22 @@ const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>
9645
10220
  });
9646
10221
  }
9647
10222
 
10223
+ document.querySelectorAll('.ox-api-controls').forEach((controls) => {
10224
+ const targetSelector = controls.getAttribute('data-ox-api-target');
10225
+ if (!targetSelector) return;
10226
+
10227
+ controls.querySelectorAll('[data-ox-api-toggle]').forEach((button) => {
10228
+ button.addEventListener('click', () => {
10229
+ const shouldOpen = button.getAttribute('data-ox-api-toggle') === 'expand';
10230
+ document.querySelectorAll(targetSelector).forEach((entry) => {
10231
+ if (entry instanceof HTMLDetailsElement) {
10232
+ entry.open = shouldOpen;
10233
+ }
10234
+ });
10235
+ });
10236
+ });
10237
+ });
10238
+
9648
10239
  // Search functionality
9649
10240
  const searchButton = document.querySelector('.search-button');
9650
10241
  const searchOverlay = document.querySelector('.search-modal-overlay');
@@ -9977,8 +10568,14 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
9977
10568
  name: pageData.entryPage.hero.name,
9978
10569
  text: pageData.entryPage.hero.text,
9979
10570
  tagline: pageData.entryPage.hero.tagline,
10571
+ notice: pageData.entryPage.hero.notice ? {
10572
+ title: pageData.entryPage.hero.notice.title,
10573
+ body: pageData.entryPage.hero.notice.body
10574
+ } : void 0,
9980
10575
  image: pageData.entryPage.hero.image ? {
9981
10576
  src: pageData.entryPage.hero.image.src,
10577
+ lightSrc: pageData.entryPage.hero.image.lightSrc,
10578
+ darkSrc: pageData.entryPage.hero.image.darkSrc,
9982
10579
  alt: pageData.entryPage.hero.image.alt,
9983
10580
  width: pageData.entryPage.hero.image.width,
9984
10581
  height: pageData.entryPage.hero.image.height
@@ -11093,43 +11690,41 @@ function renderViewerHtml(pages, options) {
11093
11690
  <style>
11094
11691
  :root {
11095
11692
  --bg: #ffffff;
11096
- --bg-card: #f8f9fa;
11693
+ --bg-card: #f5f7fb;
11097
11694
  --bg-preview: #ffffff;
11098
- --text: #1a1a2e;
11099
- --text-muted: #6b7280;
11100
- --border: #e5e7eb;
11101
- --accent: #e8590c;
11102
- --accent-light: #fff4e6;
11695
+ --text: #131a30;
11696
+ --text-muted: #4f607b;
11697
+ --border: #d2dbea;
11698
+ --accent: #4f6fae;
11699
+ --accent-light: #eef2fa;
11103
11700
  --error: #dc2626;
11104
11701
  --error-bg: #fef2f2;
11105
11702
  --warning: #d97706;
11106
11703
  --warning-bg: #fffbeb;
11107
11704
  --success: #16a34a;
11108
- --tag-bg: #f0f0f0;
11109
- --shadow: 0 1px 3px rgba(0,0,0,0.08);
11110
- --radius: 8px;
11705
+ --tag-bg: #ecf3ff;
11706
+ --radius: 16px;
11111
11707
  }
11112
11708
  @media (prefers-color-scheme: dark) {
11113
11709
  :root {
11114
- --bg: #0f172a;
11115
- --bg-card: #1e293b;
11116
- --bg-preview: #334155;
11117
- --text: #e2e8f0;
11118
- --text-muted: #94a3b8;
11119
- --border: #334155;
11120
- --accent: #fb923c;
11121
- --accent-light: #431407;
11710
+ --bg: #060816;
11711
+ --bg-card: #0d1528;
11712
+ --bg-preview: #10172d;
11713
+ --text: #ebf2ff;
11714
+ --text-muted: #8ea0bf;
11715
+ --border: #223252;
11716
+ --accent: #86a4da;
11717
+ --accent-light: #151730;
11122
11718
  --error: #f87171;
11123
11719
  --error-bg: #450a0a;
11124
11720
  --warning: #fbbf24;
11125
11721
  --warning-bg: #451a03;
11126
11722
  --success: #4ade80;
11127
- --tag-bg: #334155;
11128
- --shadow: 0 1px 3px rgba(0,0,0,0.3);
11723
+ --tag-bg: #131b33;
11129
11724
  }
11130
11725
  }
11131
11726
  * { margin: 0; padding: 0; box-sizing: border-box; }
11132
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: var(--bg); color: var(--text); }
11727
+ body { font-family: 'IBM Plex Sans', 'Avenir Next', 'Segoe UI', system-ui, sans-serif; background: radial-gradient(circle at top left, rgba(79,111,174,0.10), transparent 24%), radial-gradient(circle at 85% 14%, rgba(145,237,233,0.08), transparent 22%), var(--bg); color: var(--text); }
11133
11728
  .header { padding: 16px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
11134
11729
  .header svg { width: 28px; height: 28px; color: var(--accent); }
11135
11730
  .header h1 { font-size: 18px; font-weight: 600; }
@@ -11150,7 +11745,7 @@ function renderViewerHtml(pages, options) {
11150
11745
  .search-input { padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); font-size: 13px; flex: 1; min-width: 200px; }
11151
11746
  .search-input::placeholder { color: var(--text-muted); }
11152
11747
  .container { padding: 24px; display: flex; flex-direction: column; gap: 20px; max-width: 1200px; margin: 0 auto; }
11153
- .card { border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-card); box-shadow: var(--shadow); overflow: hidden; }
11748
+ .card { border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-card); overflow: hidden; }
11154
11749
  .card-header { padding: 16px; border-bottom: 1px solid var(--border); }
11155
11750
  .card-path { font-size: 12px; color: var(--text-muted); font-family: monospace; margin-bottom: 4px; }
11156
11751
  .card-title { font-size: 16px; font-weight: 600; }
@@ -12045,13 +12640,16 @@ function DefaultTheme({ children }) {
12045
12640
  ${page.description ? `<meta name="description" content="${escapeHtml(page.description)}">` : ""}
12046
12641
  <style>
12047
12642
  :root {
12048
- --octc-color-primary: #e04d0a;
12049
- --octc-color-text: #1a1a1a;
12643
+ --octc-color-primary: #4f6fae;
12644
+ --octc-color-text: #131a30;
12050
12645
  --octc-color-bg: #ffffff;
12646
+ --octc-color-bg-alt: #f5f7fb;
12647
+ --octc-color-text-muted: #4f607b;
12648
+ --octc-color-border: #d2dbea;
12051
12649
  }
12052
12650
  body {
12053
- font-family: system-ui, sans-serif;
12054
- line-height: 1.6;
12651
+ font-family: "IBM Plex Sans", "Avenir Next", "Segoe UI Variable", "Segoe UI", sans-serif;
12652
+ line-height: 1.7;
12055
12653
  color: var(--octc-color-text);
12056
12654
  background: var(--octc-color-bg);
12057
12655
  max-width: 800px;