@commentray/render 0.0.4 → 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.map +1 -1
- package/dist/block-stretch-layout.js +11 -23
- package/dist/block-stretch-layout.js.map +1 -1
- package/dist/build-commentray-nav-search.d.ts +11 -1
- package/dist/build-commentray-nav-search.d.ts.map +1 -1
- package/dist/build-commentray-nav-search.js +45 -26
- package/dist/build-commentray-nav-search.js.map +1 -1
- 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 +11 -6
- package/dist/code-browser-client.js +463 -94
- package/dist/code-browser-client.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.d.ts +30 -2
- package/dist/code-browser.d.ts.map +1 -1
- package/dist/code-browser.js +599 -171
- 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 +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown-pipeline.js +1 -1
- package/dist/markdown-pipeline.js.map +1 -1
- package/package.json +2 -2
package/dist/code-browser.js
CHANGED
|
@@ -3,78 +3,109 @@ import { dirname, join } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { MARKER_ID_BODY, buildBlockScrollLinks, } from "@commentray/core";
|
|
5
5
|
import { tryBuildBlockStretchTableHtml } from "./block-stretch-layout.js";
|
|
6
|
+
import { formatCommentrayBuiltAtLocal } from "./build-stamp.js";
|
|
6
7
|
import { escapeHtml } from "./html-utils.js";
|
|
8
|
+
import { renderHighlightedCodeLineRows } from "./highlighted-code-lines.js";
|
|
7
9
|
import { mermaidRuntimeScriptHtml } from "./mermaid-runtime-html.js";
|
|
8
|
-
import {
|
|
10
|
+
import { renderMarkdownToHtml } from "./markdown-pipeline.js";
|
|
11
|
+
import { commentrayRenderVersion } from "./package-version.js";
|
|
9
12
|
function renderGeneratorMetaHtml(label) {
|
|
10
13
|
const t = label?.trim();
|
|
11
14
|
if (!t)
|
|
12
15
|
return "";
|
|
13
16
|
return `<meta name="generator" content="${escapeHtml(t)}" />\n `;
|
|
14
17
|
}
|
|
15
|
-
function extractPreCodeInner(html) {
|
|
16
|
-
const m = /<pre(?:\s[^>]*)?>\s*<code(?:\s[^>]*)?>([\s\S]*?)<\/code>\s*<\/pre>/i.exec(html.trim());
|
|
17
|
-
return m ? m[1] : escapeHtml(html);
|
|
18
|
-
}
|
|
19
18
|
/** Single capture: marker id (avoid a wrapping group around the whole comment — that shifted indices). */
|
|
20
19
|
const BLOCK_MARKER_HTML_LINE = new RegExp(`^<!--\\s*commentray:block\\s+id=(${MARKER_ID_BODY})\\s*-->$`, "i");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const byId = links ? new Map(links.map((l) => [l.id, l])) : undefined;
|
|
24
|
-
return markdown
|
|
25
|
-
.split("\n")
|
|
26
|
-
.map((line) => {
|
|
27
|
-
const m = BLOCK_MARKER_HTML_LINE.exec(line);
|
|
28
|
-
if (!m?.[1])
|
|
29
|
-
return line;
|
|
30
|
-
const id = m[1];
|
|
31
|
-
const link = byId?.get(id);
|
|
32
|
-
const attrs = link !== undefined
|
|
33
|
-
? ` data-source-start="${String(link.sourceStart)}" data-commentray-line="${String(link.commentrayLine)}"`
|
|
34
|
-
: "";
|
|
35
|
-
return `${line}\n\n<div id="commentray-block-${escapeHtml(id)}" class="commentray-block-anchor" aria-hidden="true"${attrs}></div>`;
|
|
36
|
-
})
|
|
37
|
-
.join("\n");
|
|
20
|
+
function trimEndSpacesTabs(s) {
|
|
21
|
+
return s.replace(/[ \t]+$/, "");
|
|
38
22
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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));
|
|
32
|
+
}
|
|
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() === "";
|
|
56
49
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const idx = normalized.lastIndexOf("/");
|
|
61
|
-
if (idx < 0)
|
|
62
|
-
return { dir: "", base: normalized };
|
|
63
|
-
return { dir: normalized.slice(0, idx + 1), base: normalized.slice(idx + 1) };
|
|
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>`;
|
|
64
53
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)}`;
|
|
61
|
+
}
|
|
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));
|
|
69
107
|
}
|
|
70
|
-
|
|
71
|
-
const dirHtml = dir
|
|
72
|
-
? `<span class="file-path__dir">${escapeHtml(dir)}</span>`
|
|
73
|
-
: `<span class="file-path__dir file-path__dir--root" title="Repository root">/ </span>`;
|
|
74
|
-
return (`<strong class="file-path" title="${escapeHtml(shown)}">` +
|
|
75
|
-
dirHtml +
|
|
76
|
-
`<span class="file-path__base">${escapeHtml(base)}</span>` +
|
|
77
|
-
`</strong>`);
|
|
108
|
+
return out.join("\n");
|
|
78
109
|
}
|
|
79
110
|
/** GitHub “mark” glyph (Octicons-style path), MIT-licensed silhouette. */
|
|
80
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">' +
|
|
@@ -88,7 +119,7 @@ function safeExternalHttpUrl(url) {
|
|
|
88
119
|
return null;
|
|
89
120
|
return t;
|
|
90
121
|
}
|
|
91
|
-
function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl) {
|
|
122
|
+
function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl, commentrayRenderSemver) {
|
|
92
123
|
const gh = safeExternalHttpUrl(githubRepoUrl);
|
|
93
124
|
const tool = safeExternalHttpUrl(toolHomeUrl);
|
|
94
125
|
const bits = [];
|
|
@@ -98,12 +129,20 @@ function buildToolbarEndHtml(githubRepoUrl, toolHomeUrl) {
|
|
|
98
129
|
}
|
|
99
130
|
if (tool) {
|
|
100
131
|
const te = escapeHtml(tool);
|
|
101
|
-
|
|
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>`);
|
|
102
134
|
}
|
|
103
135
|
if (bits.length === 0)
|
|
104
136
|
return "";
|
|
105
137
|
return `<div class="toolbar__end">${bits.join("")}</div>`;
|
|
106
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
|
+
}
|
|
107
146
|
function renderRelatedGithubNavHtml(links) {
|
|
108
147
|
if (links.length === 0)
|
|
109
148
|
return "";
|
|
@@ -114,34 +153,50 @@ function renderRelatedGithubNavHtml(links) {
|
|
|
114
153
|
`</nav>`);
|
|
115
154
|
}
|
|
116
155
|
function renderToolbarDocHubHtml(opts) {
|
|
117
|
-
const parts = [];
|
|
118
|
-
const src = safeExternalHttpUrl(opts.sourceOnGithubUrl);
|
|
119
|
-
const cr = safeExternalHttpUrl(opts.commentrayOnGithubUrl);
|
|
120
156
|
const nav = opts.documentedNavJsonUrl?.trim();
|
|
121
157
|
const hasEmbed = (opts.documentedPairsEmbeddedB64?.trim() ?? "").length > 0;
|
|
122
158
|
const showDocumentedTree = Boolean(nav) || hasEmbed;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (showDocumentedTree) {
|
|
130
|
-
const navAttr = nav ? escapeHtml(nav) : "";
|
|
131
|
-
parts.push(`<button type="button" class="toolbar-tree-toggle" id="documented-files-toggle" aria-expanded="false" aria-controls="documented-files-panel" data-nav-json-url="${navAttr}">Documented files</button>`);
|
|
132
|
-
}
|
|
133
|
-
const toolbarDocHubHtml = parts.length > 0
|
|
134
|
-
? `<div class="toolbar-doc-hub">${parts.join('<span class="toolbar-doc-hub__sep" aria-hidden="true"> · </span>')}</div>`
|
|
135
|
-
: "";
|
|
136
|
-
const documentedPanelHtml = showDocumentedTree
|
|
137
|
-
? `<div id="documented-files-panel" class="documented-files-panel" hidden>
|
|
138
|
-
<div class="documented-files-panel__inner">
|
|
139
|
-
<p class="documented-files-panel__hint">Indexed source ↔ commentray pairs (embedded for offline when available). Links open on GitHub.</p>
|
|
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">
|
|
140
165
|
<div id="documented-files-tree" class="documented-files-tree" role="tree"></div>
|
|
141
166
|
</div>
|
|
142
|
-
</
|
|
167
|
+
</details>`
|
|
143
168
|
: "";
|
|
144
|
-
return { toolbarDocHubHtml,
|
|
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>`;
|
|
145
200
|
}
|
|
146
201
|
/** IIFE produced by `npm run build -w @commentray/render` (esbuild of `code-browser-client.ts`). */
|
|
147
202
|
function loadCodeBrowserClientBundle() {
|
|
@@ -159,6 +214,220 @@ const CODE_BROWSER_STYLES = `
|
|
|
159
214
|
:root { color-scheme: light dark; }
|
|
160
215
|
* { box-sizing: border-box; }
|
|
161
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; }
|
|
162
431
|
.toolbar {
|
|
163
432
|
display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 8px 12px;
|
|
164
433
|
border-bottom: 1px solid color-mix(in oklab, CanvasText 18%, Canvas);
|
|
@@ -166,7 +435,7 @@ const CODE_BROWSER_STYLES = `
|
|
|
166
435
|
}
|
|
167
436
|
.toolbar__main {
|
|
168
437
|
display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
|
|
169
|
-
flex:
|
|
438
|
+
flex: 0 1 auto;
|
|
170
439
|
min-width: 0;
|
|
171
440
|
}
|
|
172
441
|
.toolbar__end {
|
|
@@ -215,45 +484,24 @@ const CODE_BROWSER_STYLES = `
|
|
|
215
484
|
word-break: break-word;
|
|
216
485
|
}
|
|
217
486
|
.toolbar-related__sep { opacity: 0.55; user-select: none; }
|
|
218
|
-
.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
white-space: nowrap;
|
|
227
|
-
}
|
|
228
|
-
.toolbar-tree-toggle {
|
|
229
|
-
font: inherit; font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 6px; cursor: pointer;
|
|
230
|
-
border: 1px solid color-mix(in oklab, CanvasText 25%, Canvas);
|
|
231
|
-
background: color-mix(in oklab, CanvasText 6%, Canvas); color: CanvasText;
|
|
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;
|
|
232
495
|
}
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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;
|
|
238
502
|
}
|
|
239
|
-
.documented-files-
|
|
240
|
-
|
|
241
|
-
.documented-files-panel__code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 12px; }
|
|
242
|
-
.documented-files-tree ul { list-style: none; margin: 0; padding-left: 14px; }
|
|
243
|
-
.documented-files-tree > ul { padding-left: 0; }
|
|
244
|
-
.documented-files-tree li { margin: 2px 0; line-height: 1.4; }
|
|
245
|
-
.documented-files-tree .tree-dir { font-weight: 600; margin-top: 6px; }
|
|
246
|
-
.documented-files-tree .tree-file { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin: 4px 0; }
|
|
247
|
-
.documented-files-tree .tree-file-name { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 12px; }
|
|
248
|
-
.documented-files-tree .tree-file-links { display: inline-flex; flex-wrap: wrap; gap: 6px 10px; font-size: 11px; }
|
|
249
|
-
.documented-files-tree .tree-file-links a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
|
|
250
|
-
.toolbar .search-field {
|
|
251
|
-
display: inline-flex; align-items: center; gap: 6px; flex: 1 1 220px; min-width: 160px;
|
|
252
|
-
}
|
|
253
|
-
.toolbar .search-field input[type="search"] {
|
|
254
|
-
flex: 1; min-width: 0; padding: 4px 8px; font: inherit; border-radius: 6px;
|
|
255
|
-
border: 1px solid color-mix(in oklab, CanvasText 25%, Canvas); background: Canvas;
|
|
256
|
-
color: CanvasText;
|
|
503
|
+
.documented-files-tree .tree-file-link:hover {
|
|
504
|
+
opacity: 0.92;
|
|
257
505
|
}
|
|
258
506
|
.toolbar button {
|
|
259
507
|
font: inherit; padding: 4px 10px; border-radius: 6px; cursor: pointer;
|
|
@@ -261,23 +509,45 @@ const CODE_BROWSER_STYLES = `
|
|
|
261
509
|
color: CanvasText;
|
|
262
510
|
}
|
|
263
511
|
.search-results {
|
|
264
|
-
flex: 0
|
|
265
|
-
|
|
266
|
-
|
|
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;
|
|
267
521
|
}
|
|
268
522
|
.search-results[hidden] { display: none !important; }
|
|
269
|
-
.search-results .hint { opacity: 0.75; margin-bottom:
|
|
523
|
+
.search-results .hint { opacity: 0.75; margin-bottom: 8px; line-height: 1.45; }
|
|
270
524
|
.search-results button.hit {
|
|
271
|
-
display: block; width: 100%; text-align: left; margin:
|
|
525
|
+
display: block; width: 100%; text-align: left; margin: 4px 0; padding: 8px 10px;
|
|
272
526
|
border-radius: 6px; border: 1px solid color-mix(in oklab, CanvasText 14%, Canvas);
|
|
273
527
|
background: color-mix(in oklab, CanvasText 5%, Canvas); color: CanvasText; cursor: pointer;
|
|
274
528
|
font: inherit;
|
|
275
529
|
}
|
|
276
530
|
.search-results button.hit:hover { background: color-mix(in oklab, CanvasText 10%, Canvas); }
|
|
277
|
-
.search-results button.hit .meta { opacity: 0.8; font-size:
|
|
278
|
-
.search-results button.hit .src-tag { opacity: 0.75; font-weight: 500; font-size:
|
|
279
|
-
.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
|
+
}
|
|
280
549
|
.shell { display: flex; flex-direction: row; flex: 1; min-height: 0; }
|
|
550
|
+
.app__main .shell { flex: 1 1 auto; }
|
|
281
551
|
.pane--code {
|
|
282
552
|
flex: 0 0 50%;
|
|
283
553
|
min-width: 120px; overflow: auto; padding: 12px 16px;
|
|
@@ -285,12 +555,12 @@ const CODE_BROWSER_STYLES = `
|
|
|
285
555
|
--code-line-font-size: 13px;
|
|
286
556
|
--code-line-height: 1.5;
|
|
287
557
|
}
|
|
558
|
+
.pane--code .code-line-stack { --code-ln-min-ch: 3; }
|
|
288
559
|
.pane--code .code-line {
|
|
289
560
|
display: grid;
|
|
290
|
-
/* max-content: column wide enough for the longest line number (avoids 100+ bleeding into code). */
|
|
291
561
|
grid-template-columns: max-content 1fr;
|
|
292
|
-
column-gap:
|
|
293
|
-
align-items:
|
|
562
|
+
column-gap: 10px;
|
|
563
|
+
align-items: start;
|
|
294
564
|
}
|
|
295
565
|
.pane--code .code-line pre {
|
|
296
566
|
margin: 0;
|
|
@@ -316,6 +586,8 @@ const CODE_BROWSER_STYLES = `
|
|
|
316
586
|
white-space: nowrap;
|
|
317
587
|
font-size: var(--code-line-font-size);
|
|
318
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;
|
|
319
591
|
}
|
|
320
592
|
.pane--code .code-line:target .ln,
|
|
321
593
|
.pane--code .code-line:hover .ln {
|
|
@@ -336,10 +608,27 @@ const CODE_BROWSER_STYLES = `
|
|
|
336
608
|
content: ""; position: absolute; top: 0; bottom: 0; left: -4px; right: -4px;
|
|
337
609
|
}
|
|
338
610
|
.pane--doc {
|
|
339
|
-
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;
|
|
340
624
|
}
|
|
341
625
|
.pane--doc { font-size: 15px; line-height: 1.45; }
|
|
342
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
|
+
}
|
|
343
632
|
.pane--doc .commentray-block-anchor {
|
|
344
633
|
display: block;
|
|
345
634
|
height: 0;
|
|
@@ -349,7 +638,6 @@ const CODE_BROWSER_STYLES = `
|
|
|
349
638
|
pointer-events: none;
|
|
350
639
|
}
|
|
351
640
|
.pane h2.pane-title { margin: 0 0 10px; font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.75; }
|
|
352
|
-
.app { display: flex; flex-direction: column; height: 100vh; }
|
|
353
641
|
.shell--stretch-rows {
|
|
354
642
|
flex: 1;
|
|
355
643
|
min-height: 0;
|
|
@@ -413,6 +701,7 @@ const CODE_BROWSER_STYLES = `
|
|
|
413
701
|
font-size: var(--code-line-font-size, 13px);
|
|
414
702
|
line-height: var(--code-line-height, 1.5);
|
|
415
703
|
}
|
|
704
|
+
.block-stretch .code-line-stack { --code-ln-min-ch: 3; }
|
|
416
705
|
.block-stretch .code-line .ln {
|
|
417
706
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
418
707
|
font-variant-numeric: tabular-nums;
|
|
@@ -425,6 +714,8 @@ const CODE_BROWSER_STYLES = `
|
|
|
425
714
|
white-space: nowrap;
|
|
426
715
|
font-size: var(--code-line-font-size, 13px);
|
|
427
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;
|
|
428
719
|
}
|
|
429
720
|
.block-stretch.wrap .code-line pre,
|
|
430
721
|
.block-stretch.wrap .code-line pre code { white-space: pre-wrap; word-break: break-word; }
|
|
@@ -439,6 +730,8 @@ const CODE_BROWSER_STYLES = `
|
|
|
439
730
|
}
|
|
440
731
|
.block-stretch-headings .pane-title { margin: 0; }
|
|
441
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).";
|
|
442
735
|
function buildCodeBrowserPageHtml(p) {
|
|
443
736
|
const shellClass = p.layout === "stretch" ? "shell shell--stretch-rows" : "shell";
|
|
444
737
|
return `<!doctype html>
|
|
@@ -455,26 +748,34 @@ ${CODE_BROWSER_STYLES}
|
|
|
455
748
|
</head>
|
|
456
749
|
<body>
|
|
457
750
|
<div class="app">
|
|
458
|
-
<header class="
|
|
459
|
-
<div class="
|
|
460
|
-
|
|
461
|
-
${p.
|
|
462
|
-
<
|
|
463
|
-
<label for="search-q">Search</label>
|
|
464
|
-
<input type="search" id="search-q" placeholder="${escapeHtml(p.searchPlaceholder)}" autocomplete="off" spellcheck="false" />
|
|
465
|
-
<button type="button" id="search-clear" title="Clear search">Clear</button>
|
|
466
|
-
</span>
|
|
467
|
-
${p.relatedNavHtml}
|
|
468
|
-
<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>
|
|
469
756
|
</div>
|
|
470
|
-
|
|
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>
|
|
471
759
|
</header>
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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}>
|
|
475
773
|
${p.shellInner}
|
|
774
|
+
</div>
|
|
476
775
|
</div>
|
|
776
|
+
${p.pageFooterHtml}
|
|
477
777
|
</div>
|
|
778
|
+
<script type="text/plain" id="commentray-multi-angle-b64">${p.multiAngleScriptBlock}</script>
|
|
478
779
|
<script>
|
|
479
780
|
${loadCodeBrowserClientBundle()}
|
|
480
781
|
</script>
|
|
@@ -482,10 +783,94 @@ ${loadCodeBrowserClientBundle()}
|
|
|
482
783
|
</body>
|
|
483
784
|
</html>`;
|
|
484
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
|
+
}
|
|
485
856
|
async function buildCodeBrowserShell(opts, layoutPref) {
|
|
486
857
|
let layout = "dual";
|
|
487
858
|
let shellInner = "";
|
|
488
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
|
+
}
|
|
489
874
|
if (opts.blockStretchRows && layoutPref !== "dual") {
|
|
490
875
|
const stretched = await tryBuildBlockStretchTableHtml({
|
|
491
876
|
code: opts.code,
|
|
@@ -511,12 +896,12 @@ async function buildCodeBrowserShell(opts, layoutPref) {
|
|
|
511
896
|
const links = opts.blockStretchRows !== undefined
|
|
512
897
|
? buildBlockScrollLinks(opts.blockStretchRows.index, opts.blockStretchRows.sourceRelative, opts.blockStretchRows.commentrayPathRel, opts.commentrayMarkdown, opts.code)
|
|
513
898
|
: [];
|
|
514
|
-
const mdForDoc =
|
|
899
|
+
const mdForDoc = injectCommentrayDocAnchors(opts.commentrayMarkdown, links.length > 0 ? links : undefined);
|
|
515
900
|
if (links.length > 0) {
|
|
516
901
|
scrollBlockLinksB64 = Buffer.from(JSON.stringify(links), "utf8").toString("base64");
|
|
517
902
|
}
|
|
518
903
|
const [codeHtml, commentrayHtml] = await Promise.all([
|
|
519
|
-
|
|
904
|
+
renderHighlightedCodeLineRows(opts.code, opts.language),
|
|
520
905
|
renderMarkdownToHtml(mdForDoc, {
|
|
521
906
|
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
522
907
|
}),
|
|
@@ -529,20 +914,29 @@ async function buildCodeBrowserShell(opts, layoutPref) {
|
|
|
529
914
|
` <div class="gutter" id="gutter" role="separator" aria-orientation="vertical" aria-label="Resize panes"></div>\n` +
|
|
530
915
|
` <section class="pane--doc commentray" id="doc-pane" aria-label="Commentray">\n` +
|
|
531
916
|
` <h2 class="pane-title">Commentray</h2>\n` +
|
|
917
|
+
` <div id="doc-pane-body" class="doc-pane-body">\n` +
|
|
532
918
|
` ${commentrayHtml}\n` +
|
|
919
|
+
` </div>\n` +
|
|
533
920
|
` </section>\n`;
|
|
534
921
|
}
|
|
535
|
-
return {
|
|
922
|
+
return {
|
|
923
|
+
layout,
|
|
924
|
+
shellInner,
|
|
925
|
+
scrollBlockLinksB64,
|
|
926
|
+
angleSelectHtml: "",
|
|
927
|
+
multiAnglePayloadB64: "",
|
|
928
|
+
};
|
|
536
929
|
}
|
|
537
|
-
function searchChromeFromOptions(opts) {
|
|
930
|
+
function searchChromeFromOptions(opts, commentrayPathOverride) {
|
|
931
|
+
const crPath = (commentrayPathOverride ?? opts.commentrayPathForSearch ?? "").trim();
|
|
538
932
|
if (opts.staticSearchScope === "commentray-and-paths") {
|
|
539
933
|
return {
|
|
540
|
-
searchPlaceholder: "
|
|
541
|
-
shellSearchAttrs: ` data-search-scope="commentray-and-paths" data-search-file-path="${escapeHtml(opts.filePath ?? "")}" data-search-commentray-path="${escapeHtml(
|
|
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)}"`,
|
|
542
936
|
};
|
|
543
937
|
}
|
|
544
938
|
return {
|
|
545
|
-
searchPlaceholder: "
|
|
939
|
+
searchPlaceholder: "Filename, path, or keywords…",
|
|
546
940
|
shellSearchAttrs: "",
|
|
547
941
|
};
|
|
548
942
|
}
|
|
@@ -552,6 +946,32 @@ function shellDocumentedPairsAttrFromOptions(opts) {
|
|
|
552
946
|
return "";
|
|
553
947
|
return ` data-documented-pairs-b64="${escapeHtml(emb)}"`;
|
|
554
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
|
+
}
|
|
555
975
|
/**
|
|
556
976
|
* Static HTML shell for a minimal “code browser”: code + rendered commentray,
|
|
557
977
|
* draggable vertical splitter, togglable line wrap for the code pane, and
|
|
@@ -559,35 +979,42 @@ function shellDocumentedPairsAttrFromOptions(opts) {
|
|
|
559
979
|
*/
|
|
560
980
|
export async function renderCodeBrowserHtml(opts) {
|
|
561
981
|
const rawCodeB64 = Buffer.from(opts.code, "utf8").toString("base64");
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const toolbarEndHtml = buildToolbarEndHtml(opts.githubRepoUrl, opts.toolHomeUrl);
|
|
566
|
-
const
|
|
567
|
-
const hljsDark = opts
|
|
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);
|
|
568
988
|
const mermaidScript = mermaidRuntimeScriptHtml(opts.includeMermaidRuntime);
|
|
569
989
|
const relatedNavHtml = renderRelatedGithubNavHtml(opts.relatedGithubNav ?? []);
|
|
570
990
|
const generatorMetaHtml = renderGeneratorMetaHtml(opts.generatorLabel);
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
991
|
+
const layoutPref = opts.codeBrowserLayout ?? "auto";
|
|
992
|
+
const shell = await buildCodeBrowserShell(opts, layoutPref);
|
|
993
|
+
const { toolbarDocHubHtml, navRailDocumentedHtml } = renderToolbarDocHubHtml({
|
|
574
994
|
documentedNavJsonUrl: opts.documentedNavJsonUrl,
|
|
575
995
|
documentedPairsEmbeddedB64: opts.documentedPairsEmbeddedB64,
|
|
576
996
|
});
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
const { searchPlaceholder, shellSearchAttrs } = searchChromeFromOptions(opts);
|
|
997
|
+
const rawMdB64 = rawMdB64FromShell(shell, opts);
|
|
998
|
+
const scrollBlockLinksB64 = shell.scrollBlockLinksB64;
|
|
999
|
+
const { searchPlaceholder, shellSearchAttrs: shellSearchAttrsBase } = searchChromeFromOptions(opts, shell.multiShell?.commentrayPathForSearch);
|
|
580
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
|
+
});
|
|
581
1006
|
return buildCodeBrowserPageHtml({
|
|
582
1007
|
title,
|
|
583
1008
|
generatorMetaHtml,
|
|
584
|
-
|
|
1009
|
+
navRailContextHtml,
|
|
1010
|
+
angleSelectHtml: shell.angleSelectHtml,
|
|
585
1011
|
toolbarDocHubHtml,
|
|
586
|
-
|
|
1012
|
+
navRailDocumentedHtml,
|
|
587
1013
|
relatedNavHtml,
|
|
588
1014
|
toolbarEndHtml,
|
|
589
|
-
|
|
590
|
-
|
|
1015
|
+
pageFooterHtml,
|
|
1016
|
+
layout: shell.layout,
|
|
1017
|
+
shellInner: shell.shellInner,
|
|
591
1018
|
rawCodeB64,
|
|
592
1019
|
rawMdB64,
|
|
593
1020
|
scrollBlockLinksB64,
|
|
@@ -597,6 +1024,7 @@ export async function renderCodeBrowserHtml(opts) {
|
|
|
597
1024
|
mermaidScript,
|
|
598
1025
|
searchPlaceholder,
|
|
599
1026
|
shellSearchAttrs,
|
|
1027
|
+
multiAngleScriptBlock: shell.multiAnglePayloadB64,
|
|
600
1028
|
});
|
|
601
1029
|
}
|
|
602
1030
|
//# sourceMappingURL=code-browser.js.map
|