@commentray/render 0.0.2 → 0.0.5
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/block-stretch-layout.d.ts +30 -0
- package/dist/block-stretch-layout.d.ts.map +1 -0
- package/dist/block-stretch-layout.js +104 -0
- package/dist/block-stretch-layout.js.map +1 -0
- package/dist/build-commentray-nav-search.d.ts +62 -0
- package/dist/build-commentray-nav-search.d.ts.map +1 -0
- package/dist/build-commentray-nav-search.js +98 -0
- package/dist/build-commentray-nav-search.js.map +1 -0
- package/dist/build-stamp.d.ts +6 -0
- package/dist/build-stamp.d.ts.map +1 -0
- package/dist/build-stamp.js +23 -0
- package/dist/build-stamp.js.map +1 -0
- package/dist/code-browser-client.bundle.js +12 -5
- package/dist/code-browser-client.js +825 -97
- package/dist/code-browser-client.js.map +1 -1
- package/dist/code-browser-embedded-payload.d.ts +10 -0
- package/dist/code-browser-embedded-payload.d.ts.map +1 -0
- package/dist/code-browser-embedded-payload.js +18 -0
- package/dist/code-browser-embedded-payload.js.map +1 -0
- package/dist/code-browser-encoding.d.ts +9 -0
- package/dist/code-browser-encoding.d.ts.map +1 -0
- package/dist/code-browser-encoding.js +24 -0
- package/dist/code-browser-encoding.js.map +1 -0
- package/dist/code-browser-scroll-sync.d.ts +6 -0
- package/dist/code-browser-scroll-sync.d.ts.map +1 -1
- package/dist/code-browser-scroll-sync.js +1 -0
- package/dist/code-browser-scroll-sync.js.map +1 -1
- package/dist/code-browser-search.d.ts +5 -0
- package/dist/code-browser-search.d.ts.map +1 -1
- package/dist/code-browser-search.js +28 -0
- package/dist/code-browser-search.js.map +1 -1
- package/dist/code-browser-web-storage.d.ts +7 -0
- package/dist/code-browser-web-storage.d.ts.map +1 -0
- package/dist/code-browser-web-storage.js +21 -0
- package/dist/code-browser-web-storage.js.map +1 -0
- package/dist/code-browser.d.ts +76 -1
- package/dist/code-browser.d.ts.map +1 -1
- package/dist/code-browser.js +809 -111
- package/dist/code-browser.js.map +1 -1
- package/dist/highlighted-code-lines.d.ts +19 -0
- package/dist/highlighted-code-lines.d.ts.map +1 -0
- package/dist/highlighted-code-lines.js +61 -0
- package/dist/highlighted-code-lines.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/markdown-pipeline.d.ts.map +1 -1
- package/dist/markdown-pipeline.js +13 -1
- package/dist/markdown-pipeline.js.map +1 -1
- package/dist/mermaid-runtime-html.d.ts +7 -0
- package/dist/mermaid-runtime-html.d.ts.map +1 -0
- package/dist/mermaid-runtime-html.js +26 -0
- package/dist/mermaid-runtime-html.js.map +1 -0
- package/dist/side-by-side.d.ts +2 -0
- package/dist/side-by-side.d.ts.map +1 -1
- package/dist/side-by-side.js +7 -7
- package/dist/side-by-side.js.map +1 -1
- package/package.json +6 -4
package/dist/code-browser.js
CHANGED
|
@@ -1,57 +1,111 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { MARKER_ID_BODY, buildBlockScrollLinks, } from "@commentray/core";
|
|
5
|
+
import { tryBuildBlockStretchTableHtml } from "./block-stretch-layout.js";
|
|
6
|
+
import { formatCommentrayBuiltAtLocal } from "./build-stamp.js";
|
|
4
7
|
import { escapeHtml } from "./html-utils.js";
|
|
5
|
-
import {
|
|
8
|
+
import { renderHighlightedCodeLineRows } from "./highlighted-code-lines.js";
|
|
9
|
+
import { mermaidRuntimeScriptHtml } from "./mermaid-runtime-html.js";
|
|
10
|
+
import { renderMarkdownToHtml } from "./markdown-pipeline.js";
|
|
11
|
+
import { commentrayRenderVersion } from "./package-version.js";
|
|
6
12
|
function renderGeneratorMetaHtml(label) {
|
|
7
13
|
const t = label?.trim();
|
|
8
14
|
if (!t)
|
|
9
15
|
return "";
|
|
10
16
|
return `<meta name="generator" content="${escapeHtml(t)}" />\n `;
|
|
11
17
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
/** Single capture: marker id (avoid a wrapping group around the whole comment — that shifted indices). */
|
|
19
|
+
const BLOCK_MARKER_HTML_LINE = new RegExp(`^<!--\\s*commentray:block\\s+id=(${MARKER_ID_BODY})\\s*-->$`, "i");
|
|
20
|
+
function trimEndSpacesTabs(s) {
|
|
21
|
+
return s.replace(/[ \t]+$/, "");
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const inner = extractPreCodeInner(block);
|
|
26
|
-
const num = i + 1;
|
|
27
|
-
parts.push(`<div class="code-line" id="code-line-${i}" data-line="${i}">` +
|
|
28
|
-
`<span class="ln" aria-hidden="true">${num}</span>` +
|
|
29
|
-
`<pre><code class="hljs language-${langAttr}">${inner}</code></pre>` +
|
|
30
|
-
`</div>`);
|
|
31
|
-
}
|
|
32
|
-
return parts.join("\n");
|
|
23
|
+
function isSetextUnderlineLine(line) {
|
|
24
|
+
const t = trimEndSpacesTabs(line);
|
|
25
|
+
return /^\s{0,3}=+\s*$/.test(t) || /^\s{0,3}-+\s*$/.test(t);
|
|
26
|
+
}
|
|
27
|
+
function isThematicBreakLine(line) {
|
|
28
|
+
const t = trimEndSpacesTabs(line);
|
|
29
|
+
return (/^\s{0,3}(?:\*[ \t]*){3,}\s*$/.test(t) ||
|
|
30
|
+
/^\s{0,3}(?:-[ \t]*){3,}\s*$/.test(t) ||
|
|
31
|
+
/^\s{0,3}(?:_[ \t]*){3,}\s*$/.test(t));
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
function parseFenceDelimiter(line) {
|
|
34
|
+
const t = trimEndSpacesTabs(line);
|
|
35
|
+
const m = /^(\s{0,3})(`{3,}|~{3,})(.*)$/.exec(t);
|
|
36
|
+
if (!m)
|
|
37
|
+
return null;
|
|
38
|
+
const run = m[2];
|
|
39
|
+
const head = run[0];
|
|
40
|
+
if (head !== "`" && head !== "~")
|
|
41
|
+
return null;
|
|
42
|
+
const ch = head === "`" ? "`" : "~";
|
|
43
|
+
return { ch, runLen: run.length, rest: m[3] ?? "" };
|
|
44
|
+
}
|
|
45
|
+
function isClosingFenceLine(info, open) {
|
|
46
|
+
if (info.ch !== open.ch || info.runLen < open.len)
|
|
47
|
+
return false;
|
|
48
|
+
return info.rest.trim() === "";
|
|
49
|
+
}
|
|
50
|
+
function lineAnchorHtml(mdLine0) {
|
|
51
|
+
const mdLine = String(mdLine0);
|
|
52
|
+
return `<span class="commentray-line-anchor" data-commentray-md-line="${mdLine}" id="commentray-md-line-${mdLine}" aria-hidden="true"></span>`;
|
|
53
|
+
}
|
|
54
|
+
function appendMdLineAnchorWhenAllowed(line, mdLine0) {
|
|
55
|
+
if (isSetextUnderlineLine(line) || isThematicBreakLine(line))
|
|
56
|
+
return line;
|
|
57
|
+
/** Blank lines must stay blank: a line that is only `<span …>` breaks CommonMark HTML / paragraph starts after block markers. */
|
|
58
|
+
if (line === "")
|
|
59
|
+
return "";
|
|
60
|
+
return `${line}${lineAnchorHtml(mdLine0)}`;
|
|
41
61
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Inserts per-line anchors for search / hash jumps and block separator anchors after each
|
|
64
|
+
* `<!-- commentray:block … -->` line (optional index attrs).
|
|
65
|
+
*
|
|
66
|
+
* Anchors are appended to the line when safe. A **leading** `<span>` breaks CommonMark block
|
|
67
|
+
* recognition (`#` headings, lists, thematic breaks, fences). Fenced code lines must not get a
|
|
68
|
+
* trailing anchor either (would corrupt fence delimiters or appear inside code).
|
|
69
|
+
*/
|
|
70
|
+
function injectCommentrayDocAnchors(markdown, links) {
|
|
71
|
+
const byId = links ? new Map(links.map((l) => [l.id, l])) : undefined;
|
|
72
|
+
const lines = markdown.split("\n");
|
|
73
|
+
let fence = null;
|
|
74
|
+
const out = [];
|
|
75
|
+
for (let i = 0; i < lines.length; i++) {
|
|
76
|
+
const line = lines[i];
|
|
77
|
+
const delim = parseFenceDelimiter(line);
|
|
78
|
+
if (fence) {
|
|
79
|
+
if (delim && isClosingFenceLine(delim, fence)) {
|
|
80
|
+
fence = null;
|
|
81
|
+
out.push(line);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
out.push(line);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (delim) {
|
|
88
|
+
fence = { ch: delim.ch, len: delim.runLen };
|
|
89
|
+
out.push(line);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const m = BLOCK_MARKER_HTML_LINE.exec(line);
|
|
93
|
+
if (m?.[1]) {
|
|
94
|
+
const id = m[1];
|
|
95
|
+
const link = byId?.get(id);
|
|
96
|
+
const attrs = link !== undefined
|
|
97
|
+
? ` data-source-start="${String(link.sourceStart)}" data-commentray-line="${String(link.commentrayLine)}"`
|
|
98
|
+
: "";
|
|
99
|
+
/** One `push` with embedded `\n\n` merged poorly with `join("\\n")`; keep real blank lines around raw `<div>`. */
|
|
100
|
+
out.push(`${line}${lineAnchorHtml(i)}`);
|
|
101
|
+
out.push("");
|
|
102
|
+
out.push(`<div id="commentray-block-${escapeHtml(id)}" class="commentray-block-anchor" aria-hidden="true"${attrs}></div>`);
|
|
103
|
+
out.push("");
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
out.push(appendMdLineAnchorWhenAllowed(line, i));
|
|
46
107
|
}
|
|
47
|
-
|
|
48
|
-
const dirHtml = dir
|
|
49
|
-
? `<span class="file-path__dir">${escapeHtml(dir)}</span>`
|
|
50
|
-
: `<span class="file-path__dir file-path__dir--root" title="Repository root">/ </span>`;
|
|
51
|
-
return (`<strong class="file-path" title="${escapeHtml(shown)}">` +
|
|
52
|
-
dirHtml +
|
|
53
|
-
`<span class="file-path__base">${escapeHtml(base)}</span>` +
|
|
54
|
-
`</strong>`);
|
|
108
|
+
return out.join("\n");
|
|
55
109
|
}
|
|
56
110
|
/** GitHub “mark” glyph (Octicons-style path), MIT-licensed silhouette. */
|
|
57
111
|
const GITHUB_MARK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20" fill="currentColor" aria-hidden="true">' +
|
|
@@ -65,7 +119,7 @@ function safeExternalHttpUrl(url) {
|
|
|
65
119
|
return null;
|
|
66
120
|
return t;
|
|
67
121
|
}
|
|
68
|
-
function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl) {
|
|
122
|
+
function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl, commentrayRenderSemver) {
|
|
69
123
|
const gh = safeExternalHttpUrl(githubRepoUrl);
|
|
70
124
|
const tool = safeExternalHttpUrl(toolHomeUrl);
|
|
71
125
|
const bits = [];
|
|
@@ -75,12 +129,20 @@ function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl) {
|
|
|
75
129
|
}
|
|
76
130
|
if (tool) {
|
|
77
131
|
const te = escapeHtml(tool);
|
|
78
|
-
|
|
132
|
+
const ver = escapeHtml(commentrayRenderSemver);
|
|
133
|
+
bits.push(`<span class="toolbar-attribution" role="note">Rendered with <a href="${te}" target="_blank" rel="noopener noreferrer">Commentray</a> <span class="toolbar-attribution__version" translate="no">v${ver}</span></span>`);
|
|
79
134
|
}
|
|
80
135
|
if (bits.length === 0)
|
|
81
136
|
return "";
|
|
82
137
|
return `<div class="toolbar__end">${bits.join("")}</div>`;
|
|
83
138
|
}
|
|
139
|
+
function renderPageFooterHtml(builtAt) {
|
|
140
|
+
const iso = builtAt.toISOString();
|
|
141
|
+
const human = formatCommentrayBuiltAtLocal(builtAt);
|
|
142
|
+
return (`<footer class="app__footer" role="contentinfo">` +
|
|
143
|
+
`<p class="app__footer-line">HTML generated <time datetime="${escapeHtml(iso)}">${escapeHtml(human)}</time></p>` +
|
|
144
|
+
`</footer>`);
|
|
145
|
+
}
|
|
84
146
|
function renderRelatedGithubNavHtml(links) {
|
|
85
147
|
if (links.length === 0)
|
|
86
148
|
return "";
|
|
@@ -90,6 +152,52 @@ function renderRelatedGithubNavHtml(links) {
|
|
|
90
152
|
`<span class="toolbar-related__links">${parts.join('<span class="toolbar-related__sep" aria-hidden="true"> · </span>')}</span>` +
|
|
91
153
|
`</nav>`);
|
|
92
154
|
}
|
|
155
|
+
function renderToolbarDocHubHtml(opts) {
|
|
156
|
+
const nav = opts.documentedNavJsonUrl?.trim();
|
|
157
|
+
const hasEmbed = (opts.documentedPairsEmbeddedB64?.trim() ?? "").length > 0;
|
|
158
|
+
const showDocumentedTree = Boolean(nav) || hasEmbed;
|
|
159
|
+
const toolbarDocHubHtml = "";
|
|
160
|
+
const navAttr = escapeHtml(nav ?? "");
|
|
161
|
+
const navRailDocumentedHtml = showDocumentedTree
|
|
162
|
+
? `<details class="nav-rail__doc-hub" id="documented-files-hub" data-nav-json-url="${navAttr}">
|
|
163
|
+
<summary class="nav-rail__doc-hub-summary">Documented files</summary>
|
|
164
|
+
<div class="nav-rail__doc-hub-inner">
|
|
165
|
+
<div id="documented-files-tree" class="documented-files-tree" role="tree"></div>
|
|
166
|
+
</div>
|
|
167
|
+
</details>`
|
|
168
|
+
: "";
|
|
169
|
+
return { toolbarDocHubHtml, navRailDocumentedHtml };
|
|
170
|
+
}
|
|
171
|
+
function renderNavRailContextHtml(filePath, commentrayPath, opts) {
|
|
172
|
+
const fpRaw = (filePath ?? "").trim();
|
|
173
|
+
const crRaw = (commentrayPath ?? "").trim();
|
|
174
|
+
const srcUrl = safeExternalHttpUrl(opts?.sourceOnGithubUrl);
|
|
175
|
+
const crUrl = safeExternalHttpUrl(opts?.commentrayOnGithubUrl);
|
|
176
|
+
if (fpRaw.length === 0 && crRaw.length === 0 && srcUrl === null && crUrl === null) {
|
|
177
|
+
return "";
|
|
178
|
+
}
|
|
179
|
+
const fp = escapeHtml(fpRaw);
|
|
180
|
+
const cr = escapeHtml(crRaw);
|
|
181
|
+
const fpDisp = fpRaw.length > 0 ? fp : "—";
|
|
182
|
+
const crDisp = crRaw.length > 0 ? cr : "—";
|
|
183
|
+
const srcGh = srcUrl !== null
|
|
184
|
+
? `<a class="nav-rail__pair-gh" id="toolbar-source-github" href="${escapeHtml(srcUrl)}" target="_blank" rel="noopener noreferrer" aria-label="Source file on GitHub" title="Open source on GitHub">${GITHUB_MARK_SVG}</a>`
|
|
185
|
+
: "";
|
|
186
|
+
const crGh = crUrl !== null
|
|
187
|
+
? `<a class="nav-rail__pair-gh" id="toolbar-commentray-github" href="${escapeHtml(crUrl)}" target="_blank" rel="noopener noreferrer" aria-label="Companion commentray on GitHub" title="Open companion Markdown on GitHub">${GITHUB_MARK_SVG}</a>`
|
|
188
|
+
: "";
|
|
189
|
+
return `<div class="nav-rail__context nav-rail__context--compact" aria-label="Current documentation pair">
|
|
190
|
+
<span class="nav-rail__pair">
|
|
191
|
+
<span class="nav-rail__pair-lab">Src</span>
|
|
192
|
+
<span class="nav-rail__pair-path" title="${fp}">${fpDisp}</span>${srcGh}
|
|
193
|
+
</span>
|
|
194
|
+
<span class="nav-rail__pair-sep" aria-hidden="true">·</span>
|
|
195
|
+
<span class="nav-rail__pair">
|
|
196
|
+
<span class="nav-rail__pair-lab">Doc</span>
|
|
197
|
+
<span class="nav-rail__pair-path nav-rail__pair-path--secondary" title="${cr}">${crDisp}</span>${crGh}
|
|
198
|
+
</span>
|
|
199
|
+
</div>`;
|
|
200
|
+
}
|
|
93
201
|
/** IIFE produced by `npm run build -w @commentray/render` (esbuild of `code-browser-client.ts`). */
|
|
94
202
|
function loadCodeBrowserClientBundle() {
|
|
95
203
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -106,6 +214,220 @@ const CODE_BROWSER_STYLES = `
|
|
|
106
214
|
:root { color-scheme: light dark; }
|
|
107
215
|
* { box-sizing: border-box; }
|
|
108
216
|
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
|
|
217
|
+
.app {
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
align-items: stretch;
|
|
221
|
+
height: 100vh;
|
|
222
|
+
width: 100%;
|
|
223
|
+
overflow: hidden;
|
|
224
|
+
}
|
|
225
|
+
.app__chrome {
|
|
226
|
+
flex: 0 0 auto;
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
gap: 8px;
|
|
230
|
+
padding: 8px 12px 10px;
|
|
231
|
+
border-bottom: 1px solid color-mix(in oklab, CanvasText 15%, Canvas);
|
|
232
|
+
background: color-mix(in oklab, CanvasText 4%, Canvas);
|
|
233
|
+
max-height: min(40vh, 420px);
|
|
234
|
+
min-height: 0;
|
|
235
|
+
overflow: auto;
|
|
236
|
+
}
|
|
237
|
+
.chrome__search-row {
|
|
238
|
+
display: flex;
|
|
239
|
+
flex-direction: row;
|
|
240
|
+
align-items: center;
|
|
241
|
+
gap: 10px;
|
|
242
|
+
flex-wrap: nowrap;
|
|
243
|
+
}
|
|
244
|
+
.chrome__search-row input[type="search"] {
|
|
245
|
+
flex: 1 1 auto;
|
|
246
|
+
min-width: 140px;
|
|
247
|
+
padding: 8px 10px;
|
|
248
|
+
font: inherit;
|
|
249
|
+
font-size: 14px;
|
|
250
|
+
border-radius: 8px;
|
|
251
|
+
border: 1px solid color-mix(in oklab, CanvasText 25%, Canvas);
|
|
252
|
+
background: Canvas;
|
|
253
|
+
color: CanvasText;
|
|
254
|
+
}
|
|
255
|
+
.chrome__search-row #search-clear {
|
|
256
|
+
flex: 0 0 auto;
|
|
257
|
+
font: inherit;
|
|
258
|
+
padding: 6px 14px;
|
|
259
|
+
border-radius: 8px;
|
|
260
|
+
cursor: pointer;
|
|
261
|
+
border: 1px solid color-mix(in oklab, CanvasText 25%, Canvas);
|
|
262
|
+
background: color-mix(in oklab, CanvasText 6%, Canvas);
|
|
263
|
+
color: CanvasText;
|
|
264
|
+
}
|
|
265
|
+
.chrome__search-label {
|
|
266
|
+
flex: 0 0 auto;
|
|
267
|
+
white-space: nowrap;
|
|
268
|
+
}
|
|
269
|
+
.nav-rail__context--compact {
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-direction: row;
|
|
272
|
+
flex-wrap: wrap;
|
|
273
|
+
align-items: center;
|
|
274
|
+
gap: 6px 10px;
|
|
275
|
+
padding: 5px 10px;
|
|
276
|
+
border-radius: 8px;
|
|
277
|
+
border: 1px solid color-mix(in oklab, CanvasText 14%, Canvas);
|
|
278
|
+
background: Canvas;
|
|
279
|
+
font-size: 12px;
|
|
280
|
+
line-height: 1.3;
|
|
281
|
+
}
|
|
282
|
+
.nav-rail__pair {
|
|
283
|
+
display: inline-flex;
|
|
284
|
+
flex-direction: row;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 6px;
|
|
287
|
+
min-width: 0;
|
|
288
|
+
flex: 1 1 140px;
|
|
289
|
+
max-width: min(48%, 100%);
|
|
290
|
+
}
|
|
291
|
+
.nav-rail__pair-lab {
|
|
292
|
+
flex: 0 0 auto;
|
|
293
|
+
font-size: 9px;
|
|
294
|
+
font-weight: 700;
|
|
295
|
+
letter-spacing: 0.06em;
|
|
296
|
+
text-transform: uppercase;
|
|
297
|
+
opacity: 0.72;
|
|
298
|
+
}
|
|
299
|
+
.nav-rail__pair-path {
|
|
300
|
+
flex: 1 1 auto;
|
|
301
|
+
min-width: 0;
|
|
302
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
303
|
+
font-size: 11px;
|
|
304
|
+
color: CanvasText;
|
|
305
|
+
overflow: hidden;
|
|
306
|
+
text-overflow: ellipsis;
|
|
307
|
+
white-space: nowrap;
|
|
308
|
+
}
|
|
309
|
+
.nav-rail__pair-path--secondary { opacity: 0.88; }
|
|
310
|
+
.nav-rail__pair-sep {
|
|
311
|
+
flex: 0 0 auto;
|
|
312
|
+
opacity: 0.45;
|
|
313
|
+
user-select: none;
|
|
314
|
+
padding: 0 2px;
|
|
315
|
+
}
|
|
316
|
+
.nav-rail__pair-gh {
|
|
317
|
+
flex: 0 0 auto;
|
|
318
|
+
display: inline-flex;
|
|
319
|
+
align-items: center;
|
|
320
|
+
justify-content: center;
|
|
321
|
+
width: 26px;
|
|
322
|
+
height: 26px;
|
|
323
|
+
border-radius: 6px;
|
|
324
|
+
border: 1px solid color-mix(in oklab, CanvasText 20%, Canvas);
|
|
325
|
+
background: color-mix(in oklab, CanvasText 5%, Canvas);
|
|
326
|
+
color: CanvasText;
|
|
327
|
+
}
|
|
328
|
+
.nav-rail__pair-gh:hover {
|
|
329
|
+
background: color-mix(in oklab, CanvasText 10%, Canvas);
|
|
330
|
+
}
|
|
331
|
+
.nav-rail__pair-gh:focus-visible {
|
|
332
|
+
outline: 2px solid color-mix(in oklab, CanvasText 45%, Canvas);
|
|
333
|
+
outline-offset: 2px;
|
|
334
|
+
}
|
|
335
|
+
.nav-rail__pair-gh svg {
|
|
336
|
+
width: 14px;
|
|
337
|
+
height: 14px;
|
|
338
|
+
display: block;
|
|
339
|
+
}
|
|
340
|
+
.toolbar .nav-rail__context--compact {
|
|
341
|
+
border: 0;
|
|
342
|
+
background: transparent;
|
|
343
|
+
padding: 0;
|
|
344
|
+
flex: 1 1 200px;
|
|
345
|
+
min-width: 0;
|
|
346
|
+
max-width: none;
|
|
347
|
+
gap: 6px 10px;
|
|
348
|
+
}
|
|
349
|
+
.toolbar .nav-rail__pair {
|
|
350
|
+
flex: 1 1 auto;
|
|
351
|
+
min-width: 0;
|
|
352
|
+
max-width: min(44vw, 420px);
|
|
353
|
+
}
|
|
354
|
+
.nav-rail__search-label {
|
|
355
|
+
font-size: 11px;
|
|
356
|
+
font-weight: 700;
|
|
357
|
+
letter-spacing: 0.05em;
|
|
358
|
+
text-transform: uppercase;
|
|
359
|
+
opacity: 0.8;
|
|
360
|
+
}
|
|
361
|
+
.nav-rail__search-hint {
|
|
362
|
+
margin: 0;
|
|
363
|
+
font-size: 11px;
|
|
364
|
+
line-height: 1.35;
|
|
365
|
+
opacity: 0.78;
|
|
366
|
+
}
|
|
367
|
+
.nav-rail__code {
|
|
368
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
369
|
+
font-size: 10px;
|
|
370
|
+
}
|
|
371
|
+
.nav-rail__doc-hub {
|
|
372
|
+
position: relative;
|
|
373
|
+
flex: 0 0 auto;
|
|
374
|
+
align-self: center;
|
|
375
|
+
display: block;
|
|
376
|
+
border: 1px solid color-mix(in oklab, CanvasText 16%, Canvas);
|
|
377
|
+
border-radius: 6px;
|
|
378
|
+
background: Canvas;
|
|
379
|
+
overflow: visible;
|
|
380
|
+
}
|
|
381
|
+
.nav-rail__doc-hub-summary {
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
font-size: 12px;
|
|
384
|
+
font-weight: 600;
|
|
385
|
+
padding: 4px 10px;
|
|
386
|
+
list-style: none;
|
|
387
|
+
user-select: none;
|
|
388
|
+
line-height: 1.35;
|
|
389
|
+
}
|
|
390
|
+
.nav-rail__doc-hub-summary::-webkit-details-marker { display: none; }
|
|
391
|
+
.nav-rail__doc-hub-inner {
|
|
392
|
+
position: absolute;
|
|
393
|
+
left: 0;
|
|
394
|
+
top: calc(100% + 4px);
|
|
395
|
+
z-index: 60;
|
|
396
|
+
min-width: min(280px, 78vw);
|
|
397
|
+
max-width: min(440px, 94vw);
|
|
398
|
+
max-height: min(52vh, 400px);
|
|
399
|
+
overflow: auto;
|
|
400
|
+
padding: 8px 10px;
|
|
401
|
+
font-size: 12px;
|
|
402
|
+
border: 1px solid color-mix(in oklab, CanvasText 16%, Canvas);
|
|
403
|
+
border-radius: 8px;
|
|
404
|
+
background: Canvas;
|
|
405
|
+
box-shadow: 0 8px 28px color-mix(in oklab, CanvasText 12%, transparent);
|
|
406
|
+
}
|
|
407
|
+
.nav-rail__doc-hub-hint {
|
|
408
|
+
margin: 0 0 8px;
|
|
409
|
+
opacity: 0.78;
|
|
410
|
+
line-height: 1.4;
|
|
411
|
+
font-size: 12px;
|
|
412
|
+
}
|
|
413
|
+
.app__main {
|
|
414
|
+
flex: 1 1 auto;
|
|
415
|
+
min-width: 0;
|
|
416
|
+
min-height: 0;
|
|
417
|
+
display: flex;
|
|
418
|
+
flex-direction: column;
|
|
419
|
+
}
|
|
420
|
+
.app__footer {
|
|
421
|
+
flex: 0 0 auto;
|
|
422
|
+
padding: 6px 12px 10px;
|
|
423
|
+
border-top: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
424
|
+
background: color-mix(in oklab, CanvasText 3%, Canvas);
|
|
425
|
+
font-size: 11px;
|
|
426
|
+
line-height: 1.4;
|
|
427
|
+
color: color-mix(in oklab, CanvasText 72%, Canvas);
|
|
428
|
+
}
|
|
429
|
+
.app__footer-line { margin: 0; }
|
|
430
|
+
.app__footer time { font-variant-numeric: tabular-nums; }
|
|
109
431
|
.toolbar {
|
|
110
432
|
display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 8px 12px;
|
|
111
433
|
border-bottom: 1px solid color-mix(in oklab, CanvasText 18%, Canvas);
|
|
@@ -113,7 +435,7 @@ const CODE_BROWSER_STYLES = `
|
|
|
113
435
|
}
|
|
114
436
|
.toolbar__main {
|
|
115
437
|
display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
|
|
116
|
-
flex:
|
|
438
|
+
flex: 0 1 auto;
|
|
117
439
|
min-width: 0;
|
|
118
440
|
}
|
|
119
441
|
.toolbar__end {
|
|
@@ -162,13 +484,24 @@ const CODE_BROWSER_STYLES = `
|
|
|
162
484
|
word-break: break-word;
|
|
163
485
|
}
|
|
164
486
|
.toolbar-related__sep { opacity: 0.55; user-select: none; }
|
|
165
|
-
.
|
|
166
|
-
|
|
487
|
+
.documented-files-tree ul { list-style: none; margin: 0; padding-left: 12px; }
|
|
488
|
+
.documented-files-tree > ul { padding-left: 0; }
|
|
489
|
+
.documented-files-tree li { margin: 2px 0; line-height: 1.35; }
|
|
490
|
+
.documented-files-tree .tree-dir { font-weight: 600; margin-top: 4px; font-size: 12px; }
|
|
491
|
+
.documented-files-tree .tree-file {
|
|
492
|
+
margin: 3px 0;
|
|
493
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
494
|
+
font-size: 11px;
|
|
167
495
|
}
|
|
168
|
-
.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
496
|
+
.documented-files-tree .tree-file-link {
|
|
497
|
+
color: inherit;
|
|
498
|
+
font-weight: 500;
|
|
499
|
+
text-decoration: underline;
|
|
500
|
+
text-underline-offset: 2px;
|
|
501
|
+
word-break: break-word;
|
|
502
|
+
}
|
|
503
|
+
.documented-files-tree .tree-file-link:hover {
|
|
504
|
+
opacity: 0.92;
|
|
172
505
|
}
|
|
173
506
|
.toolbar button {
|
|
174
507
|
font: inherit; padding: 4px 10px; border-radius: 6px; cursor: pointer;
|
|
@@ -176,32 +509,73 @@ const CODE_BROWSER_STYLES = `
|
|
|
176
509
|
color: CanvasText;
|
|
177
510
|
}
|
|
178
511
|
.search-results {
|
|
179
|
-
flex: 0
|
|
180
|
-
|
|
181
|
-
|
|
512
|
+
flex: 0 1 auto;
|
|
513
|
+
min-height: 0;
|
|
514
|
+
max-height: min(320px, 38vh);
|
|
515
|
+
overflow: auto;
|
|
516
|
+
padding: 8px 8px 10px;
|
|
517
|
+
border-radius: 8px;
|
|
518
|
+
border: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
519
|
+
background: Canvas;
|
|
520
|
+
font-size: 13px;
|
|
182
521
|
}
|
|
183
522
|
.search-results[hidden] { display: none !important; }
|
|
184
|
-
.search-results .hint { opacity: 0.75; margin-bottom:
|
|
523
|
+
.search-results .hint { opacity: 0.75; margin-bottom: 8px; line-height: 1.45; }
|
|
185
524
|
.search-results button.hit {
|
|
186
|
-
display: block; width: 100%; text-align: left; margin:
|
|
525
|
+
display: block; width: 100%; text-align: left; margin: 4px 0; padding: 8px 10px;
|
|
187
526
|
border-radius: 6px; border: 1px solid color-mix(in oklab, CanvasText 14%, Canvas);
|
|
188
527
|
background: color-mix(in oklab, CanvasText 5%, Canvas); color: CanvasText; cursor: pointer;
|
|
189
528
|
font: inherit;
|
|
190
529
|
}
|
|
191
530
|
.search-results button.hit:hover { background: color-mix(in oklab, CanvasText 10%, Canvas); }
|
|
192
|
-
.search-results button.hit .meta { opacity: 0.8; font-size:
|
|
193
|
-
.search-results button.hit .src-tag { opacity: 0.75; font-weight: 500; font-size:
|
|
194
|
-
.search-results button.hit .snippet {
|
|
531
|
+
.search-results button.hit .meta { opacity: 0.8; font-size: 12px; }
|
|
532
|
+
.search-results button.hit .src-tag { opacity: 0.75; font-weight: 500; font-size: 11px; }
|
|
533
|
+
.search-results button.hit .snippet {
|
|
534
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 13px;
|
|
535
|
+
line-height: 1.45; white-space: pre-wrap; word-break: break-word; margin-top: 4px;
|
|
536
|
+
}
|
|
537
|
+
.search-results mark.search-hit {
|
|
538
|
+
padding: 0 2px; border-radius: 3px; font: inherit;
|
|
539
|
+
background: color-mix(in oklab, #f5a623 70%, Canvas);
|
|
540
|
+
color: CanvasText;
|
|
541
|
+
box-decoration-break: clone;
|
|
542
|
+
-webkit-box-decoration-break: clone;
|
|
543
|
+
}
|
|
544
|
+
@media (prefers-color-scheme: dark) {
|
|
545
|
+
.search-results mark.search-hit {
|
|
546
|
+
background: color-mix(in oklab, #c9a227 55%, Canvas);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
195
549
|
.shell { display: flex; flex-direction: row; flex: 1; min-height: 0; }
|
|
550
|
+
.app__main .shell { flex: 1 1 auto; }
|
|
196
551
|
.pane--code {
|
|
197
552
|
flex: 0 0 50%;
|
|
198
553
|
min-width: 120px; overflow: auto; padding: 12px 16px;
|
|
199
554
|
border-right: 1px solid color-mix(in oklab, CanvasText 15%, Canvas);
|
|
555
|
+
--code-line-font-size: 13px;
|
|
556
|
+
--code-line-height: 1.5;
|
|
200
557
|
}
|
|
558
|
+
.pane--code .code-line-stack { --code-ln-min-ch: 3; }
|
|
201
559
|
.pane--code .code-line {
|
|
202
|
-
display: grid;
|
|
560
|
+
display: grid;
|
|
561
|
+
grid-template-columns: max-content 1fr;
|
|
562
|
+
column-gap: 10px;
|
|
563
|
+
align-items: start;
|
|
564
|
+
}
|
|
565
|
+
.pane--code .code-line pre {
|
|
566
|
+
margin: 0;
|
|
567
|
+
min-width: 0;
|
|
568
|
+
padding: 0;
|
|
569
|
+
border: 0;
|
|
570
|
+
background: transparent;
|
|
571
|
+
}
|
|
572
|
+
.pane--code .code-line pre code.hljs {
|
|
573
|
+
display: block;
|
|
574
|
+
margin: 0;
|
|
575
|
+
padding: 0;
|
|
576
|
+
font-size: var(--code-line-font-size);
|
|
577
|
+
line-height: var(--code-line-height);
|
|
203
578
|
}
|
|
204
|
-
.pane--code .code-line pre { margin: 0; min-width: 0; }
|
|
205
579
|
.pane--code .code-line .ln {
|
|
206
580
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
207
581
|
font-variant-numeric: tabular-nums;
|
|
@@ -209,7 +583,11 @@ const CODE_BROWSER_STYLES = `
|
|
|
209
583
|
color: color-mix(in oklab, CanvasText 45%, Canvas);
|
|
210
584
|
padding-right: 8px;
|
|
211
585
|
border-right: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
212
|
-
|
|
586
|
+
white-space: nowrap;
|
|
587
|
+
font-size: var(--code-line-font-size);
|
|
588
|
+
line-height: var(--code-line-height);
|
|
589
|
+
min-width: calc(var(--code-ln-min-ch, 3) * 1ch + 0.6ch);
|
|
590
|
+
box-sizing: content-box;
|
|
213
591
|
}
|
|
214
592
|
.pane--code .code-line:target .ln,
|
|
215
593
|
.pane--code .code-line:hover .ln {
|
|
@@ -230,103 +608,423 @@ const CODE_BROWSER_STYLES = `
|
|
|
230
608
|
content: ""; position: absolute; top: 0; bottom: 0; left: -4px; right: -4px;
|
|
231
609
|
}
|
|
232
610
|
.pane--doc {
|
|
233
|
-
flex: 1 1 auto; min-width:
|
|
611
|
+
flex: 1 1 auto; min-width: 0; min-height: 0;
|
|
612
|
+
display: flex; flex-direction: column; overflow: hidden; padding: 12px 16px;
|
|
613
|
+
}
|
|
614
|
+
.doc-pane-body {
|
|
615
|
+
flex: 1 1 auto; min-height: 0; overflow: auto;
|
|
616
|
+
}
|
|
617
|
+
.toolbar-angle-picker {
|
|
618
|
+
display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
|
|
619
|
+
font-size: 12px; color: color-mix(in oklab, CanvasText 88%, Canvas);
|
|
620
|
+
}
|
|
621
|
+
.toolbar-angle-picker select {
|
|
622
|
+
font: inherit; font-size: 12px; padding: 3px 8px; border-radius: 6px;
|
|
623
|
+
border: 1px solid color-mix(in oklab, CanvasText 25%, Canvas); background: Canvas; color: CanvasText;
|
|
234
624
|
}
|
|
235
625
|
.pane--doc { font-size: 15px; line-height: 1.45; }
|
|
236
626
|
.pane--doc img { max-width: 100%; height: auto; }
|
|
627
|
+
.pane--doc .commentray-line-anchor {
|
|
628
|
+
display: inline;
|
|
629
|
+
vertical-align: baseline;
|
|
630
|
+
scroll-margin-top: 10px;
|
|
631
|
+
}
|
|
632
|
+
.pane--doc .commentray-block-anchor {
|
|
633
|
+
display: block;
|
|
634
|
+
height: 0;
|
|
635
|
+
margin: 14px 0 0;
|
|
636
|
+
border: 0;
|
|
637
|
+
border-top: 1px solid color-mix(in oklab, CanvasText 22%, Canvas);
|
|
638
|
+
pointer-events: none;
|
|
639
|
+
}
|
|
237
640
|
.pane h2.pane-title { margin: 0 0 10px; font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.75; }
|
|
238
|
-
.
|
|
641
|
+
.shell--stretch-rows {
|
|
642
|
+
flex: 1;
|
|
643
|
+
min-height: 0;
|
|
644
|
+
overflow: auto;
|
|
645
|
+
display: block;
|
|
646
|
+
padding: 0 12px 20px;
|
|
647
|
+
}
|
|
648
|
+
.shell--stretch-rows .stretch-preamble {
|
|
649
|
+
padding: 8px 4px 16px;
|
|
650
|
+
margin-bottom: 8px;
|
|
651
|
+
border-bottom: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
652
|
+
font-size: 15px;
|
|
653
|
+
line-height: 1.45;
|
|
654
|
+
}
|
|
655
|
+
.shell--stretch-rows .stretch-preamble img { max-width: 100%; height: auto; }
|
|
656
|
+
.block-stretch {
|
|
657
|
+
width: 100%;
|
|
658
|
+
border-collapse: collapse;
|
|
659
|
+
table-layout: fixed;
|
|
660
|
+
}
|
|
661
|
+
.stretch-col-code { width: 50%; }
|
|
662
|
+
.stretch-col-doc { width: 50%; }
|
|
663
|
+
.block-stretch td.stretch-code {
|
|
664
|
+
vertical-align: top;
|
|
665
|
+
padding: 0 12px 0 0;
|
|
666
|
+
border-bottom: 1px solid color-mix(in oklab, CanvasText 8%, Canvas);
|
|
667
|
+
}
|
|
668
|
+
.block-stretch td.stretch-doc {
|
|
669
|
+
vertical-align: top;
|
|
670
|
+
padding: 0 0 0 12px;
|
|
671
|
+
border-bottom: 1px solid color-mix(in oklab, CanvasText 8%, Canvas);
|
|
672
|
+
}
|
|
673
|
+
.block-stretch td.stretch-doc .stretch-doc-inner {
|
|
674
|
+
font-size: 15px;
|
|
675
|
+
line-height: 1.45;
|
|
676
|
+
}
|
|
677
|
+
.block-stretch td.stretch-doc .stretch-doc-inner img { max-width: 100%; height: auto; }
|
|
678
|
+
.block-stretch td.stretch-doc--gap {
|
|
679
|
+
color: color-mix(in oklab, CanvasText 38%, Canvas);
|
|
680
|
+
font-size: 13px;
|
|
681
|
+
vertical-align: top;
|
|
682
|
+
}
|
|
683
|
+
.block-stretch .stretch-gap-mark { display: inline-block; padding-top: 2px; }
|
|
684
|
+
.block-stretch .stretch-code-stack {
|
|
685
|
+
display: flex;
|
|
686
|
+
flex-direction: column;
|
|
687
|
+
align-items: stretch;
|
|
688
|
+
min-width: 0;
|
|
689
|
+
}
|
|
690
|
+
.block-stretch .code-line {
|
|
691
|
+
display: grid;
|
|
692
|
+
grid-template-columns: max-content 1fr;
|
|
693
|
+
column-gap: 12px;
|
|
694
|
+
align-items: start;
|
|
695
|
+
}
|
|
696
|
+
.block-stretch .code-line pre { margin: 0; min-width: 0; padding: 0; border: 0; background: transparent; }
|
|
697
|
+
.block-stretch .code-line pre code.hljs {
|
|
698
|
+
display: block;
|
|
699
|
+
margin: 0;
|
|
700
|
+
padding: 0;
|
|
701
|
+
font-size: var(--code-line-font-size, 13px);
|
|
702
|
+
line-height: var(--code-line-height, 1.5);
|
|
703
|
+
}
|
|
704
|
+
.block-stretch .code-line-stack { --code-ln-min-ch: 3; }
|
|
705
|
+
.block-stretch .code-line .ln {
|
|
706
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
707
|
+
font-variant-numeric: tabular-nums;
|
|
708
|
+
text-align: right;
|
|
709
|
+
user-select: none;
|
|
710
|
+
-webkit-user-select: none;
|
|
711
|
+
color: color-mix(in oklab, CanvasText 45%, Canvas);
|
|
712
|
+
padding-right: 8px;
|
|
713
|
+
border-right: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
714
|
+
white-space: nowrap;
|
|
715
|
+
font-size: var(--code-line-font-size, 13px);
|
|
716
|
+
line-height: var(--code-line-height, 1.5);
|
|
717
|
+
min-width: calc(var(--code-ln-min-ch, 3) * 1ch + 0.6ch);
|
|
718
|
+
box-sizing: content-box;
|
|
719
|
+
}
|
|
720
|
+
.block-stretch.wrap .code-line pre,
|
|
721
|
+
.block-stretch.wrap .code-line pre code { white-space: pre-wrap; word-break: break-word; }
|
|
722
|
+
.block-stretch:not(.wrap) .code-line pre,
|
|
723
|
+
.block-stretch:not(.wrap) .code-line pre code { white-space: pre; }
|
|
724
|
+
.block-stretch-headings {
|
|
725
|
+
display: grid;
|
|
726
|
+
grid-template-columns: 1fr 1fr;
|
|
727
|
+
gap: 0 16px;
|
|
728
|
+
padding: 4px 12px 8px;
|
|
729
|
+
border-bottom: 1px solid color-mix(in oklab, CanvasText 10%, Canvas);
|
|
730
|
+
}
|
|
731
|
+
.block-stretch-headings .pane-title { margin: 0; }
|
|
239
732
|
`;
|
|
733
|
+
/** Native tooltip on #search-q (short hint is visible under the search row). */
|
|
734
|
+
const CODE_BROWSER_SEARCH_INPUT_TITLE = "Filename, path, or words. Matches this pair (source + commentray lines) first; merges commentray-nav-search.json when the export includes it (indexed paths + commentray lines).";
|
|
240
735
|
function buildCodeBrowserPageHtml(p) {
|
|
241
|
-
const
|
|
736
|
+
const shellClass = p.layout === "stretch" ? "shell shell--stretch-rows" : "shell";
|
|
242
737
|
return `<!doctype html>
|
|
243
738
|
<html lang="en">
|
|
244
739
|
<head>
|
|
245
740
|
<meta charset="utf-8" />
|
|
246
741
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
247
|
-
${generatorMetaHtml}<title>${escapeHtml(title)}</title>
|
|
248
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/${escapeHtml(hljs)}.min.css" media="(prefers-color-scheme: light)" />
|
|
249
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/${escapeHtml(hljsDark)}.min.css" media="(prefers-color-scheme: dark)" />
|
|
742
|
+
${p.generatorMetaHtml}<title>${escapeHtml(p.title)}</title>
|
|
743
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/${escapeHtml(p.hljs)}.min.css" media="(prefers-color-scheme: light)" />
|
|
744
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/${escapeHtml(p.hljsDark)}.min.css" media="(prefers-color-scheme: dark)" />
|
|
250
745
|
<style>
|
|
251
746
|
${CODE_BROWSER_STYLES}
|
|
252
747
|
</style>
|
|
253
748
|
</head>
|
|
254
749
|
<body>
|
|
255
750
|
<div class="app">
|
|
256
|
-
<header class="
|
|
257
|
-
<div class="
|
|
258
|
-
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
<input type="search" id="search-q" placeholder="Whole source (ordered tokens + fuzzy lines)…" autocomplete="off" spellcheck="false" />
|
|
262
|
-
<button type="button" id="search-clear" title="Clear search">Clear</button>
|
|
263
|
-
</span>
|
|
264
|
-
${relatedNavHtml}
|
|
265
|
-
<label><input type="checkbox" id="wrap-lines" /> Wrap code lines</label>
|
|
751
|
+
<header class="app__chrome" role="region" aria-label="Search and navigation">
|
|
752
|
+
<div class="chrome__search-row">
|
|
753
|
+
<label class="chrome__search-label nav-rail__search-label" for="search-q">Search</label>
|
|
754
|
+
<input type="search" id="search-q" placeholder="${escapeHtml(p.searchPlaceholder)}" title="${escapeHtml(CODE_BROWSER_SEARCH_INPUT_TITLE)}" autocomplete="off" spellcheck="false" />
|
|
755
|
+
<button type="button" id="search-clear" title="Clear search">Clear</button>
|
|
266
756
|
</div>
|
|
267
|
-
|
|
757
|
+
<div class="search-results" id="search-results" hidden aria-live="polite"></div>
|
|
758
|
+
<p class="nav-rail__search-hint chrome__search-hint">This pair + merged <code class="nav-rail__code">commentray-nav-search.json</code> when the export ships it.</p>
|
|
268
759
|
</header>
|
|
269
|
-
<div class="
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
760
|
+
<div class="app__main">
|
|
761
|
+
<header class="toolbar" aria-label="View options">
|
|
762
|
+
<div class="toolbar__main">
|
|
763
|
+
${p.navRailContextHtml}
|
|
764
|
+
${p.navRailDocumentedHtml}
|
|
765
|
+
${p.angleSelectHtml}
|
|
766
|
+
${p.toolbarDocHubHtml}
|
|
767
|
+
${p.relatedNavHtml}
|
|
768
|
+
<label><input type="checkbox" id="wrap-lines" /> Wrap code lines</label>
|
|
769
|
+
</div>
|
|
770
|
+
${p.toolbarEndHtml}
|
|
771
|
+
</header>
|
|
772
|
+
<div class="${shellClass}" id="shell" data-layout="${p.layout}" data-raw-code-b64="${escapeHtml(p.rawCodeB64)}" data-raw-md-b64="${escapeHtml(p.rawMdB64)}" data-scroll-block-links-b64="${escapeHtml(p.scrollBlockLinksB64)}"${p.shellDocumentedPairsAttr}${p.shellSearchAttrs}>
|
|
773
|
+
${p.shellInner}
|
|
774
|
+
</div>
|
|
280
775
|
</div>
|
|
776
|
+
${p.pageFooterHtml}
|
|
281
777
|
</div>
|
|
778
|
+
<script type="text/plain" id="commentray-multi-angle-b64">${p.multiAngleScriptBlock}</script>
|
|
282
779
|
<script>
|
|
283
780
|
${loadCodeBrowserClientBundle()}
|
|
284
781
|
</script>
|
|
285
|
-
${mermaidScript}
|
|
782
|
+
${p.mermaidScript}
|
|
286
783
|
</body>
|
|
287
784
|
</html>`;
|
|
288
785
|
}
|
|
786
|
+
async function buildMultiAngleDualPaneShell(opts, multi) {
|
|
787
|
+
const defaultId = multi.angles.some((a) => a.id === multi.defaultAngleId)
|
|
788
|
+
? multi.defaultAngleId
|
|
789
|
+
: (multi.angles[0]?.id ?? "main");
|
|
790
|
+
const jsonAngles = [];
|
|
791
|
+
let defaultMarkdown = opts.commentrayMarkdown;
|
|
792
|
+
let defaultScrollB64 = "";
|
|
793
|
+
let defaultPathSearch = (opts.commentrayPathForSearch ?? "").trim();
|
|
794
|
+
let defaultGh = opts.commentrayOnGithubUrl;
|
|
795
|
+
let defaultPaneHtml = "";
|
|
796
|
+
const codeHtml = await renderHighlightedCodeLineRows(opts.code, opts.language);
|
|
797
|
+
for (const spec of multi.angles) {
|
|
798
|
+
const rows = spec.blockStretchRows;
|
|
799
|
+
const links = rows !== undefined
|
|
800
|
+
? buildBlockScrollLinks(rows.index, rows.sourceRelative, rows.commentrayPathRel, spec.markdown, opts.code)
|
|
801
|
+
: [];
|
|
802
|
+
const mdForDoc = injectCommentrayDocAnchors(spec.markdown, links.length > 0 ? links : undefined);
|
|
803
|
+
const scrollB64 = links.length > 0 ? Buffer.from(JSON.stringify(links), "utf8").toString("base64") : "";
|
|
804
|
+
const commentrayHtml = await renderMarkdownToHtml(mdForDoc, {
|
|
805
|
+
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
806
|
+
});
|
|
807
|
+
if (spec.id === defaultId) {
|
|
808
|
+
defaultMarkdown = spec.markdown;
|
|
809
|
+
defaultScrollB64 = scrollB64;
|
|
810
|
+
defaultPathSearch = spec.commentrayPathRel.trim();
|
|
811
|
+
defaultGh = spec.commentrayOnGithubUrl;
|
|
812
|
+
defaultPaneHtml = commentrayHtml;
|
|
813
|
+
}
|
|
814
|
+
jsonAngles.push({
|
|
815
|
+
id: spec.id,
|
|
816
|
+
title: spec.title?.trim() || spec.id,
|
|
817
|
+
docInnerHtmlB64: Buffer.from(commentrayHtml, "utf8").toString("base64"),
|
|
818
|
+
rawMdB64: Buffer.from(spec.markdown, "utf8").toString("base64"),
|
|
819
|
+
scrollBlockLinksB64: scrollB64,
|
|
820
|
+
commentrayPathForSearch: spec.commentrayPathRel.trim(),
|
|
821
|
+
commentrayOnGithubUrl: spec.commentrayOnGithubUrl,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
const selOpts = multi.angles
|
|
825
|
+
.map((a) => {
|
|
826
|
+
const lab = escapeHtml(a.title?.trim() || a.id);
|
|
827
|
+
return `<option value="${escapeHtml(a.id)}"${a.id === defaultId ? " selected" : ""}>${lab}</option>`;
|
|
828
|
+
})
|
|
829
|
+
.join("");
|
|
830
|
+
const angleSelectHtml = `<span class="toolbar-angle-picker"><label for="angle-select">Angle</label><select id="angle-select" aria-label="Commentray angle">${selOpts}</select></span>`;
|
|
831
|
+
const shellInner = ` <section class="pane--code" id="code-pane" aria-label="Source code">` +
|
|
832
|
+
`<h2 class="pane-title">Code</h2>\n` +
|
|
833
|
+
` ${codeHtml}\n` +
|
|
834
|
+
` </section>\n` +
|
|
835
|
+
` <div class="gutter" id="gutter" role="separator" aria-orientation="vertical" aria-label="Resize panes"></div>\n` +
|
|
836
|
+
` <section class="pane--doc commentray" id="doc-pane" aria-label="Commentray">\n` +
|
|
837
|
+
` <h2 class="pane-title">Commentray</h2>\n` +
|
|
838
|
+
` <div id="doc-pane-body" class="doc-pane-body">\n` +
|
|
839
|
+
` ${defaultPaneHtml}\n` +
|
|
840
|
+
` </div>\n` +
|
|
841
|
+
` </section>\n`;
|
|
842
|
+
const payloadObj = { defaultAngleId: defaultId, angles: jsonAngles };
|
|
843
|
+
const multiAnglePayloadB64 = Buffer.from(JSON.stringify(payloadObj), "utf8").toString("base64");
|
|
844
|
+
return {
|
|
845
|
+
shellInner,
|
|
846
|
+
multiShell: {
|
|
847
|
+
rawMdB64: Buffer.from(defaultMarkdown, "utf8").toString("base64"),
|
|
848
|
+
scrollBlockLinksB64: defaultScrollB64,
|
|
849
|
+
commentrayPathForSearch: defaultPathSearch,
|
|
850
|
+
commentrayOnGithubUrl: defaultGh,
|
|
851
|
+
},
|
|
852
|
+
angleSelectHtml,
|
|
853
|
+
multiAnglePayloadB64,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
async function buildCodeBrowserShell(opts, layoutPref) {
|
|
857
|
+
let layout = "dual";
|
|
858
|
+
let shellInner = "";
|
|
859
|
+
let scrollBlockLinksB64 = "";
|
|
860
|
+
const multi = opts.multiAngleBrowsing;
|
|
861
|
+
const multiActive = Boolean(multi && multi.angles.length >= 2);
|
|
862
|
+
if (multiActive && multi) {
|
|
863
|
+
const built = await buildMultiAngleDualPaneShell(opts, multi);
|
|
864
|
+
const ms = built.multiShell;
|
|
865
|
+
return {
|
|
866
|
+
layout: "dual",
|
|
867
|
+
shellInner: built.shellInner,
|
|
868
|
+
scrollBlockLinksB64: ms.scrollBlockLinksB64,
|
|
869
|
+
angleSelectHtml: built.angleSelectHtml,
|
|
870
|
+
multiAnglePayloadB64: built.multiAnglePayloadB64,
|
|
871
|
+
multiShell: ms,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
if (opts.blockStretchRows && layoutPref !== "dual") {
|
|
875
|
+
const stretched = await tryBuildBlockStretchTableHtml({
|
|
876
|
+
code: opts.code,
|
|
877
|
+
language: opts.language,
|
|
878
|
+
commentrayMarkdown: opts.commentrayMarkdown,
|
|
879
|
+
index: opts.blockStretchRows.index,
|
|
880
|
+
sourceRelative: opts.blockStretchRows.sourceRelative,
|
|
881
|
+
commentrayPathRel: opts.blockStretchRows.commentrayPathRel,
|
|
882
|
+
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
883
|
+
});
|
|
884
|
+
if (stretched) {
|
|
885
|
+
layout = "stretch";
|
|
886
|
+
shellInner =
|
|
887
|
+
` <div class="block-stretch-headings">` +
|
|
888
|
+
`<h2 class="pane-title">Code</h2>` +
|
|
889
|
+
`<h2 class="pane-title">Commentray</h2>` +
|
|
890
|
+
`</div>\n` +
|
|
891
|
+
` ${stretched.preambleHtml}\n` +
|
|
892
|
+
` ${stretched.tableInnerHtml}\n`;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (layout === "dual") {
|
|
896
|
+
const links = opts.blockStretchRows !== undefined
|
|
897
|
+
? buildBlockScrollLinks(opts.blockStretchRows.index, opts.blockStretchRows.sourceRelative, opts.blockStretchRows.commentrayPathRel, opts.commentrayMarkdown, opts.code)
|
|
898
|
+
: [];
|
|
899
|
+
const mdForDoc = injectCommentrayDocAnchors(opts.commentrayMarkdown, links.length > 0 ? links : undefined);
|
|
900
|
+
if (links.length > 0) {
|
|
901
|
+
scrollBlockLinksB64 = Buffer.from(JSON.stringify(links), "utf8").toString("base64");
|
|
902
|
+
}
|
|
903
|
+
const [codeHtml, commentrayHtml] = await Promise.all([
|
|
904
|
+
renderHighlightedCodeLineRows(opts.code, opts.language),
|
|
905
|
+
renderMarkdownToHtml(mdForDoc, {
|
|
906
|
+
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
907
|
+
}),
|
|
908
|
+
]);
|
|
909
|
+
shellInner =
|
|
910
|
+
` <section class="pane--code" id="code-pane" aria-label="Source code">` +
|
|
911
|
+
`<h2 class="pane-title">Code</h2>\n` +
|
|
912
|
+
` ${codeHtml}\n` +
|
|
913
|
+
` </section>\n` +
|
|
914
|
+
` <div class="gutter" id="gutter" role="separator" aria-orientation="vertical" aria-label="Resize panes"></div>\n` +
|
|
915
|
+
` <section class="pane--doc commentray" id="doc-pane" aria-label="Commentray">\n` +
|
|
916
|
+
` <h2 class="pane-title">Commentray</h2>\n` +
|
|
917
|
+
` <div id="doc-pane-body" class="doc-pane-body">\n` +
|
|
918
|
+
` ${commentrayHtml}\n` +
|
|
919
|
+
` </div>\n` +
|
|
920
|
+
` </section>\n`;
|
|
921
|
+
}
|
|
922
|
+
return {
|
|
923
|
+
layout,
|
|
924
|
+
shellInner,
|
|
925
|
+
scrollBlockLinksB64,
|
|
926
|
+
angleSelectHtml: "",
|
|
927
|
+
multiAnglePayloadB64: "",
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
function searchChromeFromOptions(opts, commentrayPathOverride) {
|
|
931
|
+
const crPath = (commentrayPathOverride ?? opts.commentrayPathForSearch ?? "").trim();
|
|
932
|
+
if (opts.staticSearchScope === "commentray-and-paths") {
|
|
933
|
+
return {
|
|
934
|
+
searchPlaceholder: "Filename, path, or keywords…",
|
|
935
|
+
shellSearchAttrs: ` data-search-scope="commentray-and-paths" data-search-file-path="${escapeHtml(opts.filePath ?? "")}" data-search-commentray-path="${escapeHtml(crPath)}"`,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
return {
|
|
939
|
+
searchPlaceholder: "Filename, path, or keywords…",
|
|
940
|
+
shellSearchAttrs: "",
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function shellDocumentedPairsAttrFromOptions(opts) {
|
|
944
|
+
const emb = opts.documentedPairsEmbeddedB64?.trim() ?? "";
|
|
945
|
+
if (emb.length === 0)
|
|
946
|
+
return "";
|
|
947
|
+
return ` data-documented-pairs-b64="${escapeHtml(emb)}"`;
|
|
948
|
+
}
|
|
949
|
+
function codeBrowserPageTitle(opts) {
|
|
950
|
+
return opts.title ?? opts.filePath ?? "Commentray";
|
|
951
|
+
}
|
|
952
|
+
function codeBrowserHljsThemes(opts) {
|
|
953
|
+
const hljs = opts.hljsTheme ?? "github";
|
|
954
|
+
const hljsDark = opts.hljsTheme?.includes("dark") ? opts.hljsTheme : "github-dark";
|
|
955
|
+
return { hljs, hljsDark };
|
|
956
|
+
}
|
|
957
|
+
function toolbarCommentrayGithubFromShell(shell, opts) {
|
|
958
|
+
return shell.multiShell?.commentrayOnGithubUrl ?? opts.commentrayOnGithubUrl;
|
|
959
|
+
}
|
|
960
|
+
function rawMdB64FromShell(shell, opts) {
|
|
961
|
+
return (shell.multiShell?.rawMdB64 ?? Buffer.from(opts.commentrayMarkdown, "utf8").toString("base64"));
|
|
962
|
+
}
|
|
963
|
+
function navRailCommentrayPathFromShell(shell, opts) {
|
|
964
|
+
const trimmed = (shell.multiShell?.commentrayPathForSearch ??
|
|
965
|
+
opts.commentrayPathForSearch ??
|
|
966
|
+
"").trim();
|
|
967
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
968
|
+
}
|
|
969
|
+
function shellSearchAttrsWithNavJson(shellSearchAttrsBase, documentedNavJsonUrl) {
|
|
970
|
+
const navJson = documentedNavJsonUrl?.trim() ?? "";
|
|
971
|
+
if (navJson.length === 0)
|
|
972
|
+
return shellSearchAttrsBase;
|
|
973
|
+
return `${shellSearchAttrsBase} data-nav-search-json-url="${escapeHtml(navJson)}"`;
|
|
974
|
+
}
|
|
289
975
|
/**
|
|
290
976
|
* Static HTML shell for a minimal “code browser”: code + rendered commentray,
|
|
291
977
|
* draggable vertical splitter, togglable line wrap for the code pane, and
|
|
292
978
|
* token-in-line quick search (all non-whitespace tokens must appear on the same line).
|
|
293
979
|
*/
|
|
294
980
|
export async function renderCodeBrowserHtml(opts) {
|
|
295
|
-
const [codeHtml, commentrayHtml] = await Promise.all([
|
|
296
|
-
renderCodeLineBlocks(opts.code, opts.language),
|
|
297
|
-
renderMarkdownToHtml(opts.commentrayMarkdown, {
|
|
298
|
-
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
299
|
-
}),
|
|
300
|
-
]);
|
|
301
981
|
const rawCodeB64 = Buffer.from(opts.code, "utf8").toString("base64");
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
const toolbarEndHtml = buildToolbarEndHtml(opts.githubRepoUrl, opts.toolHomeUrl);
|
|
306
|
-
const
|
|
307
|
-
const hljsDark = opts
|
|
308
|
-
const mermaidScript = opts.includeMermaidRuntime
|
|
309
|
-
? `<script type="module">
|
|
310
|
-
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
311
|
-
mermaid.initialize({ startOnLoad: true, securityLevel: "strict" });
|
|
312
|
-
mermaid.run({ querySelector: ".mermaid" });
|
|
313
|
-
</script>`
|
|
314
|
-
: "";
|
|
982
|
+
const title = codeBrowserPageTitle(opts);
|
|
983
|
+
const builtAt = opts.builtAt ?? new Date();
|
|
984
|
+
const renderSemver = commentrayRenderVersion();
|
|
985
|
+
const toolbarEndHtml = buildToolbarEndHtml(opts.githubRepoUrl, opts.toolHomeUrl, renderSemver);
|
|
986
|
+
const pageFooterHtml = renderPageFooterHtml(builtAt);
|
|
987
|
+
const { hljs, hljsDark } = codeBrowserHljsThemes(opts);
|
|
988
|
+
const mermaidScript = mermaidRuntimeScriptHtml(opts.includeMermaidRuntime);
|
|
315
989
|
const relatedNavHtml = renderRelatedGithubNavHtml(opts.relatedGithubNav ?? []);
|
|
316
990
|
const generatorMetaHtml = renderGeneratorMetaHtml(opts.generatorLabel);
|
|
991
|
+
const layoutPref = opts.codeBrowserLayout ?? "auto";
|
|
992
|
+
const shell = await buildCodeBrowserShell(opts, layoutPref);
|
|
993
|
+
const { toolbarDocHubHtml, navRailDocumentedHtml } = renderToolbarDocHubHtml({
|
|
994
|
+
documentedNavJsonUrl: opts.documentedNavJsonUrl,
|
|
995
|
+
documentedPairsEmbeddedB64: opts.documentedPairsEmbeddedB64,
|
|
996
|
+
});
|
|
997
|
+
const rawMdB64 = rawMdB64FromShell(shell, opts);
|
|
998
|
+
const scrollBlockLinksB64 = shell.scrollBlockLinksB64;
|
|
999
|
+
const { searchPlaceholder, shellSearchAttrs: shellSearchAttrsBase } = searchChromeFromOptions(opts, shell.multiShell?.commentrayPathForSearch);
|
|
1000
|
+
const shellDocumentedPairsAttr = shellDocumentedPairsAttrFromOptions(opts);
|
|
1001
|
+
const shellSearchAttrs = shellSearchAttrsWithNavJson(shellSearchAttrsBase, opts.documentedNavJsonUrl);
|
|
1002
|
+
const navRailContextHtml = renderNavRailContextHtml(opts.filePath, navRailCommentrayPathFromShell(shell, opts), {
|
|
1003
|
+
sourceOnGithubUrl: opts.sourceOnGithubUrl,
|
|
1004
|
+
commentrayOnGithubUrl: toolbarCommentrayGithubFromShell(shell, opts),
|
|
1005
|
+
});
|
|
317
1006
|
return buildCodeBrowserPageHtml({
|
|
318
1007
|
title,
|
|
319
1008
|
generatorMetaHtml,
|
|
320
|
-
|
|
1009
|
+
navRailContextHtml,
|
|
1010
|
+
angleSelectHtml: shell.angleSelectHtml,
|
|
1011
|
+
toolbarDocHubHtml,
|
|
1012
|
+
navRailDocumentedHtml,
|
|
321
1013
|
relatedNavHtml,
|
|
322
1014
|
toolbarEndHtml,
|
|
323
|
-
|
|
324
|
-
|
|
1015
|
+
pageFooterHtml,
|
|
1016
|
+
layout: shell.layout,
|
|
1017
|
+
shellInner: shell.shellInner,
|
|
325
1018
|
rawCodeB64,
|
|
326
1019
|
rawMdB64,
|
|
1020
|
+
scrollBlockLinksB64,
|
|
1021
|
+
shellDocumentedPairsAttr,
|
|
327
1022
|
hljs,
|
|
328
1023
|
hljsDark,
|
|
329
1024
|
mermaidScript,
|
|
1025
|
+
searchPlaceholder,
|
|
1026
|
+
shellSearchAttrs,
|
|
1027
|
+
multiAngleScriptBlock: shell.multiAnglePayloadB64,
|
|
330
1028
|
});
|
|
331
1029
|
}
|
|
332
1030
|
//# sourceMappingURL=code-browser.js.map
|