lildocs 0.1.16 → 0.1.18
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/cli.mjs +63 -21
- package/dist/render/styles.css +49 -6
- package/dist/render/table-viewer.ts +75 -12
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
12
12
|
import markedShiki from "marked-shiki";
|
|
13
13
|
import { bundledThemes, codeToHtml } from "shiki";
|
|
14
14
|
import { decodeHTML } from "entities";
|
|
15
|
-
import { renderMermaidSVG } from "beautiful-mermaid";
|
|
15
|
+
import { fromShikiTheme, renderMermaidSVG } from "beautiful-mermaid";
|
|
16
16
|
import os from "node:os";
|
|
17
17
|
import { generateMarkdownForModule } from "exports-md";
|
|
18
18
|
import { clampGamut, converter, formatHex, parse, wcagContrast } from "culori";
|
|
@@ -914,10 +914,15 @@ function escapeHtml$1(value) {
|
|
|
914
914
|
//#endregion
|
|
915
915
|
//#region src/core/mermaid.ts
|
|
916
916
|
async function createMermaidRenderer(options) {
|
|
917
|
+
const lightColors = await loadShikiColors(options.themeConfig.light);
|
|
918
|
+
const darkColors = options.themeConfig.dark ? await loadShikiColors(options.themeConfig.dark) : void 0;
|
|
919
|
+
const colorKeys = sharedColorKeys(lightColors, darkColors);
|
|
920
|
+
const renderOptions = toRenderOptions(colorKeys, options.themeConfig.fontFamily);
|
|
917
921
|
return {
|
|
922
|
+
css: toThemeCss(lightColors, darkColors, colorKeys),
|
|
918
923
|
async render(source, idHint) {
|
|
919
924
|
try {
|
|
920
|
-
const svg = addImageRole(renderMermaidSVG(source,
|
|
925
|
+
const svg = addImageRole(renderMermaidSVG(source, renderOptions));
|
|
921
926
|
return `<figure class="mermaidDiagram" id="${escapeHtml(idHint)}">${svg}</figure>`;
|
|
922
927
|
} catch (error) {
|
|
923
928
|
throw new LildocsError(`Failed to render Mermaid diagram ${idHint}: ${errorMessage$1(error)}`);
|
|
@@ -926,17 +931,51 @@ async function createMermaidRenderer(options) {
|
|
|
926
931
|
async close() {}
|
|
927
932
|
};
|
|
928
933
|
}
|
|
929
|
-
|
|
934
|
+
const optionalColorKeys = [
|
|
935
|
+
"line",
|
|
936
|
+
"accent",
|
|
937
|
+
"muted",
|
|
938
|
+
"surface",
|
|
939
|
+
"border"
|
|
940
|
+
];
|
|
941
|
+
async function loadShikiColors(themeName) {
|
|
942
|
+
const themeLoader = bundledThemes[themeName];
|
|
943
|
+
if (!themeLoader) throw new LildocsError(`Unknown bundled Shiki theme: ${themeName}`);
|
|
944
|
+
const themeModule = await themeLoader();
|
|
945
|
+
return fromShikiTheme("default" in themeModule ? themeModule.default : themeModule);
|
|
946
|
+
}
|
|
947
|
+
function sharedColorKeys(light, dark) {
|
|
948
|
+
return optionalColorKeys.filter((key) => light[key] !== void 0 && (!dark || dark[key] !== void 0));
|
|
949
|
+
}
|
|
950
|
+
function toRenderOptions(colorKeys, fontFamily) {
|
|
930
951
|
return {
|
|
931
|
-
bg:
|
|
932
|
-
fg:
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
surface: themeConfig.surface,
|
|
936
|
-
border: themeConfig.border,
|
|
937
|
-
font: themeConfig.fontFamily
|
|
952
|
+
bg: "var(--ld-mermaid-bg)",
|
|
953
|
+
fg: "var(--ld-mermaid-fg)",
|
|
954
|
+
...Object.fromEntries(colorKeys.map((key) => [key, `var(--ld-mermaid-${key})`])),
|
|
955
|
+
font: fontFamily
|
|
938
956
|
};
|
|
939
957
|
}
|
|
958
|
+
function toThemeCss(light, dark, colorKeys) {
|
|
959
|
+
const lightVariables = toCssVariables(light, colorKeys, " ");
|
|
960
|
+
const darkVariables = dark ? toCssVariables(dark, colorKeys, " ") : void 0;
|
|
961
|
+
return `.mermaidDiagram {
|
|
962
|
+
${lightVariables}
|
|
963
|
+
}${darkVariables ? `
|
|
964
|
+
|
|
965
|
+
@media (prefers-color-scheme: dark) {
|
|
966
|
+
.mermaidDiagram {
|
|
967
|
+
${darkVariables}
|
|
968
|
+
}
|
|
969
|
+
}` : ""}
|
|
970
|
+
`;
|
|
971
|
+
}
|
|
972
|
+
function toCssVariables(colors, colorKeys, indent) {
|
|
973
|
+
return [
|
|
974
|
+
`${indent}--ld-mermaid-bg: ${colors.bg};`,
|
|
975
|
+
`${indent}--ld-mermaid-fg: ${colors.fg};`,
|
|
976
|
+
...colorKeys.map((key) => `${indent}--ld-mermaid-${key}: ${colors[key]};`)
|
|
977
|
+
].join("\n");
|
|
978
|
+
}
|
|
940
979
|
function addImageRole(svg) {
|
|
941
980
|
return svg.replace("<svg", "<svg role=\"img\"");
|
|
942
981
|
}
|
|
@@ -1023,12 +1062,14 @@ async function buildReferencePages(options) {
|
|
|
1023
1062
|
outDir: tempDir
|
|
1024
1063
|
});
|
|
1025
1064
|
const markdownPaths = await collectMarkdownPaths(tempDir);
|
|
1065
|
+
const packageName = packageJsonData.name;
|
|
1066
|
+
if (typeof packageName !== "string" || !packageName) throw new Error(`Package name not found: ${packageJson.packagePath}`);
|
|
1026
1067
|
return Promise.all(markdownPaths.map(async (sourcePath) => {
|
|
1027
|
-
const
|
|
1068
|
+
const rawMarkdown = await readFile(sourcePath, "utf8");
|
|
1028
1069
|
return {
|
|
1029
1070
|
sourcePath,
|
|
1030
|
-
relativePath:
|
|
1031
|
-
rawMarkdown
|
|
1071
|
+
relativePath: referenceMarkdownPath(rawMarkdown, packageName),
|
|
1072
|
+
rawMarkdown
|
|
1032
1073
|
};
|
|
1033
1074
|
}));
|
|
1034
1075
|
} catch (error) {
|
|
@@ -1064,6 +1105,11 @@ async function readPackageJson(packagePath) {
|
|
|
1064
1105
|
throw error;
|
|
1065
1106
|
}
|
|
1066
1107
|
}
|
|
1108
|
+
function referenceMarkdownPath(rawMarkdown, packageName) {
|
|
1109
|
+
const packageSpecifier = rawMarkdown.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
1110
|
+
if (!packageSpecifier || packageSpecifier !== packageName && !packageSpecifier.startsWith(`${packageName}/`) || packageSpecifier.split("/").some((part) => !part || part === "." || part === "..")) throw new Error(`Invalid package entry heading: ${packageSpecifier ?? "missing"}`);
|
|
1111
|
+
return toPosixPath(path.posix.join("reference", `${packageSpecifier}.md`));
|
|
1112
|
+
}
|
|
1067
1113
|
function errorMessage(error) {
|
|
1068
1114
|
if (error instanceof Error) return error.message;
|
|
1069
1115
|
return String(error);
|
|
@@ -1336,12 +1382,8 @@ function resolveBackgroundOptions(options) {
|
|
|
1336
1382
|
function themeToMermaidConfig(theme, fontOverrides = {}) {
|
|
1337
1383
|
const fonts = resolveThemeFonts(theme.light, fontOverrides);
|
|
1338
1384
|
return {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
accent: theme.light.color.link,
|
|
1342
|
-
muted: theme.light.color.mutedText,
|
|
1343
|
-
surface: theme.light.color.codeBackground,
|
|
1344
|
-
border: theme.light.color.border,
|
|
1385
|
+
light: theme.light.shiki?.theme ?? "github-light",
|
|
1386
|
+
dark: theme.dark?.shiki?.theme,
|
|
1345
1387
|
fontFamily: fonts.body
|
|
1346
1388
|
};
|
|
1347
1389
|
}
|
|
@@ -1668,13 +1710,13 @@ async function buildSite(options) {
|
|
|
1668
1710
|
defaultText: packageName,
|
|
1669
1711
|
favicon: configOptions.favicon
|
|
1670
1712
|
});
|
|
1671
|
-
const
|
|
1713
|
+
const mermaid = await createMermaidRenderer({ themeConfig: themeToMermaidConfig(theme, fontResolution.themeFonts) });
|
|
1714
|
+
const css = `${resolveThemeFontImports(theme, configOptions.fonts)}${fontResolution.css}${themeToCssVariables(theme, fontResolution.themeFonts, configOptions.link, configOptions.navigation)}\n${mermaid.css}${backgroundResolution.css}${logoResolution.css}${baseCss}`;
|
|
1672
1715
|
const assets = [
|
|
1673
1716
|
...fontResolution.assets,
|
|
1674
1717
|
...backgroundResolution.assets,
|
|
1675
1718
|
...logoResolution.assets
|
|
1676
1719
|
];
|
|
1677
|
-
const mermaid = await createMermaidRenderer({ themeConfig: themeToMermaidConfig(theme, fontResolution.themeFonts) });
|
|
1678
1720
|
let frontendRenderer;
|
|
1679
1721
|
try {
|
|
1680
1722
|
await rm(outDir, {
|
package/dist/render/styles.css
CHANGED
|
@@ -271,6 +271,10 @@ html.is-animating .transition-scale {
|
|
|
271
271
|
margin-block: 1em;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
.content article > div > ul {
|
|
275
|
+
margin-bottom: 2em;
|
|
276
|
+
}
|
|
277
|
+
|
|
274
278
|
.content :first-child {
|
|
275
279
|
margin-top: 0;
|
|
276
280
|
}
|
|
@@ -524,17 +528,20 @@ html.is-animating .transition-scale {
|
|
|
524
528
|
border-collapse: collapse;
|
|
525
529
|
}
|
|
526
530
|
|
|
527
|
-
.tableFrame
|
|
531
|
+
.tableFrame,
|
|
532
|
+
.mermaidFrame {
|
|
528
533
|
margin-block: 1em;
|
|
529
534
|
}
|
|
530
535
|
|
|
531
|
-
.tableToolbar
|
|
536
|
+
.tableToolbar,
|
|
537
|
+
.mermaidToolbar {
|
|
532
538
|
display: flex;
|
|
533
539
|
justify-content: flex-end;
|
|
534
540
|
margin-top: 8px;
|
|
535
541
|
}
|
|
536
542
|
|
|
537
|
-
.tableViewport
|
|
543
|
+
.tableViewport,
|
|
544
|
+
.mermaidViewport {
|
|
538
545
|
max-width: 100%;
|
|
539
546
|
overflow-x: auto;
|
|
540
547
|
}
|
|
@@ -544,7 +551,12 @@ html.is-animating .transition-scale {
|
|
|
544
551
|
margin: 0;
|
|
545
552
|
}
|
|
546
553
|
|
|
554
|
+
.content .mermaidViewport .mermaidDiagram {
|
|
555
|
+
margin: 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
547
558
|
.tableExpandButton,
|
|
559
|
+
.mermaidExpandButton,
|
|
548
560
|
.tableFullscreenClose {
|
|
549
561
|
display: inline-flex;
|
|
550
562
|
align-items: center;
|
|
@@ -562,19 +574,23 @@ html.is-animating .transition-scale {
|
|
|
562
574
|
}
|
|
563
575
|
|
|
564
576
|
.tableExpandButton .ti,
|
|
577
|
+
.mermaidExpandButton .ti,
|
|
565
578
|
.tableFullscreenClose .ti {
|
|
566
579
|
font-size: 18px;
|
|
567
580
|
}
|
|
568
581
|
|
|
569
582
|
.tableExpandButton:hover,
|
|
570
583
|
.tableExpandButton:focus-visible,
|
|
584
|
+
.mermaidExpandButton:hover,
|
|
585
|
+
.mermaidExpandButton:focus-visible,
|
|
571
586
|
.tableFullscreenClose:hover,
|
|
572
587
|
.tableFullscreenClose:focus-visible {
|
|
573
588
|
border-color: var(--ld-color-link);
|
|
574
589
|
color: var(--ld-color-link);
|
|
575
590
|
}
|
|
576
591
|
|
|
577
|
-
.tableFullscreenDialog
|
|
592
|
+
.tableFullscreenDialog,
|
|
593
|
+
.mermaidFullscreenDialog {
|
|
578
594
|
width: 100vw;
|
|
579
595
|
max-width: none;
|
|
580
596
|
height: 100vh;
|
|
@@ -587,7 +603,8 @@ html.is-animating .transition-scale {
|
|
|
587
603
|
font-family: var(--ld-font-body);
|
|
588
604
|
}
|
|
589
605
|
|
|
590
|
-
.tableFullscreenDialog::backdrop
|
|
606
|
+
.tableFullscreenDialog::backdrop,
|
|
607
|
+
.mermaidFullscreenDialog::backdrop {
|
|
591
608
|
background: color-mix(in srgb, black 35%, transparent);
|
|
592
609
|
}
|
|
593
610
|
|
|
@@ -613,6 +630,31 @@ html.is-animating .transition-scale {
|
|
|
613
630
|
font-size: 105%;
|
|
614
631
|
}
|
|
615
632
|
|
|
633
|
+
.mermaidFullscreenViewport {
|
|
634
|
+
display: grid;
|
|
635
|
+
min-width: 0;
|
|
636
|
+
min-height: 0;
|
|
637
|
+
overflow: auto;
|
|
638
|
+
padding: 20px;
|
|
639
|
+
place-items: center;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.mermaidFullscreenViewport .mermaidDiagram {
|
|
643
|
+
display: flex;
|
|
644
|
+
width: 100%;
|
|
645
|
+
height: 100%;
|
|
646
|
+
margin: 0;
|
|
647
|
+
align-items: center;
|
|
648
|
+
justify-content: center;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.mermaidFullscreenViewport .mermaidDiagram svg {
|
|
652
|
+
width: 100%;
|
|
653
|
+
height: 100%;
|
|
654
|
+
max-width: 100%;
|
|
655
|
+
max-height: 100%;
|
|
656
|
+
}
|
|
657
|
+
|
|
616
658
|
.tableFullscreenViewport table {
|
|
617
659
|
display: table;
|
|
618
660
|
width: max-content;
|
|
@@ -621,7 +663,8 @@ html.is-animating .transition-scale {
|
|
|
621
663
|
border-collapse: collapse;
|
|
622
664
|
}
|
|
623
665
|
|
|
624
|
-
html.tableFullscreenOpen
|
|
666
|
+
html.tableFullscreenOpen,
|
|
667
|
+
html.mermaidFullscreenOpen {
|
|
625
668
|
overflow: hidden;
|
|
626
669
|
}
|
|
627
670
|
|
|
@@ -2,9 +2,11 @@ let activeDialog: HTMLDialogElement | undefined;
|
|
|
2
2
|
|
|
3
3
|
export function initTableViewer() {
|
|
4
4
|
enhanceTables();
|
|
5
|
+
enhanceMermaidDiagrams();
|
|
5
6
|
document.addEventListener("lildocs:page-view", () => {
|
|
6
7
|
activeDialog?.close();
|
|
7
8
|
enhanceTables();
|
|
9
|
+
enhanceMermaidDiagrams();
|
|
8
10
|
});
|
|
9
11
|
}
|
|
10
12
|
|
|
@@ -31,7 +33,16 @@ function enhanceTables() {
|
|
|
31
33
|
const viewport = document.createElement("div");
|
|
32
34
|
viewport.className = "tableViewport";
|
|
33
35
|
|
|
34
|
-
button.addEventListener("click", () =>
|
|
36
|
+
button.addEventListener("click", () =>
|
|
37
|
+
openFullscreenDialog(table, button, {
|
|
38
|
+
dialogClass: "tableFullscreenDialog",
|
|
39
|
+
dialogLabel: "Full screen table",
|
|
40
|
+
title: table.querySelector("caption")?.textContent?.trim() || "Table",
|
|
41
|
+
closeLabel: "Minimize table",
|
|
42
|
+
viewportClass: "tableFullscreenViewport content",
|
|
43
|
+
openClass: "tableFullscreenOpen",
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
35
46
|
table.before(frame);
|
|
36
47
|
toolbar.append(button);
|
|
37
48
|
viewport.append(table);
|
|
@@ -39,29 +50,81 @@ function enhanceTables() {
|
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
|
|
42
|
-
function
|
|
53
|
+
function enhanceMermaidDiagrams() {
|
|
54
|
+
const diagrams = document.querySelectorAll<HTMLElement>(
|
|
55
|
+
".content article .mermaidDiagram",
|
|
56
|
+
);
|
|
57
|
+
for (const diagram of Array.from(diagrams)) {
|
|
58
|
+
if (diagram.closest(".mermaidFrame")) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const frame = document.createElement("div");
|
|
63
|
+
frame.className = "mermaidFrame";
|
|
64
|
+
const toolbar = document.createElement("div");
|
|
65
|
+
toolbar.className = "mermaidToolbar";
|
|
66
|
+
const button = document.createElement("button");
|
|
67
|
+
button.type = "button";
|
|
68
|
+
button.className = "mermaidExpandButton";
|
|
69
|
+
button.title = "Expand diagram";
|
|
70
|
+
button.setAttribute("aria-label", "View diagram in full screen");
|
|
71
|
+
button.innerHTML =
|
|
72
|
+
'<span class="ti ti-arrows-maximize" aria-hidden="true"></span>';
|
|
73
|
+
const viewport = document.createElement("div");
|
|
74
|
+
viewport.className = "mermaidViewport";
|
|
75
|
+
|
|
76
|
+
button.addEventListener("click", () =>
|
|
77
|
+
openFullscreenDialog(diagram, button, {
|
|
78
|
+
dialogClass: "mermaidFullscreenDialog",
|
|
79
|
+
dialogLabel: "Full screen diagram",
|
|
80
|
+
title: "Diagram",
|
|
81
|
+
closeLabel: "Minimize diagram",
|
|
82
|
+
viewportClass: "mermaidFullscreenViewport content",
|
|
83
|
+
openClass: "mermaidFullscreenOpen",
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
diagram.before(frame);
|
|
87
|
+
toolbar.append(button);
|
|
88
|
+
viewport.append(diagram);
|
|
89
|
+
frame.append(viewport, toolbar);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type FullscreenDialogOptions = {
|
|
94
|
+
dialogClass: string;
|
|
95
|
+
dialogLabel: string;
|
|
96
|
+
title: string;
|
|
97
|
+
closeLabel: string;
|
|
98
|
+
viewportClass: string;
|
|
99
|
+
openClass: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function openFullscreenDialog(
|
|
103
|
+
source: HTMLElement,
|
|
104
|
+
opener: HTMLButtonElement,
|
|
105
|
+
options: FullscreenDialogOptions,
|
|
106
|
+
) {
|
|
43
107
|
activeDialog?.close();
|
|
44
108
|
|
|
45
109
|
const dialog = document.createElement("dialog");
|
|
46
|
-
dialog.className =
|
|
47
|
-
dialog.setAttribute("aria-label",
|
|
110
|
+
dialog.className = options.dialogClass;
|
|
111
|
+
dialog.setAttribute("aria-label", options.dialogLabel);
|
|
48
112
|
const content = document.createElement("div");
|
|
49
113
|
content.className = "tableFullscreenContent";
|
|
50
114
|
const header = document.createElement("header");
|
|
51
115
|
header.className = "tableFullscreenHeader";
|
|
52
116
|
const title = document.createElement("strong");
|
|
53
|
-
title.textContent =
|
|
54
|
-
table.querySelector("caption")?.textContent?.trim() || "Table";
|
|
117
|
+
title.textContent = options.title;
|
|
55
118
|
const closeButton = document.createElement("button");
|
|
56
119
|
closeButton.type = "button";
|
|
57
120
|
closeButton.className = "tableFullscreenClose";
|
|
58
|
-
closeButton.title =
|
|
59
|
-
closeButton.setAttribute("aria-label",
|
|
121
|
+
closeButton.title = options.closeLabel;
|
|
122
|
+
closeButton.setAttribute("aria-label", options.closeLabel);
|
|
60
123
|
closeButton.innerHTML =
|
|
61
124
|
'<span class="ti ti-arrows-minimize" aria-hidden="true"></span>';
|
|
62
125
|
const viewport = document.createElement("div");
|
|
63
|
-
viewport.className =
|
|
64
|
-
viewport.append(
|
|
126
|
+
viewport.className = options.viewportClass;
|
|
127
|
+
viewport.append(source.cloneNode(true));
|
|
65
128
|
|
|
66
129
|
closeButton.addEventListener("click", () => dialog.close());
|
|
67
130
|
dialog.addEventListener("cancel", (event) => {
|
|
@@ -76,7 +139,7 @@ function openTableDialog(table: HTMLTableElement, opener: HTMLButtonElement) {
|
|
|
76
139
|
dialog.addEventListener(
|
|
77
140
|
"close",
|
|
78
141
|
() => {
|
|
79
|
-
document.documentElement.classList.remove(
|
|
142
|
+
document.documentElement.classList.remove(options.openClass);
|
|
80
143
|
dialog.remove();
|
|
81
144
|
if (activeDialog === dialog) {
|
|
82
145
|
activeDialog = undefined;
|
|
@@ -92,7 +155,7 @@ function openTableDialog(table: HTMLTableElement, opener: HTMLButtonElement) {
|
|
|
92
155
|
content.append(header, viewport);
|
|
93
156
|
dialog.append(content);
|
|
94
157
|
document.body.append(dialog);
|
|
95
|
-
document.documentElement.classList.add(
|
|
158
|
+
document.documentElement.classList.add(options.openClass);
|
|
96
159
|
activeDialog = dialog;
|
|
97
160
|
dialog.showModal();
|
|
98
161
|
}
|
package/package.json
CHANGED