@commentray/render 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/code-browser-client.bundle.js +24 -11
- package/dist/code-browser-client.js +697 -100
- package/dist/code-browser-client.js.map +1 -1
- package/dist/code-browser-intro.css +187 -0
- package/dist/code-browser-wide-intro-controller.d.ts +4 -0
- package/dist/code-browser-wide-intro-controller.d.ts.map +1 -0
- package/dist/code-browser-wide-intro-controller.js +148 -0
- package/dist/code-browser-wide-intro-controller.js.map +1 -0
- package/dist/code-browser-wide-intro-layout.d.ts +3 -0
- package/dist/code-browser-wide-intro-layout.d.ts.map +1 -0
- package/dist/code-browser-wide-intro-layout.js +84 -0
- package/dist/code-browser-wide-intro-layout.js.map +1 -0
- package/dist/code-browser-wide-intro-steps.d.ts +11 -0
- package/dist/code-browser-wide-intro-steps.d.ts.map +1 -0
- package/dist/code-browser-wide-intro-steps.js +108 -0
- package/dist/code-browser-wide-intro-steps.js.map +1 -0
- package/dist/code-browser-wide-intro-ui.d.ts +14 -0
- package/dist/code-browser-wide-intro-ui.d.ts.map +1 -0
- package/dist/code-browser-wide-intro-ui.js +67 -0
- package/dist/code-browser-wide-intro-ui.js.map +1 -0
- package/dist/code-browser.d.ts.map +1 -1
- package/dist/code-browser.js +597 -42
- package/dist/code-browser.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown-pipeline.d.ts +40 -3
- package/dist/markdown-pipeline.d.ts.map +1 -1
- package/dist/markdown-pipeline.js +110 -23
- package/dist/markdown-pipeline.js.map +1 -1
- package/dist/side-by-side-layout-css.d.ts +7 -0
- package/dist/side-by-side-layout-css.d.ts.map +1 -0
- package/dist/side-by-side-layout-css.js +113 -0
- package/dist/side-by-side-layout-css.js.map +1 -0
- package/dist/side-by-side-layout.embedded.d.ts +3 -0
- package/dist/side-by-side-layout.embedded.d.ts.map +1 -0
- package/dist/side-by-side-layout.embedded.js +3 -0
- package/dist/side-by-side-layout.embedded.js.map +1 -0
- package/dist/side-by-side.d.ts.map +1 -1
- package/dist/side-by-side.js +2 -5
- package/dist/side-by-side.js.map +1 -1
- package/package.json +4 -3
package/dist/code-browser.js
CHANGED
|
@@ -11,6 +11,7 @@ import { COMMENTRAY_FAVICON_LINK_HTML } from "./inline-favicon.js";
|
|
|
11
11
|
import { mermaidRuntimeScriptHtml } from "./mermaid-runtime-html.js";
|
|
12
12
|
import { renderMarkdownToHtml } from "./markdown-pipeline.js";
|
|
13
13
|
import { commentrayRenderVersion } from "./package-version.js";
|
|
14
|
+
import { normPosixPath } from "./code-browser-pair-nav.js";
|
|
14
15
|
function renderGeneratorMetaHtml(label) {
|
|
15
16
|
const t = label?.trim();
|
|
16
17
|
if (!t)
|
|
@@ -31,6 +32,7 @@ function renderMetaDescriptionHtml(opts, title) {
|
|
|
31
32
|
}
|
|
32
33
|
/** Single capture: marker id (avoid a wrapping group around the whole comment — that shifted indices). */
|
|
33
34
|
const BLOCK_MARKER_HTML_LINE = new RegExp(`^<!--\\s*commentray:block\\s+id=(${MARKER_ID_BODY})\\s*-->$`, "i");
|
|
35
|
+
const PAGE_BREAK_MARKER_HTML_LINE = /^<!--\s*commentray:page-break\s*-->$/i;
|
|
34
36
|
function trimEndSpacesTabs(s) {
|
|
35
37
|
let end = s.length;
|
|
36
38
|
while (end > 0) {
|
|
@@ -68,10 +70,72 @@ function isClosingFenceLine(info, open) {
|
|
|
68
70
|
return false;
|
|
69
71
|
return info.rest.trim() === "";
|
|
70
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* GFM delimiter row: cells between pipes contain only colons, hyphens, and spaces; each cell has
|
|
75
|
+
* at least three hyphens (same rule remark-gfm uses). Used so we do not append raw HTML to table
|
|
76
|
+
* lines — trailing `<span>` breaks GFM table recognition in the Markdown parser.
|
|
77
|
+
*/
|
|
78
|
+
function isGfmTableDelimiterRow(line) {
|
|
79
|
+
const t = trimEndSpacesTabs(line);
|
|
80
|
+
if (!t.includes("|"))
|
|
81
|
+
return false;
|
|
82
|
+
const cells = t
|
|
83
|
+
.split("|")
|
|
84
|
+
.map((s) => s.trim())
|
|
85
|
+
.filter((s) => s.length > 0);
|
|
86
|
+
if (cells.length === 0)
|
|
87
|
+
return false;
|
|
88
|
+
for (const cell of cells) {
|
|
89
|
+
if (!/^:?-{3,}:?$/.test(cell))
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 0-based line indices that must not receive a trailing line-anchor span: they belong to a GFM
|
|
96
|
+
* table (header + delimiter + following rows until a blank line). Scans full `lines` so indices
|
|
97
|
+
* align with {@link injectCommentrayDocAnchors}; lines inside fenced code are harmless to mark
|
|
98
|
+
* because that pass never appends anchors there anyway.
|
|
99
|
+
*/
|
|
100
|
+
function gfmTableLineIndicesWithoutAnchors(lines) {
|
|
101
|
+
const skip = new Set();
|
|
102
|
+
const n = lines.length;
|
|
103
|
+
for (let i = 0; i < n - 1; i++) {
|
|
104
|
+
const header = lines[i] ?? "";
|
|
105
|
+
const delim = lines[i + 1] ?? "";
|
|
106
|
+
if (header === "")
|
|
107
|
+
continue;
|
|
108
|
+
if (!trimEndSpacesTabs(header).includes("|"))
|
|
109
|
+
continue;
|
|
110
|
+
if (isSetextUnderlineLine(header) || isThematicBreakLine(header))
|
|
111
|
+
continue;
|
|
112
|
+
if (!isGfmTableDelimiterRow(delim))
|
|
113
|
+
continue;
|
|
114
|
+
skip.add(i);
|
|
115
|
+
skip.add(i + 1);
|
|
116
|
+
let j = i + 2;
|
|
117
|
+
while (j < n) {
|
|
118
|
+
const row = lines[j] ?? "";
|
|
119
|
+
if (row === "")
|
|
120
|
+
break;
|
|
121
|
+
if (isSetextUnderlineLine(row) || isThematicBreakLine(row))
|
|
122
|
+
break;
|
|
123
|
+
if (isGfmTableDelimiterRow(row))
|
|
124
|
+
break;
|
|
125
|
+
skip.add(j);
|
|
126
|
+
j++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return skip;
|
|
130
|
+
}
|
|
71
131
|
function lineAnchorHtml(mdLine0) {
|
|
72
132
|
const mdLine = String(mdLine0);
|
|
73
133
|
return `<span class="commentray-line-anchor" data-commentray-md-line="${mdLine}" id="commentray-md-line-${mdLine}" aria-hidden="true"></span>`;
|
|
74
134
|
}
|
|
135
|
+
function sourceLineAnchorHtml(line0) {
|
|
136
|
+
const s = String(line0);
|
|
137
|
+
return `<span class="commentray-line-anchor commentray-line-anchor--source" data-source-md-line="${s}" id="code-md-line-${s}" aria-hidden="true"></span>`;
|
|
138
|
+
}
|
|
75
139
|
function appendMdLineAnchorWhenAllowed(line, mdLine0) {
|
|
76
140
|
if (isSetextUnderlineLine(line) || isThematicBreakLine(line))
|
|
77
141
|
return line;
|
|
@@ -80,17 +144,59 @@ function appendMdLineAnchorWhenAllowed(line, mdLine0) {
|
|
|
80
144
|
return "";
|
|
81
145
|
return `${line}${lineAnchorHtml(mdLine0)}`;
|
|
82
146
|
}
|
|
147
|
+
function appendSourceMdLineAnchorWhenAllowed(line, line0) {
|
|
148
|
+
if (isSetextUnderlineLine(line) || isThematicBreakLine(line))
|
|
149
|
+
return line;
|
|
150
|
+
if (line === "")
|
|
151
|
+
return "";
|
|
152
|
+
return `${line}${sourceLineAnchorHtml(line0)}`;
|
|
153
|
+
}
|
|
154
|
+
function pageBreakNextBlockMetaByLine(lines, byId) {
|
|
155
|
+
const out = new Map();
|
|
156
|
+
let nextMeta = null;
|
|
157
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
158
|
+
const line = lines[i] ?? "";
|
|
159
|
+
const blockMatch = BLOCK_MARKER_HTML_LINE.exec(line);
|
|
160
|
+
if (blockMatch?.[1]) {
|
|
161
|
+
const id = blockMatch[1];
|
|
162
|
+
const sourceStart = byId?.get(id)?.sourceStart;
|
|
163
|
+
nextMeta =
|
|
164
|
+
sourceStart !== undefined ? { commentrayLine: i, sourceStart } : { commentrayLine: i };
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (!PAGE_BREAK_MARKER_HTML_LINE.test(line) || nextMeta === null)
|
|
168
|
+
continue;
|
|
169
|
+
out.set(i, nextMeta);
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
function blockAnchorAttrs(link) {
|
|
174
|
+
if (link === undefined)
|
|
175
|
+
return "";
|
|
176
|
+
return ` data-source-start="${String(link.sourceStart)}" data-commentray-line="${String(link.commentrayLine)}"`;
|
|
177
|
+
}
|
|
178
|
+
function pageBreakNextAttrs(next) {
|
|
179
|
+
if (next === undefined)
|
|
180
|
+
return "";
|
|
181
|
+
const nextCommentrayAttr = ` data-next-commentray-line="${String(next.commentrayLine)}"`;
|
|
182
|
+
const nextSourceAttr = next.sourceStart !== undefined ? ` data-next-source-start="${String(next.sourceStart)}"` : "";
|
|
183
|
+
return `${nextCommentrayAttr}${nextSourceAttr}`;
|
|
184
|
+
}
|
|
83
185
|
/**
|
|
84
186
|
* Inserts per-line anchors for search / hash jumps and block separator anchors after each
|
|
85
187
|
* `<!-- commentray:block … -->` line (optional index attrs).
|
|
86
188
|
*
|
|
87
189
|
* Anchors are appended to the line when safe. A **leading** `<span>` breaks CommonMark block
|
|
88
190
|
* recognition (`#` headings, lists, thematic breaks, fences). Fenced code lines must not get a
|
|
89
|
-
* trailing anchor either (would corrupt fence delimiters or appear inside code).
|
|
191
|
+
* trailing anchor either (would corrupt fence delimiters or appear inside code). **GFM pipe
|
|
192
|
+
* tables** must not get a trailing anchor: extra HTML after the row breaks `remark-gfm` table
|
|
193
|
+
* detection, so tables would render as plain text.
|
|
90
194
|
*/
|
|
91
195
|
function injectCommentrayDocAnchors(markdown, links) {
|
|
92
196
|
const byId = links ? new Map(links.map((l) => [l.id, l])) : undefined;
|
|
93
197
|
const lines = markdown.split("\n");
|
|
198
|
+
const pageBreakNextByLine = pageBreakNextBlockMetaByLine(lines, byId);
|
|
199
|
+
const skipLineAnchor = gfmTableLineIndicesWithoutAnchors(lines);
|
|
94
200
|
let fence = null;
|
|
95
201
|
const out = [];
|
|
96
202
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -113,10 +219,7 @@ function injectCommentrayDocAnchors(markdown, links) {
|
|
|
113
219
|
const m = BLOCK_MARKER_HTML_LINE.exec(line);
|
|
114
220
|
if (m?.[1]) {
|
|
115
221
|
const id = m[1];
|
|
116
|
-
const
|
|
117
|
-
const attrs = link !== undefined
|
|
118
|
-
? ` data-source-start="${String(link.sourceStart)}" data-commentray-line="${String(link.commentrayLine)}"`
|
|
119
|
-
: "";
|
|
222
|
+
const attrs = blockAnchorAttrs(byId?.get(id));
|
|
120
223
|
/** One `push` with embedded `\n\n` merged poorly with `join("\\n")`; keep real blank lines around raw `<div>`. */
|
|
121
224
|
out.push(`${line}${lineAnchorHtml(i)}`);
|
|
122
225
|
out.push("");
|
|
@@ -124,10 +227,56 @@ function injectCommentrayDocAnchors(markdown, links) {
|
|
|
124
227
|
out.push("");
|
|
125
228
|
continue;
|
|
126
229
|
}
|
|
230
|
+
if (PAGE_BREAK_MARKER_HTML_LINE.test(line)) {
|
|
231
|
+
const nextAttrs = pageBreakNextAttrs(pageBreakNextByLine.get(i));
|
|
232
|
+
out.push(`${line}${lineAnchorHtml(i)}`);
|
|
233
|
+
out.push("");
|
|
234
|
+
out.push(`<div class="commentray-page-break" data-commentray-page-break="true"${nextAttrs} aria-hidden="true"><div class="commentray-page-break__rule"></div></div>`);
|
|
235
|
+
out.push("");
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (skipLineAnchor.has(i)) {
|
|
239
|
+
out.push(line);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
127
242
|
out.push(appendMdLineAnchorWhenAllowed(line, i));
|
|
128
243
|
}
|
|
129
244
|
return out.join("\n");
|
|
130
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Adds stable source-line anchors (`id="code-line-N"`) to Markdown so rendered-source mode can
|
|
248
|
+
* preserve block-aware scroll sync and block ray geometry.
|
|
249
|
+
*/
|
|
250
|
+
function injectSourceMarkdownAnchors(markdown) {
|
|
251
|
+
const lines = markdown.split("\n");
|
|
252
|
+
const skipLineAnchor = gfmTableLineIndicesWithoutAnchors(lines);
|
|
253
|
+
let fence = null;
|
|
254
|
+
const out = [];
|
|
255
|
+
for (let i = 0; i < lines.length; i++) {
|
|
256
|
+
const line = lines[i] ?? "";
|
|
257
|
+
const delim = parseFenceDelimiter(line);
|
|
258
|
+
if (fence) {
|
|
259
|
+
if (delim && isClosingFenceLine(delim, fence)) {
|
|
260
|
+
fence = null;
|
|
261
|
+
out.push(line);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
out.push(line);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (delim) {
|
|
268
|
+
fence = { ch: delim.ch, len: delim.runLen };
|
|
269
|
+
out.push(line);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (skipLineAnchor.has(i)) {
|
|
273
|
+
out.push(line);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
out.push(appendSourceMdLineAnchorWhenAllowed(line, i));
|
|
277
|
+
}
|
|
278
|
+
return out.join("\n");
|
|
279
|
+
}
|
|
131
280
|
/** GitHub “mark” glyph (Octicons-style path), MIT-licensed silhouette. */
|
|
132
281
|
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">' +
|
|
133
282
|
'<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>' +
|
|
@@ -159,6 +308,25 @@ const TOOLBAR_ICON_FLIP_PANES_SVG = '<svg xmlns="http://www.w3.org/2000/svg" vie
|
|
|
159
308
|
'<path d="M10.5 12H6l2.5-2.5M6 12l2.5 2.5"/>' +
|
|
160
309
|
'<path d="M13.5 12H18l-2.5-2.5M18 12l-2.5 2.5"/>' +
|
|
161
310
|
"</svg>";
|
|
311
|
+
/** Source markdown mode flip: rendered page <-> plain markdown rows. */
|
|
312
|
+
const TOOLBAR_ICON_FLIP_SOURCE_MARKDOWN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
|
313
|
+
'<rect x="3" y="4" width="8" height="16" rx="1.5"/>' +
|
|
314
|
+
'<path d="M6 8h2M6 11h2M6 14h2"/>' +
|
|
315
|
+
'<rect x="13" y="4" width="8" height="16" rx="1.5"/>' +
|
|
316
|
+
'<path d="m15.5 12 2-2 2 2"/>' +
|
|
317
|
+
'<path d="m19.5 12-2 2-2-2"/>' +
|
|
318
|
+
"</svg>";
|
|
319
|
+
/** Link/share glyph for copying a permalink to the current documentation pair. */
|
|
320
|
+
const TOOLBAR_ICON_SHARE_LINK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
|
321
|
+
'<path d="M10 14a5 5 0 0 0 7.07 0l2.83-2.83a5 5 0 0 0-7.07-7.07L10 6"/>' +
|
|
322
|
+
'<path d="M14 10a5 5 0 0 0-7.07 0L4.1 12.83a5 5 0 0 0 7.07 7.07L14 17"/>' +
|
|
323
|
+
"</svg>";
|
|
324
|
+
/** Help glyph for re-running the onboarding walkthrough. */
|
|
325
|
+
const TOOLBAR_ICON_HELP_TOUR_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
|
326
|
+
'<circle cx="12" cy="12" r="9"/>' +
|
|
327
|
+
'<path d="M9.1 9a3 3 0 1 1 4.92 2.3c-.8.6-1.52 1.08-1.52 2.2"/>' +
|
|
328
|
+
'<circle cx="12" cy="17" r="0.8" fill="currentColor" stroke="none"/>' +
|
|
329
|
+
"</svg>";
|
|
162
330
|
function safeExternalHttpUrl(url) {
|
|
163
331
|
const t = url?.trim();
|
|
164
332
|
if (!t)
|
|
@@ -242,9 +410,13 @@ function renderToolbarDocHubHtml(opts) {
|
|
|
242
410
|
: "";
|
|
243
411
|
return { toolbarDocHubHtml, navRailDocumentedHtml };
|
|
244
412
|
}
|
|
245
|
-
function dualPanePanesInnerHtml(codeHtml, commentrayHtml) {
|
|
413
|
+
function dualPanePanesInnerHtml(codeHtml, commentrayHtml, sourceMarkdownRenderedHtml) {
|
|
414
|
+
const sourceRenderedPaneHtml = typeof sourceMarkdownRenderedHtml === "string" && sourceMarkdownRenderedHtml.trim().length > 0
|
|
415
|
+
? ` <div class="source-pane source-pane--rendered-md" id="code-pane-markdown-body">${sourceMarkdownRenderedHtml}</div>\n`
|
|
416
|
+
: "";
|
|
246
417
|
return (` <section class="pane--code" id="code-pane" aria-label="Source code">` +
|
|
247
|
-
`
|
|
418
|
+
` <div class="source-pane source-pane--code" id="code-pane-code-body">${codeHtml}</div>\n` +
|
|
419
|
+
sourceRenderedPaneHtml +
|
|
248
420
|
` </section>\n` +
|
|
249
421
|
` <div class="gutter" id="gutter" role="separator" aria-orientation="vertical" aria-label="Resize panes"></div>\n` +
|
|
250
422
|
` <section class="pane--doc commentray" id="doc-pane" aria-label="Commentray">\n` +
|
|
@@ -253,6 +425,26 @@ function dualPanePanesInnerHtml(codeHtml, commentrayHtml) {
|
|
|
253
425
|
` </div>\n` +
|
|
254
426
|
` </section>\n`);
|
|
255
427
|
}
|
|
428
|
+
function sourceMarkdownToggleControlsHtml(enabled) {
|
|
429
|
+
if (!enabled) {
|
|
430
|
+
return { sourceMarkdownToggleHtml: "", sourceMarkdownFlipScrollAffordanceHtml: "" };
|
|
431
|
+
}
|
|
432
|
+
const label = "Switch source pane between rendered markdown and markdown source";
|
|
433
|
+
const title = "Switch source pane between rendered markdown and markdown source";
|
|
434
|
+
const btn = `<button type="button" id="source-markdown-pane-flip" class="toolbar-source-render-toggle" aria-controls="code-pane" aria-pressed="false" aria-label="${label}" title="${title}"><span class="toolbar-source-render-toggle__box" aria-hidden="true"></span><span class="toolbar-source-render-toggle__face" aria-hidden="true">${TOOLBAR_ICON_FLIP_SOURCE_MARKDOWN_SVG}</span><span class="toolbar-source-render-toggle__caption">Render</span></button>`;
|
|
435
|
+
const floating = `<button type="button" id="source-markdown-pane-flip-scroll" class="toolbar-icon-btn toolbar-icon-btn--source-markdown-scroll-narrow" hidden aria-controls="code-pane" aria-pressed="false" aria-label="${label}" title="${title}">${TOOLBAR_ICON_FLIP_SOURCE_MARKDOWN_SVG}</button>`;
|
|
436
|
+
return {
|
|
437
|
+
sourceMarkdownToggleHtml: btn,
|
|
438
|
+
sourceMarkdownFlipScrollAffordanceHtml: floating,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function isMarkdownLikeSource(opts) {
|
|
442
|
+
const lang = opts.language.trim().toLowerCase();
|
|
443
|
+
if (lang === "md" || lang === "markdown" || lang === "mdx")
|
|
444
|
+
return true;
|
|
445
|
+
const path = (opts.filePath ?? "").trim().toLowerCase();
|
|
446
|
+
return path.endsWith(".md") || path.endsWith(".mdx") || path.endsWith(".markdown");
|
|
447
|
+
}
|
|
256
448
|
/** Plain-text Src/Doc labels above the panes; column widths track the resizable split via `--split-pct`. */
|
|
257
449
|
function renderShellPairContextHtml(filePath, commentrayPath) {
|
|
258
450
|
const fpRaw = (filePath ?? "").trim();
|
|
@@ -292,6 +484,19 @@ function loadCodeBrowserClientBundle() {
|
|
|
292
484
|
}
|
|
293
485
|
throw new Error("Missing code-browser-client.bundle.js. Run `npm run build -w @commentray/render` to bundle the browser client.");
|
|
294
486
|
}
|
|
487
|
+
/** Intro-tour specific stylesheet; kept in a dedicated CSS file for easier tweaking. */
|
|
488
|
+
function loadCodeBrowserIntroStyles() {
|
|
489
|
+
const packagesDir = findMonorepoPackagesDir(monorepoLayoutStartDir(import.meta.url));
|
|
490
|
+
const renderDistDir = join(packagesDir, "render", "dist");
|
|
491
|
+
const inDist = join(renderDistDir, "code-browser-intro.css");
|
|
492
|
+
const fromSrc = join(packagesDir, "render", "src", "code-browser-intro.css");
|
|
493
|
+
for (const p of [inDist, fromSrc]) {
|
|
494
|
+
if (existsSync(p)) {
|
|
495
|
+
return readFileSync(p, "utf8");
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
throw new Error("Missing code-browser-intro.css. Ensure the render package includes intro tour styles.");
|
|
499
|
+
}
|
|
295
500
|
/**
|
|
296
501
|
* Compact theme control: primary click opens a menu (readme.io–style), secondary click cycles
|
|
297
502
|
* system → light → dark. Paired with {@link ./code-browser-color-theme.ts} and the client bundle.
|
|
@@ -309,6 +514,11 @@ const TOOLBAR_COLOR_THEME_HTML = ` <div class="toolbar-theme">
|
|
|
309
514
|
</div>
|
|
310
515
|
</div>
|
|
311
516
|
`;
|
|
517
|
+
const TOOLBAR_SHARE_LINK_HTML = ` <button type="button" id="commentray-share-link" class="toolbar-theme__trigger toolbar-share-link-btn" aria-label="Copy shareable permalink" title="Copy shareable permalink">${TOOLBAR_ICON_SHARE_LINK_SVG}</button>
|
|
518
|
+
`;
|
|
519
|
+
const TOOLBAR_HELP_TOUR_HTML = ` <button type="button" id="commentray-help-tour" class="toolbar-theme__trigger toolbar-help-tour-btn" aria-label="Restart onboarding walkthrough" title="Restart onboarding walkthrough">${TOOLBAR_ICON_HELP_TOUR_SVG}</button>
|
|
520
|
+
`;
|
|
521
|
+
const CODE_BROWSER_INTRO_STYLES = loadCodeBrowserIntroStyles();
|
|
312
522
|
const CODE_BROWSER_STYLES = `
|
|
313
523
|
:root {
|
|
314
524
|
--cr-control-h: 32px;
|
|
@@ -654,6 +864,7 @@ const CODE_BROWSER_STYLES = `
|
|
|
654
864
|
}
|
|
655
865
|
.app__footer-attribution__version { font-weight: 600; }
|
|
656
866
|
.toolbar label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
|
|
867
|
+
.toolbar label[hidden] { display: none !important; }
|
|
657
868
|
.toolbar-wrap-lines {
|
|
658
869
|
position: relative;
|
|
659
870
|
margin: 0;
|
|
@@ -772,6 +983,80 @@ const CODE_BROWSER_STYLES = `
|
|
|
772
983
|
outline: 2px solid color-mix(in oklab, CanvasText 45%, Canvas);
|
|
773
984
|
outline-offset: 2px;
|
|
774
985
|
}
|
|
986
|
+
.toolbar-source-render-toggle {
|
|
987
|
+
position: relative;
|
|
988
|
+
margin: 0;
|
|
989
|
+
min-height: var(--cr-control-h);
|
|
990
|
+
padding: 0 12px 0 10px;
|
|
991
|
+
border-radius: var(--cr-control-radius);
|
|
992
|
+
border: 1px solid color-mix(in oklab, CanvasText 16%, Canvas);
|
|
993
|
+
background: Canvas;
|
|
994
|
+
display: inline-flex;
|
|
995
|
+
flex-direction: row;
|
|
996
|
+
align-items: center;
|
|
997
|
+
justify-content: flex-start;
|
|
998
|
+
gap: 8px;
|
|
999
|
+
font-size: var(--cr-ui-fs);
|
|
1000
|
+
font-weight: 500;
|
|
1001
|
+
color: color-mix(in oklab, CanvasText 88%, Canvas);
|
|
1002
|
+
cursor: pointer;
|
|
1003
|
+
}
|
|
1004
|
+
.toolbar-source-render-toggle:hover {
|
|
1005
|
+
background: color-mix(in oklab, CanvasText 6%, Canvas);
|
|
1006
|
+
}
|
|
1007
|
+
.toolbar-source-render-toggle__box {
|
|
1008
|
+
flex: 0 0 auto;
|
|
1009
|
+
width: 16px;
|
|
1010
|
+
height: 16px;
|
|
1011
|
+
box-sizing: border-box;
|
|
1012
|
+
border: 1.5px solid color-mix(in oklab, CanvasText 38%, Canvas);
|
|
1013
|
+
border-radius: 3px;
|
|
1014
|
+
background: Canvas;
|
|
1015
|
+
display: inline-flex;
|
|
1016
|
+
align-items: center;
|
|
1017
|
+
justify-content: center;
|
|
1018
|
+
color: CanvasText;
|
|
1019
|
+
}
|
|
1020
|
+
.toolbar-source-render-toggle[aria-pressed="true"] .toolbar-source-render-toggle__box {
|
|
1021
|
+
border-color: color-mix(in oklab, CanvasText 52%, Canvas);
|
|
1022
|
+
background: color-mix(in oklab, CanvasText 6%, Canvas);
|
|
1023
|
+
}
|
|
1024
|
+
.toolbar-source-render-toggle__box::after {
|
|
1025
|
+
content: "";
|
|
1026
|
+
display: none;
|
|
1027
|
+
width: 4px;
|
|
1028
|
+
height: 9px;
|
|
1029
|
+
margin-top: -2px;
|
|
1030
|
+
border: solid currentColor;
|
|
1031
|
+
border-width: 0 2px 2px 0;
|
|
1032
|
+
transform: rotate(45deg);
|
|
1033
|
+
}
|
|
1034
|
+
.toolbar-source-render-toggle[aria-pressed="true"] .toolbar-source-render-toggle__box::after {
|
|
1035
|
+
display: block;
|
|
1036
|
+
}
|
|
1037
|
+
.toolbar-source-render-toggle__face {
|
|
1038
|
+
display: none;
|
|
1039
|
+
align-items: center;
|
|
1040
|
+
justify-content: center;
|
|
1041
|
+
min-height: var(--cr-control-h);
|
|
1042
|
+
min-width: var(--cr-control-h);
|
|
1043
|
+
color: color-mix(in oklab, CanvasText 82%, Canvas);
|
|
1044
|
+
}
|
|
1045
|
+
.toolbar-source-render-toggle__caption {
|
|
1046
|
+
white-space: nowrap;
|
|
1047
|
+
}
|
|
1048
|
+
.toolbar-source-render-toggle[aria-pressed="true"] {
|
|
1049
|
+
color: CanvasText;
|
|
1050
|
+
background: color-mix(in oklab, CanvasText 10%, Canvas);
|
|
1051
|
+
}
|
|
1052
|
+
.toolbar-source-render-toggle:focus-visible {
|
|
1053
|
+
outline: 2px solid color-mix(in oklab, CanvasText 45%, Canvas);
|
|
1054
|
+
outline-offset: 2px;
|
|
1055
|
+
}
|
|
1056
|
+
.toolbar-icon-btn--source-markdown {
|
|
1057
|
+
display: inline-flex;
|
|
1058
|
+
}
|
|
1059
|
+
${CODE_BROWSER_INTRO_STYLES}
|
|
775
1060
|
.toolbar label input:focus-visible {
|
|
776
1061
|
outline: 2px solid color-mix(in oklab, CanvasText 45%, Canvas);
|
|
777
1062
|
outline-offset: 2px;
|
|
@@ -810,6 +1095,16 @@ const CODE_BROWSER_STYLES = `
|
|
|
810
1095
|
outline: 2px solid color-mix(in oklab, CanvasText 45%, Canvas);
|
|
811
1096
|
outline-offset: 2px;
|
|
812
1097
|
}
|
|
1098
|
+
.toolbar-theme__trigger svg {
|
|
1099
|
+
width: var(--cr-icon-inner);
|
|
1100
|
+
height: var(--cr-icon-inner);
|
|
1101
|
+
display: block;
|
|
1102
|
+
flex: 0 0 auto;
|
|
1103
|
+
}
|
|
1104
|
+
.toolbar-share-link-btn[data-copied="true"] {
|
|
1105
|
+
background: color-mix(in oklab, #2ea043 24%, Canvas);
|
|
1106
|
+
border-color: color-mix(in oklab, #2ea043 48%, CanvasText);
|
|
1107
|
+
}
|
|
813
1108
|
.toolbar-theme__trigger .toolbar-theme__icon {
|
|
814
1109
|
display: none;
|
|
815
1110
|
flex: 0 0 auto;
|
|
@@ -917,6 +1212,14 @@ const CODE_BROWSER_STYLES = `
|
|
|
917
1212
|
.documented-files-tree .tree-file-link:hover {
|
|
918
1213
|
opacity: 0.92;
|
|
919
1214
|
}
|
|
1215
|
+
.documented-files-tree .tree-file-link.tree-file-link--current {
|
|
1216
|
+
font-weight: 600;
|
|
1217
|
+
text-decoration-thickness: 2px;
|
|
1218
|
+
border-radius: 3px;
|
|
1219
|
+
padding: 1px 3px;
|
|
1220
|
+
margin: -1px -3px;
|
|
1221
|
+
background: color-mix(in oklab, CanvasText 10%, Canvas);
|
|
1222
|
+
}
|
|
920
1223
|
.toolbar button {
|
|
921
1224
|
font: inherit;
|
|
922
1225
|
font-size: var(--cr-ui-fs);
|
|
@@ -1044,6 +1347,31 @@ const CODE_BROWSER_STYLES = `
|
|
|
1044
1347
|
--code-line-font-size: 13px;
|
|
1045
1348
|
--code-line-height: 1.5;
|
|
1046
1349
|
}
|
|
1350
|
+
.source-pane {
|
|
1351
|
+
min-width: 0;
|
|
1352
|
+
}
|
|
1353
|
+
.source-pane--rendered-md {
|
|
1354
|
+
font-size: 15px;
|
|
1355
|
+
line-height: 1.45;
|
|
1356
|
+
}
|
|
1357
|
+
.source-pane--rendered-md img {
|
|
1358
|
+
max-width: 100%;
|
|
1359
|
+
height: auto;
|
|
1360
|
+
}
|
|
1361
|
+
.source-pane--rendered-md .commentray-mermaid {
|
|
1362
|
+
overflow-x: auto;
|
|
1363
|
+
max-width: 100%;
|
|
1364
|
+
}
|
|
1365
|
+
.source-pane--rendered-md .commentray-line-anchor--source {
|
|
1366
|
+
display: inline;
|
|
1367
|
+
vertical-align: baseline;
|
|
1368
|
+
}
|
|
1369
|
+
#shell[data-source-pane-mode="rendered-markdown"] .source-pane--code {
|
|
1370
|
+
display: none;
|
|
1371
|
+
}
|
|
1372
|
+
#shell[data-source-pane-mode="source"] .source-pane--rendered-md {
|
|
1373
|
+
display: none;
|
|
1374
|
+
}
|
|
1047
1375
|
.pane--code .code-line-stack { --code-ln-min-ch: 3; }
|
|
1048
1376
|
.pane--code .code-line {
|
|
1049
1377
|
display: grid;
|
|
@@ -1144,11 +1472,95 @@ const CODE_BROWSER_STYLES = `
|
|
|
1144
1472
|
.doc-pane-body {
|
|
1145
1473
|
flex: 1 1 auto; min-height: 0; overflow: auto;
|
|
1146
1474
|
}
|
|
1147
|
-
|
|
1148
|
-
.pane--doc .doc-pane-body :where(
|
|
1475
|
+
/* Inline backtick code chips (GitHub-like): prose context only, never fenced pre/code blocks. */
|
|
1476
|
+
.pane--doc .doc-pane-body :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1477
|
+
.shell--stretch-rows .stretch-preamble :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1478
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code {
|
|
1479
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
1480
|
+
font-size: 0.92em;
|
|
1481
|
+
padding: 0.12em 0.36em;
|
|
1482
|
+
border-radius: 6px;
|
|
1483
|
+
border: 1px solid color-mix(in oklab, CanvasText 12%, Canvas);
|
|
1484
|
+
background: color-mix(in oklab, CanvasText 8%, Canvas);
|
|
1485
|
+
color: inherit;
|
|
1486
|
+
}
|
|
1487
|
+
@media (prefers-color-scheme: dark) {
|
|
1488
|
+
:root:is(:not([data-commentray-theme]), [data-commentray-theme="system"]) .pane--doc .doc-pane-body :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1489
|
+
:root:is(:not([data-commentray-theme]), [data-commentray-theme="system"]) .shell--stretch-rows .stretch-preamble :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1490
|
+
:root:is(:not([data-commentray-theme]), [data-commentray-theme="system"]) .block-stretch td.stretch-doc .stretch-doc-inner :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code {
|
|
1491
|
+
border-color: color-mix(in oklab, CanvasText 26%, Canvas);
|
|
1492
|
+
background: color-mix(in oklab, CanvasText 16%, Canvas);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
:root[data-commentray-theme="dark"] .pane--doc .doc-pane-body :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1496
|
+
:root[data-commentray-theme="dark"] .shell--stretch-rows .stretch-preamble :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code,
|
|
1497
|
+
:root[data-commentray-theme="dark"] .block-stretch td.stretch-doc .stretch-doc-inner :where(p, li, blockquote, td, th, h1, h2, h3, h4, h5, h6) > code {
|
|
1498
|
+
border-color: color-mix(in oklab, CanvasText 26%, Canvas);
|
|
1499
|
+
background: color-mix(in oklab, CanvasText 16%, Canvas);
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* GFM tables in rendered Markdown (doc pane, stretch preamble, per-block doc cells).
|
|
1503
|
+
* Intrinsic width so the pane scrolls sideways instead of squeezing columns; borders
|
|
1504
|
+
* and padding match familiar GitHub-style readability.
|
|
1505
|
+
*/
|
|
1506
|
+
.pane--doc .doc-pane-body :where(table),
|
|
1507
|
+
.shell--stretch-rows .stretch-preamble :where(table),
|
|
1508
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(table) {
|
|
1149
1509
|
width: max-content;
|
|
1150
1510
|
max-width: none;
|
|
1151
1511
|
border-collapse: collapse;
|
|
1512
|
+
margin: 0.85em 0;
|
|
1513
|
+
font-size: inherit;
|
|
1514
|
+
line-height: inherit;
|
|
1515
|
+
}
|
|
1516
|
+
.pane--doc .doc-pane-body :where(th, td),
|
|
1517
|
+
.shell--stretch-rows .stretch-preamble :where(th, td),
|
|
1518
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(th, td) {
|
|
1519
|
+
border: 1px solid color-mix(in oklab, CanvasText 22%, Canvas);
|
|
1520
|
+
padding: 8px 12px;
|
|
1521
|
+
vertical-align: top;
|
|
1522
|
+
}
|
|
1523
|
+
.pane--doc .doc-pane-body :where(thead th),
|
|
1524
|
+
.shell--stretch-rows .stretch-preamble :where(thead th),
|
|
1525
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(thead th) {
|
|
1526
|
+
font-weight: 600;
|
|
1527
|
+
background: color-mix(in oklab, CanvasText 7%, Canvas);
|
|
1528
|
+
}
|
|
1529
|
+
.pane--doc .doc-pane-body tbody tr:nth-child(even) :where(td),
|
|
1530
|
+
.shell--stretch-rows .stretch-preamble tbody tr:nth-child(even) :where(td),
|
|
1531
|
+
.block-stretch td.stretch-doc .stretch-doc-inner tbody tr:nth-child(even) :where(td) {
|
|
1532
|
+
background: color-mix(in oklab, CanvasText 3.5%, Canvas);
|
|
1533
|
+
}
|
|
1534
|
+
.pane--doc .doc-pane-body :where(ul.contains-task-list),
|
|
1535
|
+
.shell--stretch-rows .stretch-preamble :where(ul.contains-task-list),
|
|
1536
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(ul.contains-task-list) {
|
|
1537
|
+
list-style: none;
|
|
1538
|
+
padding-inline-start: 1.2em;
|
|
1539
|
+
}
|
|
1540
|
+
.pane--doc .doc-pane-body :where(li.task-list-item),
|
|
1541
|
+
.shell--stretch-rows .stretch-preamble :where(li.task-list-item),
|
|
1542
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(li.task-list-item) {
|
|
1543
|
+
position: relative;
|
|
1544
|
+
}
|
|
1545
|
+
.pane--doc .doc-pane-body :where(li.task-list-item input[type="checkbox"]),
|
|
1546
|
+
.shell--stretch-rows .stretch-preamble :where(li.task-list-item input[type="checkbox"]),
|
|
1547
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(li.task-list-item input[type="checkbox"]) {
|
|
1548
|
+
position: absolute;
|
|
1549
|
+
margin-inline-start: -1.35em;
|
|
1550
|
+
margin-top: 0.2em;
|
|
1551
|
+
}
|
|
1552
|
+
.pane--doc .doc-pane-body :where(del),
|
|
1553
|
+
.shell--stretch-rows .stretch-preamble :where(del),
|
|
1554
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(del) {
|
|
1555
|
+
opacity: 0.82;
|
|
1556
|
+
}
|
|
1557
|
+
.pane--doc .doc-pane-body :where(section.footnotes),
|
|
1558
|
+
.shell--stretch-rows .stretch-preamble :where(section.footnotes),
|
|
1559
|
+
.block-stretch td.stretch-doc .stretch-doc-inner :where(section.footnotes) {
|
|
1560
|
+
margin-top: 1.5em;
|
|
1561
|
+
padding-top: 0.75em;
|
|
1562
|
+
border-top: 1px solid color-mix(in oklab, CanvasText 18%, Canvas);
|
|
1563
|
+
font-size: 0.92em;
|
|
1152
1564
|
}
|
|
1153
1565
|
.pane--doc .doc-pane-body .commentray-mermaid {
|
|
1154
1566
|
overflow-x: auto;
|
|
@@ -1166,6 +1578,23 @@ const CODE_BROWSER_STYLES = `
|
|
|
1166
1578
|
overflow-wrap: normal;
|
|
1167
1579
|
word-break: normal;
|
|
1168
1580
|
}
|
|
1581
|
+
#doc-pane-body .commentray-page-break {
|
|
1582
|
+
position: relative;
|
|
1583
|
+
min-height: var(--commentray-page-break-min-height, clamp(260px, 56vh, 620px));
|
|
1584
|
+
margin: 24px 0;
|
|
1585
|
+
display: flex;
|
|
1586
|
+
align-items: center;
|
|
1587
|
+
justify-content: center;
|
|
1588
|
+
pointer-events: none;
|
|
1589
|
+
}
|
|
1590
|
+
#doc-pane-body .commentray-page-break__rule {
|
|
1591
|
+
width: 100%;
|
|
1592
|
+
border-top: 1px dashed var(--border);
|
|
1593
|
+
opacity: 0.38;
|
|
1594
|
+
}
|
|
1595
|
+
#shell[data-page-breaks-enabled="false"] .commentray-page-break {
|
|
1596
|
+
display: none;
|
|
1597
|
+
}
|
|
1169
1598
|
.toolbar-angle-picker {
|
|
1170
1599
|
display: inline-flex;
|
|
1171
1600
|
align-items: center;
|
|
@@ -1385,6 +1814,19 @@ const CODE_BROWSER_STYLES = `
|
|
|
1385
1814
|
0 1px 2px color-mix(in oklab, CanvasText 12%, transparent),
|
|
1386
1815
|
0 4px 14px color-mix(in oklab, CanvasText 18%, transparent);
|
|
1387
1816
|
}
|
|
1817
|
+
.toolbar-icon-btn--source-markdown-scroll-narrow {
|
|
1818
|
+
display: none;
|
|
1819
|
+
}
|
|
1820
|
+
#source-markdown-pane-flip-scroll.toolbar-icon-btn--source-markdown-scroll-narrow.is-visible {
|
|
1821
|
+
display: inline-flex;
|
|
1822
|
+
position: fixed;
|
|
1823
|
+
top: calc(10px + env(safe-area-inset-top, 0px));
|
|
1824
|
+
left: calc(12px + env(safe-area-inset-left, 0px));
|
|
1825
|
+
z-index: 50;
|
|
1826
|
+
box-shadow:
|
|
1827
|
+
0 1px 2px color-mix(in oklab, CanvasText 12%, transparent),
|
|
1828
|
+
0 4px 14px color-mix(in oklab, CanvasText 18%, transparent);
|
|
1829
|
+
}
|
|
1388
1830
|
/** Region connector lines are not needed on the narrow single-pane layout (gutter is hidden). */
|
|
1389
1831
|
.shell:not(.shell--stretch-rows) .gutter .gutter__rays {
|
|
1390
1832
|
opacity: 0 !important;
|
|
@@ -1450,6 +1892,47 @@ const CODE_BROWSER_STYLES = `
|
|
|
1450
1892
|
color: CanvasText;
|
|
1451
1893
|
text-shadow: 0 0 2px Canvas, 0 0 3px Canvas;
|
|
1452
1894
|
}
|
|
1895
|
+
.toolbar-source-render-toggle {
|
|
1896
|
+
min-width: var(--cr-control-h);
|
|
1897
|
+
width: var(--cr-control-h);
|
|
1898
|
+
height: var(--cr-control-h);
|
|
1899
|
+
padding: 0;
|
|
1900
|
+
justify-content: center;
|
|
1901
|
+
gap: 0;
|
|
1902
|
+
}
|
|
1903
|
+
.toolbar-source-render-toggle__caption {
|
|
1904
|
+
position: absolute;
|
|
1905
|
+
width: 1px;
|
|
1906
|
+
height: 1px;
|
|
1907
|
+
padding: 0;
|
|
1908
|
+
margin: -1px;
|
|
1909
|
+
overflow: hidden;
|
|
1910
|
+
clip: rect(0, 0, 0, 0);
|
|
1911
|
+
white-space: nowrap;
|
|
1912
|
+
border: 0;
|
|
1913
|
+
}
|
|
1914
|
+
.toolbar-source-render-toggle__box {
|
|
1915
|
+
display: none;
|
|
1916
|
+
}
|
|
1917
|
+
.toolbar-source-render-toggle__face {
|
|
1918
|
+
display: inline-flex;
|
|
1919
|
+
position: relative;
|
|
1920
|
+
width: 100%;
|
|
1921
|
+
height: 100%;
|
|
1922
|
+
min-height: var(--cr-control-h);
|
|
1923
|
+
min-width: var(--cr-control-h);
|
|
1924
|
+
}
|
|
1925
|
+
.toolbar-source-render-toggle[aria-pressed="true"] .toolbar-source-render-toggle__face::after {
|
|
1926
|
+
content: "✓";
|
|
1927
|
+
position: absolute;
|
|
1928
|
+
right: 1px;
|
|
1929
|
+
bottom: 0;
|
|
1930
|
+
font-size: 11px;
|
|
1931
|
+
line-height: 1;
|
|
1932
|
+
font-weight: 800;
|
|
1933
|
+
color: CanvasText;
|
|
1934
|
+
text-shadow: 0 0 2px Canvas, 0 0 3px Canvas;
|
|
1935
|
+
}
|
|
1453
1936
|
.shell:not(.shell--stretch-rows)[data-dual-mobile-pane="code"] .pane--doc,
|
|
1454
1937
|
.shell:not(.shell--stretch-rows)[data-dual-mobile-pane="code"] .gutter {
|
|
1455
1938
|
display: none !important;
|
|
@@ -1512,11 +1995,6 @@ const CODE_BROWSER_STYLES = `
|
|
|
1512
1995
|
max-width: 100%;
|
|
1513
1996
|
}
|
|
1514
1997
|
.shell--stretch-rows .stretch-preamble img { max-width: 100%; height: auto; }
|
|
1515
|
-
.shell--stretch-rows .stretch-preamble :where(table) {
|
|
1516
|
-
width: max-content;
|
|
1517
|
-
max-width: none;
|
|
1518
|
-
border-collapse: collapse;
|
|
1519
|
-
}
|
|
1520
1998
|
.block-stretch {
|
|
1521
1999
|
width: 100%;
|
|
1522
2000
|
border-collapse: collapse;
|
|
@@ -1541,11 +2019,6 @@ const CODE_BROWSER_STYLES = `
|
|
|
1541
2019
|
overflow-x: auto;
|
|
1542
2020
|
}
|
|
1543
2021
|
.block-stretch td.stretch-doc .stretch-doc-inner img { max-width: 100%; height: auto; }
|
|
1544
|
-
.block-stretch td.stretch-doc .stretch-doc-inner :where(table) {
|
|
1545
|
-
width: max-content;
|
|
1546
|
-
max-width: none;
|
|
1547
|
-
border-collapse: collapse;
|
|
1548
|
-
}
|
|
1549
2022
|
.block-stretch td.stretch-doc .stretch-doc-inner .commentray-mermaid {
|
|
1550
2023
|
overflow-x: auto;
|
|
1551
2024
|
max-width: 100%;
|
|
@@ -1660,16 +2133,20 @@ ${CODE_BROWSER_STYLES}
|
|
|
1660
2133
|
<span class="toolbar-wrap-lines__caption">Wrap lines</span>
|
|
1661
2134
|
</label>
|
|
1662
2135
|
${dualFlipControlHtml}
|
|
2136
|
+
${p.sourceMarkdownToggleHtml}
|
|
1663
2137
|
${p.toolbarDocHubHtml}
|
|
1664
2138
|
${p.relatedNavHtml}
|
|
1665
2139
|
</div>
|
|
1666
2140
|
<div class="toolbar__primary-trail">
|
|
1667
2141
|
${p.toolbarEndHtml}
|
|
2142
|
+
${TOOLBAR_SHARE_LINK_HTML}
|
|
2143
|
+
${TOOLBAR_HELP_TOUR_HTML}
|
|
1668
2144
|
${TOOLBAR_COLOR_THEME_HTML}
|
|
1669
2145
|
</div>
|
|
1670
2146
|
</div>
|
|
1671
2147
|
</header>
|
|
1672
2148
|
${dualFlipScrollAffordanceHtml}
|
|
2149
|
+
${p.sourceMarkdownFlipScrollAffordanceHtml}
|
|
1673
2150
|
<header class="app__chrome" role="region" aria-label="Search">
|
|
1674
2151
|
<div class="chrome__search-row">
|
|
1675
2152
|
<label class="chrome__search-label" for="search-q" aria-label="Search" title="Search"><span class="chrome__search-label__caption nav-rail__search-label">Search</span><span class="chrome__search-label__glyph" aria-hidden="true">${CHROME_ICON_SEARCH_SVG}</span></label>
|
|
@@ -1679,7 +2156,7 @@ ${TOOLBAR_COLOR_THEME_HTML}
|
|
|
1679
2156
|
<div class="search-results" id="search-results" hidden aria-live="polite"></div>
|
|
1680
2157
|
</header>
|
|
1681
2158
|
<main id="main-content" class="app__main" tabindex="-1">
|
|
1682
|
-
<div class="${shellClass}" id="shell" data-layout="${p.layout}"${p.layout === "dual" ? ' data-dual-mobile-pane="doc"' : ""} 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}${p.shellPairDocDataAttr}>
|
|
2159
|
+
<div class="${shellClass}" id="shell" data-layout="${p.layout}"${p.layout === "dual" ? ' data-dual-mobile-pane="doc"' : ""}${p.sourcePaneModeAttr} 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}${p.shellPairIdentityDataAttrs}${p.shellPairDocDataAttr}>
|
|
1683
2160
|
${p.shellInner}
|
|
1684
2161
|
</div>
|
|
1685
2162
|
</main>
|
|
@@ -1693,6 +2170,44 @@ ${loadCodeBrowserClientBundle()}
|
|
|
1693
2170
|
</body>
|
|
1694
2171
|
</html>`;
|
|
1695
2172
|
}
|
|
2173
|
+
function firstNonEmpty(values) {
|
|
2174
|
+
return values.find((v) => v.trim().length > 0);
|
|
2175
|
+
}
|
|
2176
|
+
function resolveMultiAngleDefaultSelection(args) {
|
|
2177
|
+
const { multi, defaultId, opts, builtAngles } = args;
|
|
2178
|
+
let defaultMarkdown = opts.commentrayMarkdown;
|
|
2179
|
+
let defaultScrollB64 = "";
|
|
2180
|
+
let defaultPathSearch = (opts.commentrayPathForSearch ?? "").trim();
|
|
2181
|
+
let defaultGh = opts.commentrayOnGithubUrl;
|
|
2182
|
+
let defaultStaticBrowse = (opts.commentrayStaticBrowseUrl ?? "").trim();
|
|
2183
|
+
let defaultPaneHtml = "";
|
|
2184
|
+
for (const b of builtAngles) {
|
|
2185
|
+
if (b.spec.id !== defaultId)
|
|
2186
|
+
continue;
|
|
2187
|
+
defaultMarkdown = b.spec.markdown;
|
|
2188
|
+
defaultScrollB64 = b.scrollB64;
|
|
2189
|
+
defaultPathSearch = b.spec.commentrayPathRel.trim();
|
|
2190
|
+
defaultGh = b.spec.commentrayOnGithubUrl;
|
|
2191
|
+
defaultStaticBrowse = (b.spec.staticBrowseUrl ?? "").trim();
|
|
2192
|
+
defaultPaneHtml = b.commentrayHtml;
|
|
2193
|
+
break;
|
|
2194
|
+
}
|
|
2195
|
+
if (defaultStaticBrowse.length === 0) {
|
|
2196
|
+
defaultStaticBrowse =
|
|
2197
|
+
firstNonEmpty(multi.angles.map((a) => (a.staticBrowseUrl ?? "").trim())) ?? "";
|
|
2198
|
+
}
|
|
2199
|
+
if ((defaultGh ?? "").trim().length === 0) {
|
|
2200
|
+
defaultGh = firstNonEmpty(multi.angles.map((a) => (a.commentrayOnGithubUrl ?? "").trim()));
|
|
2201
|
+
}
|
|
2202
|
+
return {
|
|
2203
|
+
defaultMarkdown,
|
|
2204
|
+
defaultScrollB64,
|
|
2205
|
+
defaultPathSearch,
|
|
2206
|
+
defaultGh,
|
|
2207
|
+
defaultStaticBrowse,
|
|
2208
|
+
defaultPaneHtml,
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
1696
2211
|
async function multiAngleJsonRowAndDocHtml(opts, spec) {
|
|
1697
2212
|
const rows = spec.blockStretchRows;
|
|
1698
2213
|
const links = rows !== undefined
|
|
@@ -1723,29 +2238,23 @@ async function buildMultiAngleDualPaneShell(opts, multi) {
|
|
|
1723
2238
|
? multi.defaultAngleId
|
|
1724
2239
|
: (multi.angles[0]?.id ?? "main");
|
|
1725
2240
|
const jsonAngles = [];
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
2241
|
+
const builtAngles = [];
|
|
2242
|
+
const sourceMarkdownEnabled = isMarkdownLikeSource(opts);
|
|
2243
|
+
const sourceMdForPane = sourceMarkdownEnabled ? injectSourceMarkdownAnchors(opts.code) : "";
|
|
2244
|
+
const [codeHtml, sourceMarkdownPaneHtml] = await Promise.all([
|
|
2245
|
+
renderHighlightedCodeLineRows(opts.code, opts.language),
|
|
2246
|
+
sourceMarkdownEnabled
|
|
2247
|
+
? renderMarkdownToHtml(sourceMdForPane, {
|
|
2248
|
+
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
2249
|
+
})
|
|
2250
|
+
: Promise.resolve(""),
|
|
2251
|
+
]);
|
|
1733
2252
|
for (const spec of multi.angles) {
|
|
1734
2253
|
const { jsonRow, commentrayHtml, scrollB64 } = await multiAngleJsonRowAndDocHtml(opts, spec);
|
|
1735
|
-
|
|
1736
|
-
defaultMarkdown = spec.markdown;
|
|
1737
|
-
defaultScrollB64 = scrollB64;
|
|
1738
|
-
defaultPathSearch = spec.commentrayPathRel.trim();
|
|
1739
|
-
defaultGh = spec.commentrayOnGithubUrl;
|
|
1740
|
-
{
|
|
1741
|
-
const sb = (spec.staticBrowseUrl ?? "").trim();
|
|
1742
|
-
if (sb.length > 0)
|
|
1743
|
-
defaultStaticBrowse = sb;
|
|
1744
|
-
}
|
|
1745
|
-
defaultPaneHtml = commentrayHtml;
|
|
1746
|
-
}
|
|
2254
|
+
builtAngles.push({ spec, commentrayHtml, scrollB64 });
|
|
1747
2255
|
jsonAngles.push(jsonRow);
|
|
1748
2256
|
}
|
|
2257
|
+
const { defaultMarkdown, defaultScrollB64, defaultPathSearch, defaultGh, defaultStaticBrowse, defaultPaneHtml, } = resolveMultiAngleDefaultSelection({ multi, defaultId, opts, builtAngles });
|
|
1749
2258
|
const selOpts = multi.angles
|
|
1750
2259
|
.map((a) => {
|
|
1751
2260
|
const lab = escapeHtml(a.title?.trim() || a.id);
|
|
@@ -1754,7 +2263,7 @@ async function buildMultiAngleDualPaneShell(opts, multi) {
|
|
|
1754
2263
|
.join("");
|
|
1755
2264
|
const angleSelectHtml = `<span class="toolbar-angle-picker"><label class="toolbar-angle-picker__lab nav-rail__search-label" for="angle-select">Angle</label><select id="angle-select" aria-label="Commentray angle">${selOpts}</select></span>`;
|
|
1756
2265
|
const pairHtml = renderShellPairContextHtml(opts.filePath, defaultPathSearch);
|
|
1757
|
-
const shellInner = wrapDualShellInner(pairHtml, dualPanePanesInnerHtml(codeHtml, defaultPaneHtml));
|
|
2266
|
+
const shellInner = wrapDualShellInner(pairHtml, dualPanePanesInnerHtml(codeHtml, defaultPaneHtml, sourceMarkdownPaneHtml));
|
|
1758
2267
|
const payloadObj = { defaultAngleId: defaultId, angles: jsonAngles };
|
|
1759
2268
|
const multiAnglePayloadB64 = Buffer.from(JSON.stringify(payloadObj), "utf8").toString("base64");
|
|
1760
2269
|
return {
|
|
@@ -1768,6 +2277,8 @@ async function buildMultiAngleDualPaneShell(opts, multi) {
|
|
|
1768
2277
|
},
|
|
1769
2278
|
angleSelectHtml,
|
|
1770
2279
|
multiAnglePayloadB64,
|
|
2280
|
+
sourceMarkdownToggleEnabled: sourceMarkdownEnabled,
|
|
2281
|
+
sourcePaneDefaultMode: sourceMarkdownEnabled ? "rendered-markdown" : "source",
|
|
1771
2282
|
};
|
|
1772
2283
|
}
|
|
1773
2284
|
async function buildCodeBrowserShell(opts, layoutPref) {
|
|
@@ -1785,6 +2296,8 @@ async function buildCodeBrowserShell(opts, layoutPref) {
|
|
|
1785
2296
|
scrollBlockLinksB64: ms.scrollBlockLinksB64,
|
|
1786
2297
|
angleSelectHtml: built.angleSelectHtml,
|
|
1787
2298
|
multiAnglePayloadB64: built.multiAnglePayloadB64,
|
|
2299
|
+
sourceMarkdownToggleEnabled: built.sourceMarkdownToggleEnabled,
|
|
2300
|
+
sourcePaneDefaultMode: built.sourcePaneDefaultMode,
|
|
1788
2301
|
multiShell: ms,
|
|
1789
2302
|
};
|
|
1790
2303
|
}
|
|
@@ -1811,14 +2324,30 @@ async function buildCodeBrowserShell(opts, layoutPref) {
|
|
|
1811
2324
|
if (links.length > 0) {
|
|
1812
2325
|
scrollBlockLinksB64 = Buffer.from(JSON.stringify(links), "utf8").toString("base64");
|
|
1813
2326
|
}
|
|
1814
|
-
const
|
|
2327
|
+
const sourceMarkdownEnabled = isMarkdownLikeSource(opts);
|
|
2328
|
+
const sourceMdForPane = sourceMarkdownEnabled ? injectSourceMarkdownAnchors(opts.code) : "";
|
|
2329
|
+
const [codeHtml, commentrayHtml, sourceMarkdownPaneHtml] = await Promise.all([
|
|
1815
2330
|
renderHighlightedCodeLineRows(opts.code, opts.language),
|
|
1816
2331
|
renderMarkdownToHtml(mdForDoc, {
|
|
1817
2332
|
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
1818
2333
|
}),
|
|
2334
|
+
sourceMarkdownEnabled
|
|
2335
|
+
? renderMarkdownToHtml(sourceMdForPane, {
|
|
2336
|
+
commentrayOutputUrls: opts.commentrayOutputUrls,
|
|
2337
|
+
})
|
|
2338
|
+
: Promise.resolve(""),
|
|
1819
2339
|
]);
|
|
1820
2340
|
const pairHtml = renderShellPairContextHtml(opts.filePath, (opts.commentrayPathForSearch ?? "").trim());
|
|
1821
|
-
shellInner = wrapDualShellInner(pairHtml, dualPanePanesInnerHtml(codeHtml, commentrayHtml));
|
|
2341
|
+
shellInner = wrapDualShellInner(pairHtml, dualPanePanesInnerHtml(codeHtml, commentrayHtml, sourceMarkdownPaneHtml));
|
|
2342
|
+
return {
|
|
2343
|
+
layout,
|
|
2344
|
+
shellInner,
|
|
2345
|
+
scrollBlockLinksB64,
|
|
2346
|
+
angleSelectHtml: "",
|
|
2347
|
+
multiAnglePayloadB64: "",
|
|
2348
|
+
sourceMarkdownToggleEnabled: sourceMarkdownEnabled,
|
|
2349
|
+
sourcePaneDefaultMode: sourceMarkdownEnabled ? "rendered-markdown" : "source",
|
|
2350
|
+
};
|
|
1822
2351
|
}
|
|
1823
2352
|
return {
|
|
1824
2353
|
layout,
|
|
@@ -1826,6 +2355,8 @@ async function buildCodeBrowserShell(opts, layoutPref) {
|
|
|
1826
2355
|
scrollBlockLinksB64,
|
|
1827
2356
|
angleSelectHtml: "",
|
|
1828
2357
|
multiAnglePayloadB64: "",
|
|
2358
|
+
sourceMarkdownToggleEnabled: false,
|
|
2359
|
+
sourcePaneDefaultMode: "source",
|
|
1829
2360
|
};
|
|
1830
2361
|
}
|
|
1831
2362
|
function searchChromeFromOptions(opts, commentrayPathOverride) {
|
|
@@ -1856,6 +2387,23 @@ function toolbarCommentrayGithubFromShell(shell, opts) {
|
|
|
1856
2387
|
function rawMdB64FromShell(shell, opts) {
|
|
1857
2388
|
return (shell.multiShell?.rawMdB64 ?? Buffer.from(opts.commentrayMarkdown, "utf8").toString("base64"));
|
|
1858
2389
|
}
|
|
2390
|
+
function currentPairCommentrayPathRel(shell, opts) {
|
|
2391
|
+
return (shell.multiShell?.commentrayPathForSearch ??
|
|
2392
|
+
opts.commentrayPathForSearch ??
|
|
2393
|
+
opts.blockStretchRows?.commentrayPathRel ??
|
|
2394
|
+
"").trim();
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Repo-relative source + companion Markdown paths for matching the current page to nav pairs
|
|
2398
|
+
* (see `code-browser-client.ts` documented-files tree).
|
|
2399
|
+
*/
|
|
2400
|
+
function shellPairIdentityDataAttrs(shell, opts) {
|
|
2401
|
+
const src = normPosixPath(opts.filePath ?? "");
|
|
2402
|
+
const cr = normPosixPath(currentPairCommentrayPathRel(shell, opts));
|
|
2403
|
+
if (src.length === 0 || cr.length === 0)
|
|
2404
|
+
return "";
|
|
2405
|
+
return ` data-commentray-pair-source-path="${escapeHtml(src)}" data-commentray-pair-commentray-path="${escapeHtml(cr)}"`;
|
|
2406
|
+
}
|
|
1859
2407
|
/** Canonical doc target for static validation: same-site `./browse/…` when present, else GitHub blob. */
|
|
1860
2408
|
function shellPairDocDataAttr(shell, opts) {
|
|
1861
2409
|
if (shell.layout !== "dual")
|
|
@@ -1915,11 +2463,15 @@ export async function renderCodeBrowserHtml(opts) {
|
|
|
1915
2463
|
const shellDocumentedPairsAttr = shellDocumentedPairsAttrFromOptions(opts);
|
|
1916
2464
|
const shellSearchAttrs = shellSearchAttrsWithNavJson(shellSearchAttrsBase, opts.documentedNavJsonUrl);
|
|
1917
2465
|
const pairDocDataAttr = shellPairDocDataAttr(shell, opts);
|
|
2466
|
+
const pairIdentityDataAttrs = shellPairIdentityDataAttrs(shell, opts);
|
|
2467
|
+
const sourceMarkdownToggles = sourceMarkdownToggleControlsHtml(shell.sourceMarkdownToggleEnabled);
|
|
2468
|
+
const sourcePaneModeAttr = ` data-source-pane-mode="${shell.sourcePaneDefaultMode}"`;
|
|
1918
2469
|
return buildCodeBrowserPageHtml({
|
|
1919
2470
|
title,
|
|
1920
2471
|
metaDescriptionHtml,
|
|
1921
2472
|
generatorMetaHtml,
|
|
1922
2473
|
toolbarSiteHubHtml,
|
|
2474
|
+
shellPairIdentityDataAttrs: pairIdentityDataAttrs,
|
|
1923
2475
|
shellPairDocDataAttr: pairDocDataAttr,
|
|
1924
2476
|
angleSelectHtml: shell.angleSelectHtml,
|
|
1925
2477
|
toolbarDocHubHtml,
|
|
@@ -1939,6 +2491,9 @@ export async function renderCodeBrowserHtml(opts) {
|
|
|
1939
2491
|
searchPlaceholder,
|
|
1940
2492
|
shellSearchAttrs,
|
|
1941
2493
|
multiAngleScriptBlock: shell.multiAnglePayloadB64,
|
|
2494
|
+
sourceMarkdownToggleHtml: sourceMarkdownToggles.sourceMarkdownToggleHtml,
|
|
2495
|
+
sourceMarkdownFlipScrollAffordanceHtml: sourceMarkdownToggles.sourceMarkdownFlipScrollAffordanceHtml,
|
|
2496
|
+
sourcePaneModeAttr,
|
|
1942
2497
|
});
|
|
1943
2498
|
}
|
|
1944
2499
|
//# sourceMappingURL=code-browser.js.map
|