@entropicwarrior/sdoc 0.2.15 → 0.2.17
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/docs/reference/sdoc-authoring.sdoc +22 -0
- package/lexica/specification.sdoc +23 -1
- package/package.json +1 -1
- package/src/sdoc.js +125 -11
- package/src/slide-renderer.js +6 -1
|
@@ -333,6 +333,22 @@ Content of Section B.
|
|
|
333
333
|
|
|
334
334
|
The \`Amount\` column is right-aligned and formatted as currency. The formula result and data cells all display as \`$1,000,000\` style. The \`**Total**\` label renders as bold text.
|
|
335
335
|
}
|
|
336
|
+
|
|
337
|
+
# Copyable Columns
|
|
338
|
+
{
|
|
339
|
+
A \`copy\` token in the directive row gives every body cell in that column a click-to-copy icon (copying the cell's raw text). Ideal for columns of sequences, IDs, or commands.
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
{[table]
|
|
343
|
+
Gene | Sequence
|
|
344
|
+
| copy
|
|
345
|
+
BRCA1 | ACGTACGTGGCA
|
|
346
|
+
TP53 | GGCATTACGATT
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
\`copy\` can share a directive row with alignment/format directives on other columns.
|
|
351
|
+
}
|
|
336
352
|
}
|
|
337
353
|
|
|
338
354
|
# Table Formulas @table-formulas
|
|
@@ -384,8 +400,14 @@ Content of Section B.
|
|
|
384
400
|
\`\{!text!\}\` | Warning marker (orange)
|
|
385
401
|
\`\{-text-\}\` | Negative marker (red)
|
|
386
402
|
\`\{~text~\}\` | Highlight (yellow)
|
|
403
|
+
\`#1a73e8\` | Hex color swatch (auto-readable text)
|
|
404
|
+
\`\{copy\}text\{/copy\}\` | Copyable text with click-to-copy icon
|
|
387
405
|
}
|
|
388
406
|
|
|
407
|
+
Hex color codes (\`#rgb\`, \`#rgba\`, \`#rrggbb\`, \`#rrggbbaa\`) render as inline swatches — a rounded box filled with the color and labelled with the code, with the text color chosen automatically (black or white) for legibility. They are detected in normal text, list items, table cells, and headings, but not inside \`\\\`inline code\\\`\` (which stays literal). A bare \`#\` at the start of a line is a heading; escape with \`\\#\` to keep a literal hash.
|
|
408
|
+
|
|
409
|
+
Copyable text: \`\{copy\}ACGTACGTGGCA\{/copy\}\` renders the inner text verbatim in monospace with a copy icon; clicking it copies the text. Great for DNA/protein sequences, IDs, hashes, and commands. Escape with \`\\\{copy\}\` for a literal. Copy icons show in HTML output (preview, exported HTML, web viewer) and are hidden when printed/exported to PDF.
|
|
410
|
+
|
|
389
411
|
Links: \`[Link text](https://example.com)\` or \`[Other doc](./other-file.sdoc)\`. Relative paths resolve from the document's directory.
|
|
390
412
|
|
|
391
413
|
Images: \`\`
|
|
@@ -435,9 +435,25 @@ Content of Section B.
|
|
|
435
435
|
All numeric values and formula results in the `Amount` column display as currency (`$3,500,000`). The `**Total**` label renders normally as bold text.
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
# Copyable Columns @column-copy
|
|
439
|
+
{
|
|
440
|
+
A `copy` token (case-insensitive) in the directive row marks a column copyable: every body cell in that column is rendered with a click-to-copy icon whose target is the cell's raw text. Useful for sequences, identifiers, hashes, and commands.
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
{[table]
|
|
444
|
+
Gene | Sequence
|
|
445
|
+
| copy
|
|
446
|
+
BRCA1 | ACGTACGTGGCA
|
|
447
|
+
TP53 | GGCATTACGATT
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
The `copy` token occupies a directive cell of its own and may coexist with alignment/format directives on other columns in the same row. The literal (unformatted) cell text is copied. Copy icons are interactive HTML only — they appear in preview, exported HTML, and the web viewer, and are hidden in printed/PDF output.
|
|
452
|
+
}
|
|
453
|
+
|
|
438
454
|
# Directive Row Detection @directive-detection
|
|
439
455
|
{
|
|
440
|
-
A row is identified as a directive row when every non-empty cell contains only an alignment character (`<`, `>`, `=`), a format token, or
|
|
456
|
+
A row is identified as a directive row when every non-empty cell contains only an alignment character (`<`, `>`, `=`), a format token, the `copy` keyword, or a combination of alignment and format. It must be the first row after the header (or the first row in a headerless table), and at least one cell must contain a directive.
|
|
441
457
|
|
|
442
458
|
For headerless tables, the directive row functions as a virtual header — it is consumed for its directives but no `<thead>` is rendered.
|
|
443
459
|
}
|
|
@@ -719,12 +735,18 @@ Content of Section B.
|
|
|
719
735
|
- Warning marker: `{!text!}` (orange highlight)
|
|
720
736
|
- Negative marker: `{-text-}` (red highlight)
|
|
721
737
|
- Highlight: `{~text~}` (yellow highlight)
|
|
738
|
+
- Hex color swatch: `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa` (e.g. `#9aa0a8`) renders as a filled inline box labelled with the code
|
|
739
|
+
- Copyable text: `{copy}...{/copy}` renders the inner text verbatim (monospace) with a click-to-copy icon
|
|
722
740
|
- Citation reference: `[@key]` (numbered superscript link to citation entry)
|
|
723
741
|
- Multiple citation references: `[@key1, @key2]` (each individually linked)
|
|
724
742
|
}
|
|
725
743
|
|
|
726
744
|
Inline math requires non-whitespace immediately after the opening `$` and before the closing `$`. A plain `$` followed by a digit (e.g. `$100`) does not trigger math mode because there is no closing `$`.
|
|
727
745
|
|
|
746
|
+
Hex color swatches are detected only when the `#` is at the start of the inline text or preceded by a non-alphanumeric character, the digit run is exactly 3, 4, 6, or 8 hexadecimal digits, and it is not immediately followed by another letter, digit, or underscore. The background is the literal color and the text color (black or white) is chosen by perceptual brightness (YIQ); for `#rgba`/`#rrggbbaa` the alpha channel is ignored when choosing the text color. Hex inside `` `inline code` `` stays literal, and a `#` at the start of a line is parsed as a heading (escape with `\#`).
|
|
747
|
+
|
|
748
|
+
Copyable text spans from `{copy}` to the next unescaped `{/copy}`. The inner text is captured verbatim — no nested inline formatting is applied — and is both displayed (monospace) and used as the copy target. An unterminated `{copy}` (no closing `{/copy}`) falls through as literal text. Escape with `\{copy}` to write a literal. As with other `{`-delimited constructs, a `{copy}` at the very start of a line is subject to block-level parsing; use it mid-line or within a cell.
|
|
749
|
+
|
|
728
750
|
Citation references (`[@key]`) are parsed before link syntax. `[@key]` is always a citation reference even if followed by `(url)`. Use `\[@key]` for a literal `[@key]` in text.
|
|
729
751
|
}
|
|
730
752
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@entropicwarrior/sdoc",
|
|
3
3
|
"displayName": "SDOC - Docs for Human/Agent Teams",
|
|
4
4
|
"description": "A plain-text documentation format with explicit brace scoping — deterministic parsing, AI-agent efficiency, and 10-50x token savings vs Markdown.",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.17",
|
|
6
6
|
"publisher": "entropicwarrior-msenfin",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
package/src/sdoc.js
CHANGED
|
@@ -72,6 +72,7 @@ function isDirectiveRow(cells) {
|
|
|
72
72
|
for (const cell of cells) {
|
|
73
73
|
const trimmed = cell.trim();
|
|
74
74
|
if (trimmed === "") continue;
|
|
75
|
+
if (trimmed.toLowerCase() === "copy") { hasDirective = true; continue; }
|
|
75
76
|
if (!pattern.test(trimmed)) return false;
|
|
76
77
|
hasDirective = true;
|
|
77
78
|
}
|
|
@@ -102,12 +103,23 @@ function parseFormatSpec(spec) {
|
|
|
102
103
|
function parseDirectiveRow(cells) {
|
|
103
104
|
const align = [];
|
|
104
105
|
const format = [];
|
|
106
|
+
const copy = [];
|
|
105
107
|
let hasAlign = false;
|
|
106
108
|
let hasFormat = false;
|
|
109
|
+
let hasCopy = false;
|
|
107
110
|
const fmtPattern = /(\$(?:\.\d+)?|,(?:\.\d+)?|\.\d+|%(?:\.\d+)?)$/;
|
|
108
111
|
|
|
109
112
|
for (const cell of cells) {
|
|
110
113
|
const trimmed = cell.trim();
|
|
114
|
+
|
|
115
|
+
if (trimmed.toLowerCase() === "copy") {
|
|
116
|
+
align.push(null);
|
|
117
|
+
format.push(null);
|
|
118
|
+
copy.push(true);
|
|
119
|
+
hasCopy = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
111
123
|
const alignMatch = trimmed.match(/^([<>=])/);
|
|
112
124
|
const fmtMatch = trimmed.match(fmtPattern);
|
|
113
125
|
|
|
@@ -124,11 +136,13 @@ function parseDirectiveRow(cells) {
|
|
|
124
136
|
hasFormat = true;
|
|
125
137
|
}
|
|
126
138
|
format.push(f);
|
|
139
|
+
copy.push(false);
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
return {
|
|
130
143
|
align: hasAlign ? align : null,
|
|
131
144
|
format: hasFormat ? format : null,
|
|
145
|
+
copy: hasCopy ? copy : null,
|
|
132
146
|
};
|
|
133
147
|
}
|
|
134
148
|
|
|
@@ -940,10 +954,12 @@ function parseTableBody(cursor, tableStartLine, options) {
|
|
|
940
954
|
const directiveIndex = options.headerless ? 0 : 1;
|
|
941
955
|
let columnAlign = null;
|
|
942
956
|
let columnFormat = null;
|
|
957
|
+
let columnCopy = null;
|
|
943
958
|
if (rows.length > directiveIndex && isDirectiveRow(rows[directiveIndex])) {
|
|
944
959
|
const directives = parseDirectiveRow(rows[directiveIndex]);
|
|
945
960
|
columnAlign = directives.align;
|
|
946
961
|
columnFormat = directives.format;
|
|
962
|
+
columnCopy = directives.copy;
|
|
947
963
|
rows.splice(directiveIndex, 1);
|
|
948
964
|
}
|
|
949
965
|
|
|
@@ -960,6 +976,7 @@ function parseTableBody(cursor, tableStartLine, options) {
|
|
|
960
976
|
if (hasOptions) tableNode.options = options;
|
|
961
977
|
if (columnAlign) tableNode.columnAlign = columnAlign;
|
|
962
978
|
if (columnFormat) tableNode.columnFormat = columnFormat;
|
|
979
|
+
if (columnCopy) tableNode.columnCopy = columnCopy;
|
|
963
980
|
return tableNode;
|
|
964
981
|
}
|
|
965
982
|
|
|
@@ -1414,6 +1431,33 @@ function parseInline(text) {
|
|
|
1414
1431
|
}
|
|
1415
1432
|
}
|
|
1416
1433
|
|
|
1434
|
+
// Hex color codes (#rgb, #rgba, #rrggbb, #rrggbbaa) render as swatches.
|
|
1435
|
+
// Require a non-word char before the # so we don't match inside identifiers,
|
|
1436
|
+
// and a non-word char after the digits so partial/over-long runs are ignored.
|
|
1437
|
+
if (ch === "#" && (i === 0 || !/[0-9A-Za-z]/.test(text[i - 1]))) {
|
|
1438
|
+
const m = /^#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})(?![0-9A-Za-z_])/.exec(
|
|
1439
|
+
text.slice(i)
|
|
1440
|
+
);
|
|
1441
|
+
if (m) {
|
|
1442
|
+
flush();
|
|
1443
|
+
nodes.push({ type: "color_swatch", value: m[0] });
|
|
1444
|
+
i += m[0].length;
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// Copyable inline text: {copy}literal text{/copy}. The inner text is copied
|
|
1450
|
+
// verbatim (no nested formatting), rendered monospace with a copy icon.
|
|
1451
|
+
if (text.startsWith("{copy}", i)) {
|
|
1452
|
+
const end = findUnescaped(text, i + 6, "{/copy}");
|
|
1453
|
+
if (end !== -1) {
|
|
1454
|
+
flush();
|
|
1455
|
+
nodes.push({ type: "copyable", value: text.slice(i + 6, end) });
|
|
1456
|
+
i = end + 7; // length of "{/copy}"
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1417
1461
|
if (ch === "{") {
|
|
1418
1462
|
const mc = next;
|
|
1419
1463
|
let mt = null;
|
|
@@ -1728,6 +1772,46 @@ function escapeAttr(value) {
|
|
|
1728
1772
|
return escapeHtml(value).replace(/'/g, "'");
|
|
1729
1773
|
}
|
|
1730
1774
|
|
|
1775
|
+
// Expand a #rgb/#rgba/#rrggbb/#rrggbbaa hex string to [r, g, b] (alpha ignored).
|
|
1776
|
+
function hexToRgb(hex) {
|
|
1777
|
+
let h = hex.replace(/^#/, "");
|
|
1778
|
+
if (h.length === 3 || h.length === 4) {
|
|
1779
|
+
h = h.slice(0, 3).split("").map((c) => c + c).join("");
|
|
1780
|
+
} else {
|
|
1781
|
+
h = h.slice(0, 6);
|
|
1782
|
+
}
|
|
1783
|
+
return [
|
|
1784
|
+
parseInt(h.slice(0, 2), 16),
|
|
1785
|
+
parseInt(h.slice(2, 4), 16),
|
|
1786
|
+
parseInt(h.slice(4, 6), 16),
|
|
1787
|
+
];
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// Pick black or white text for legibility over the given background color
|
|
1791
|
+
// using the perceptual YIQ brightness threshold.
|
|
1792
|
+
function readableTextColor(hex) {
|
|
1793
|
+
const [r, g, b] = hexToRgb(hex);
|
|
1794
|
+
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1795
|
+
return yiq >= 128 ? "#000000" : "#ffffff";
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// Render a hex color as a self-contained inline swatch: a rounded box filled
|
|
1799
|
+
// with the color, labelled with the hex code in a legible text color. Styling
|
|
1800
|
+
// is inline so it survives in slides, exports, and other CSS-free contexts.
|
|
1801
|
+
function colorSwatchHtml(value) {
|
|
1802
|
+
const fg = readableTextColor(value);
|
|
1803
|
+
// A soft neutral-grey outline on every swatch (the same for all of them) so
|
|
1804
|
+
// the box edge stays visible even when the fill matches the page background.
|
|
1805
|
+
// Semi-transparent grey reads on both light and dark backgrounds without the
|
|
1806
|
+
// harsh contrast of a black/white border.
|
|
1807
|
+
const style =
|
|
1808
|
+
`background-color:${value};color:${fg};` +
|
|
1809
|
+
"border:1px solid rgba(128,128,128,0.5);border-radius:4px;padding:0.15em 0.9em;" +
|
|
1810
|
+
"font-family:'JetBrains Mono','Fira Code','Source Code Pro',monospace;font-size:0.95em;" +
|
|
1811
|
+
"-webkit-print-color-adjust:exact;print-color-adjust:exact";
|
|
1812
|
+
return `<span class="sdoc-color-swatch" style="${style}">${escapeHtml(value)}</span>`;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1731
1815
|
function renderInline(text) {
|
|
1732
1816
|
const nodes = parseInline(text);
|
|
1733
1817
|
return renderInlineNodes(nodes);
|
|
@@ -1766,6 +1850,10 @@ function renderInlineNodes(nodes) {
|
|
|
1766
1850
|
}
|
|
1767
1851
|
case "code":
|
|
1768
1852
|
return `<code class="sdoc-inline-code">${escapeHtml(node.value)}</code>`;
|
|
1853
|
+
case "copyable":
|
|
1854
|
+
return `<span class="sdoc-copyable"><code class="sdoc-inline-code">${escapeHtml(node.value)}</code><button class="sdoc-copy-btn sdoc-copy-inline" data-copy="${escapeAttr(node.value)}" title="Copy">⧉</button></span>`;
|
|
1855
|
+
case "color_swatch":
|
|
1856
|
+
return colorSwatchHtml(node.value);
|
|
1769
1857
|
case "em":
|
|
1770
1858
|
return `<em>${renderInlineNodes(node.children)}</em>`;
|
|
1771
1859
|
case "strong":
|
|
@@ -2247,6 +2335,14 @@ function renderTable(table) {
|
|
|
2247
2335
|
const opts = table.options || {};
|
|
2248
2336
|
const colAlign = table.columnAlign || [];
|
|
2249
2337
|
const colFormat = table.columnFormat || [];
|
|
2338
|
+
const colCopy = table.columnCopy || [];
|
|
2339
|
+
|
|
2340
|
+
// Copy icon appended to body cells in a `copy` directive column. data-copy
|
|
2341
|
+
// carries the raw cell text so the shared copy handler copies it verbatim.
|
|
2342
|
+
function copyBtn(cell, c) {
|
|
2343
|
+
if (!colCopy[c]) return "";
|
|
2344
|
+
return ` <button class="sdoc-copy-btn sdoc-copy-inline" data-copy="${escapeAttr(cell.trim())}" title="Copy">⧉</button>`;
|
|
2345
|
+
}
|
|
2250
2346
|
const classes = ["sdoc-table"];
|
|
2251
2347
|
if (opts.borderless) classes.push("sdoc-table-borderless");
|
|
2252
2348
|
if (opts.headerless) classes.push("sdoc-table-headerless");
|
|
@@ -2286,20 +2382,20 @@ function renderTable(table) {
|
|
|
2286
2382
|
}
|
|
2287
2383
|
const formula = escapeAttr(cell.trim());
|
|
2288
2384
|
if (result.error) {
|
|
2289
|
-
return `<td class="sdoc-table-td sdoc-formula-error"${style} title="${formula}">${display}</td>`;
|
|
2385
|
+
return `<td class="sdoc-table-td sdoc-formula-error"${style} title="${formula}">${display}${copyBtn(cell, c)}</td>`;
|
|
2290
2386
|
}
|
|
2291
|
-
return `<td class="sdoc-table-td sdoc-formula-cell"${style} title="${formula}">${display}</td>`;
|
|
2387
|
+
return `<td class="sdoc-table-td sdoc-formula-cell"${style} title="${formula}">${display}${copyBtn(cell, c)}</td>`;
|
|
2292
2388
|
}
|
|
2293
2389
|
|
|
2294
2390
|
// Apply column format to numeric data cells
|
|
2295
2391
|
if (fmt) {
|
|
2296
2392
|
const parsed = parseCellValue(cell);
|
|
2297
2393
|
if (!isNaN(parsed.value)) {
|
|
2298
|
-
return `<td class="sdoc-table-td"${style}>${escapeHtml(formatNumber(parsed.value, fmt))}</td>`;
|
|
2394
|
+
return `<td class="sdoc-table-td"${style}>${escapeHtml(formatNumber(parsed.value, fmt))}${copyBtn(cell, c)}</td>`;
|
|
2299
2395
|
}
|
|
2300
2396
|
}
|
|
2301
2397
|
|
|
2302
|
-
return `<td class="sdoc-table-td"${style}>${renderInline(cell)}</td>`;
|
|
2398
|
+
return `<td class="sdoc-table-td"${style}>${renderInline(cell)}${copyBtn(cell, c)}</td>`;
|
|
2303
2399
|
})
|
|
2304
2400
|
.join("");
|
|
2305
2401
|
return `<tr>${cells}</tr>`;
|
|
@@ -2995,10 +3091,6 @@ const PRINT_STYLE = `
|
|
|
2995
3091
|
position: relative;
|
|
2996
3092
|
}
|
|
2997
3093
|
.sdoc-copy-btn {
|
|
2998
|
-
position: absolute;
|
|
2999
|
-
top: 6px;
|
|
3000
|
-
right: 6px;
|
|
3001
|
-
z-index: 1;
|
|
3002
3094
|
background: transparent;
|
|
3003
3095
|
border: none;
|
|
3004
3096
|
border-radius: 4px;
|
|
@@ -3006,15 +3098,35 @@ const PRINT_STYLE = `
|
|
|
3006
3098
|
font-size: 0.85rem;
|
|
3007
3099
|
line-height: 1;
|
|
3008
3100
|
padding: 2px 6px;
|
|
3101
|
+
color: inherit;
|
|
3102
|
+
}
|
|
3103
|
+
.sdoc-code-wrap .sdoc-copy-btn {
|
|
3104
|
+
position: absolute;
|
|
3105
|
+
top: 6px;
|
|
3106
|
+
right: 6px;
|
|
3107
|
+
z-index: 1;
|
|
3009
3108
|
opacity: 0;
|
|
3010
3109
|
transition: opacity 0.15s;
|
|
3011
3110
|
}
|
|
3012
3111
|
.sdoc-code-wrap:hover .sdoc-copy-btn {
|
|
3013
3112
|
opacity: 0.75;
|
|
3014
3113
|
}
|
|
3015
|
-
.sdoc-copy-btn:hover {
|
|
3114
|
+
.sdoc-code-wrap .sdoc-copy-btn:hover {
|
|
3016
3115
|
opacity: 1 !important;
|
|
3017
3116
|
}
|
|
3117
|
+
.sdoc-copyable {
|
|
3118
|
+
white-space: nowrap;
|
|
3119
|
+
}
|
|
3120
|
+
.sdoc-copy-inline {
|
|
3121
|
+
font-size: 0.8em;
|
|
3122
|
+
padding: 0 0.15em;
|
|
3123
|
+
opacity: 0.45;
|
|
3124
|
+
vertical-align: baseline;
|
|
3125
|
+
transition: opacity 0.15s;
|
|
3126
|
+
}
|
|
3127
|
+
.sdoc-copy-inline:hover {
|
|
3128
|
+
opacity: 1;
|
|
3129
|
+
}
|
|
3018
3130
|
@media print {
|
|
3019
3131
|
html {
|
|
3020
3132
|
font-size: 80%;
|
|
@@ -3053,7 +3165,7 @@ const PRINT_STYLE = `
|
|
|
3053
3165
|
|
|
3054
3166
|
const COLLAPSE_SCRIPT = `document.addEventListener("click",function(e){if(!e.target.classList.contains("sdoc-toggle"))return;e.stopPropagation();var s=e.target.closest(".sdoc-scope");if(s)s.classList.toggle("sdoc-collapsed")});`;
|
|
3055
3167
|
|
|
3056
|
-
const COPY_SCRIPT = `document.addEventListener("click",function(e){
|
|
3168
|
+
const COPY_SCRIPT = `document.addEventListener("click",function(e){var b=e.target.closest(".sdoc-copy-btn");if(!b)return;e.stopPropagation();e.preventDefault();var t=b.getAttribute("data-copy");if(t===null){var w=b.closest(".sdoc-code-wrap");if(!w)return;var c=w.querySelector("code");if(!c)return;t=c.textContent}if(navigator.clipboard){navigator.clipboard.writeText(t).then(function(){b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)})}else{var a=document.createElement("textarea");a.value=t;a.style.position="fixed";a.style.opacity="0";document.body.appendChild(a);a.select();document.execCommand("copy");document.body.removeChild(a);b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)}});`;
|
|
3057
3169
|
|
|
3058
3170
|
const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js";
|
|
3059
3171
|
const KATEX_CDN_CSS = "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css";
|
|
@@ -3773,5 +3885,7 @@ module.exports = {
|
|
|
3773
3885
|
renderKatex,
|
|
3774
3886
|
escapeHtml,
|
|
3775
3887
|
escapeAttr,
|
|
3776
|
-
sanitizeSvg
|
|
3888
|
+
sanitizeSvg,
|
|
3889
|
+
colorSwatchHtml,
|
|
3890
|
+
readableTextColor
|
|
3777
3891
|
};
|
package/src/slide-renderer.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// const { nodes, meta } = extractMeta(parsed.nodes);
|
|
8
8
|
// const html = renderSlides(nodes, { meta, themeCss, themeJs });
|
|
9
9
|
|
|
10
|
-
const { parseInline, renderKatex, escapeHtml, escapeAttr, sanitizeSvg } = require("./sdoc");
|
|
10
|
+
const { parseInline, renderKatex, escapeHtml, escapeAttr, sanitizeSvg, colorSwatchHtml } = require("./sdoc");
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Inline rendering — produces clean HTML without sdoc-* classes
|
|
@@ -21,6 +21,11 @@ function renderInlineNodes(nodes) {
|
|
|
21
21
|
return escapeHtml(node.value);
|
|
22
22
|
case "code":
|
|
23
23
|
return `<code>${escapeHtml(node.value)}</code>`;
|
|
24
|
+
case "copyable":
|
|
25
|
+
// Slides have no copy handler; render the literal text as monospace.
|
|
26
|
+
return `<code>${escapeHtml(node.value)}</code>`;
|
|
27
|
+
case "color_swatch":
|
|
28
|
+
return colorSwatchHtml(node.value);
|
|
24
29
|
case "em":
|
|
25
30
|
return `<em>${renderInlineNodes(node.children)}</em>`;
|
|
26
31
|
case "strong":
|