vantage-md 0.1.3 → 0.5.4
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 +24 -9
- package/dist/index.cjs +862 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +440 -14
- package/dist/index.d.ts +440 -14
- package/dist/index.js +840 -47
- package/dist/index.js.map +1 -1
- package/dist/prose.css +2 -1
- package/dist/react.cjs +1063 -117
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +260 -14
- package/dist/react.d.ts +260 -14
- package/dist/react.js +1056 -118
- package/dist/react.js.map +1 -1
- package/dist/styles.css +240 -0
- package/package.json +18 -8
package/dist/index.cjs
CHANGED
|
@@ -2,30 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
var unified = require('unified');
|
|
4
4
|
var remarkParse = require('remark-parse');
|
|
5
|
+
var remarkRehype = require('remark-rehype');
|
|
6
|
+
var rehypeStringify = require('rehype-stringify');
|
|
5
7
|
var remarkGfm = require('remark-gfm');
|
|
6
8
|
var remarkMath = require('remark-math');
|
|
7
|
-
var remarkRehype = require('remark-rehype');
|
|
8
9
|
var rehypeRaw = require('rehype-raw');
|
|
9
10
|
var rehypeSanitize = require('rehype-sanitize');
|
|
10
11
|
var rehypeHighlight = require('rehype-highlight');
|
|
11
12
|
var rehypeKatex = require('rehype-katex');
|
|
12
13
|
var rehypeSlug = require('rehype-slug');
|
|
13
|
-
var rehypeStringify = require('rehype-stringify');
|
|
14
14
|
var YAML = require('yaml');
|
|
15
15
|
var smolToml = require('smol-toml');
|
|
16
16
|
|
|
17
17
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
18
|
|
|
19
19
|
var remarkParse__default = /*#__PURE__*/_interopDefault(remarkParse);
|
|
20
|
+
var remarkRehype__default = /*#__PURE__*/_interopDefault(remarkRehype);
|
|
21
|
+
var rehypeStringify__default = /*#__PURE__*/_interopDefault(rehypeStringify);
|
|
20
22
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
21
23
|
var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
|
|
22
|
-
var remarkRehype__default = /*#__PURE__*/_interopDefault(remarkRehype);
|
|
23
24
|
var rehypeRaw__default = /*#__PURE__*/_interopDefault(rehypeRaw);
|
|
24
25
|
var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
|
|
25
26
|
var rehypeHighlight__default = /*#__PURE__*/_interopDefault(rehypeHighlight);
|
|
26
27
|
var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
27
28
|
var rehypeSlug__default = /*#__PURE__*/_interopDefault(rehypeSlug);
|
|
28
|
-
var rehypeStringify__default = /*#__PURE__*/_interopDefault(rehypeStringify);
|
|
29
29
|
var YAML__default = /*#__PURE__*/_interopDefault(YAML);
|
|
30
30
|
|
|
31
31
|
// src/renderMarkdown.ts
|
|
@@ -49,25 +49,511 @@ var BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
|
49
49
|
"hr",
|
|
50
50
|
"div"
|
|
51
51
|
]);
|
|
52
|
-
function visit(node) {
|
|
52
|
+
function visit(node, offset) {
|
|
53
53
|
if ("children" in node) {
|
|
54
54
|
for (const child of node.children) {
|
|
55
55
|
if (child.type === "element") {
|
|
56
56
|
if (BLOCK_TAGS.has(child.tagName) && child.position?.start?.line) {
|
|
57
57
|
child.properties = child.properties || {};
|
|
58
|
-
child.properties["dataSourceLine"] = child.position.start.line;
|
|
58
|
+
child.properties["dataSourceLine"] = child.position.start.line + offset;
|
|
59
59
|
}
|
|
60
|
-
visit(child);
|
|
60
|
+
visit(child, offset);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
var rehypeSourceLines = () => {
|
|
65
|
+
var rehypeSourceLines = (options) => {
|
|
66
|
+
const offset = options?.offset ?? 0;
|
|
66
67
|
return (tree) => {
|
|
67
|
-
visit(tree);
|
|
68
|
+
visit(tree, offset);
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
var rehypeSourceLines_default = rehypeSourceLines;
|
|
72
|
+
|
|
73
|
+
// src/vantageDirectives.ts
|
|
74
|
+
var VANTAGE_SENTINEL = "vantage:";
|
|
75
|
+
var DIRECTIVE_NAMES = ["section", "block", "oq"];
|
|
76
|
+
var VANTAGE_TONES = [
|
|
77
|
+
"note",
|
|
78
|
+
"tip",
|
|
79
|
+
"important",
|
|
80
|
+
"warning",
|
|
81
|
+
"caution",
|
|
82
|
+
"muted"
|
|
83
|
+
];
|
|
84
|
+
var VANTAGE_EMPHASIS = ["strong", "normal", "quiet"];
|
|
85
|
+
var VANTAGE_BADGES = [
|
|
86
|
+
"draft",
|
|
87
|
+
"stale",
|
|
88
|
+
"blocked",
|
|
89
|
+
"done",
|
|
90
|
+
"wip"
|
|
91
|
+
];
|
|
92
|
+
var VANTAGE_COLLAPSED = ["true", "false"];
|
|
93
|
+
var VANTAGE_RUNS = ["start", "middle", "end", "only"];
|
|
94
|
+
var VANTAGE_STYLE_TARGETS = [
|
|
95
|
+
"p",
|
|
96
|
+
"h1",
|
|
97
|
+
"h2",
|
|
98
|
+
"h3",
|
|
99
|
+
"h4",
|
|
100
|
+
"h5",
|
|
101
|
+
"h6",
|
|
102
|
+
"li",
|
|
103
|
+
"blockquote",
|
|
104
|
+
"pre",
|
|
105
|
+
"table",
|
|
106
|
+
"tr",
|
|
107
|
+
"ul",
|
|
108
|
+
"ol",
|
|
109
|
+
"hr",
|
|
110
|
+
"div"
|
|
111
|
+
];
|
|
112
|
+
var VANTAGE_ANCHOR_TARGETS = [
|
|
113
|
+
"p",
|
|
114
|
+
"h1",
|
|
115
|
+
"h2",
|
|
116
|
+
"h3",
|
|
117
|
+
"h4",
|
|
118
|
+
"h5",
|
|
119
|
+
"h6",
|
|
120
|
+
"li",
|
|
121
|
+
"blockquote",
|
|
122
|
+
"pre",
|
|
123
|
+
"table"
|
|
124
|
+
];
|
|
125
|
+
var VANTAGE_OQ_HOST_TARGETS = VANTAGE_ANCHOR_TARGETS.filter(
|
|
126
|
+
(tag) => tag !== "pre" && tag !== "table"
|
|
127
|
+
);
|
|
128
|
+
var STYLE_KEYS = {
|
|
129
|
+
tone: VANTAGE_TONES,
|
|
130
|
+
emphasis: VANTAGE_EMPHASIS,
|
|
131
|
+
badge: VANTAGE_BADGES,
|
|
132
|
+
collapsed: VANTAGE_COLLAPSED
|
|
133
|
+
};
|
|
134
|
+
var DIRECTIVE_VOCABULARY = {
|
|
135
|
+
section: STYLE_KEYS,
|
|
136
|
+
block: STYLE_KEYS,
|
|
137
|
+
oq: { id: null, leaning: null }
|
|
138
|
+
};
|
|
139
|
+
var WS = /[ \t\r\n]*/y;
|
|
140
|
+
var SENTINEL_PREFIX = /^[ \t\r\n]*vantage:/;
|
|
141
|
+
var NAME = /[a-z][a-z0-9-]*/y;
|
|
142
|
+
var UNQUOTED = /[A-Za-z0-9_.:#-]+/y;
|
|
143
|
+
var QUOTED = /"[^"]*"/y;
|
|
144
|
+
function hasVantageSentinel(comment) {
|
|
145
|
+
return SENTINEL_PREFIX.test(comment);
|
|
146
|
+
}
|
|
147
|
+
function token(comment, offset) {
|
|
148
|
+
const rest = comment.slice(offset);
|
|
149
|
+
const end = rest.search(/[ \t\r\n]/);
|
|
150
|
+
const word = end === -1 ? rest : rest.slice(0, end);
|
|
151
|
+
return word.length > 24 ? `${word.slice(0, 24)}\u2026` : word;
|
|
152
|
+
}
|
|
153
|
+
function matchAt(pattern, comment, offset) {
|
|
154
|
+
pattern.lastIndex = offset;
|
|
155
|
+
const match = pattern.exec(comment);
|
|
156
|
+
return match === null ? null : match[0];
|
|
157
|
+
}
|
|
158
|
+
function skipWhitespace(comment, offset) {
|
|
159
|
+
return matchAt(WS, comment, offset)?.length ?? 0;
|
|
160
|
+
}
|
|
161
|
+
function malformed(reason, offset) {
|
|
162
|
+
return { kind: "malformed", reason, offset };
|
|
163
|
+
}
|
|
164
|
+
function parseVantageDirective(comment) {
|
|
165
|
+
const sentinel = SENTINEL_PREFIX.exec(comment);
|
|
166
|
+
if (sentinel === null) return null;
|
|
167
|
+
let at = sentinel[0].length;
|
|
168
|
+
at += skipWhitespace(comment, at);
|
|
169
|
+
const nameOffset = at;
|
|
170
|
+
const name = matchAt(NAME, comment, at);
|
|
171
|
+
if (name === null) {
|
|
172
|
+
return malformed("no directive name after `vantage:`", at);
|
|
173
|
+
}
|
|
174
|
+
at += name.length;
|
|
175
|
+
const pairs = [];
|
|
176
|
+
while (at < comment.length) {
|
|
177
|
+
const gap = skipWhitespace(comment, at);
|
|
178
|
+
at += gap;
|
|
179
|
+
if (at >= comment.length) break;
|
|
180
|
+
if (gap === 0) {
|
|
181
|
+
return malformed(`\`${token(comment, at)}\` needs a space before it`, at);
|
|
182
|
+
}
|
|
183
|
+
const keyOffset = at;
|
|
184
|
+
const key = matchAt(NAME, comment, at);
|
|
185
|
+
if (key === null) {
|
|
186
|
+
return malformed(
|
|
187
|
+
`\`${token(comment, at)}\` is not a \`key=value\` pair`,
|
|
188
|
+
at
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
at += key.length;
|
|
192
|
+
if (comment[at] !== "=") {
|
|
193
|
+
return malformed(`\`${key}\` is not followed by \`=value\``, at);
|
|
194
|
+
}
|
|
195
|
+
at += 1;
|
|
196
|
+
const valueOffset = at;
|
|
197
|
+
const quoted = matchAt(QUOTED, comment, at);
|
|
198
|
+
if (quoted !== null) {
|
|
199
|
+
at += quoted.length;
|
|
200
|
+
pairs.push({
|
|
201
|
+
key,
|
|
202
|
+
value: quoted.slice(1, -1),
|
|
203
|
+
keyOffset,
|
|
204
|
+
valueOffset,
|
|
205
|
+
quoted: true
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const unquoted = matchAt(UNQUOTED, comment, at);
|
|
210
|
+
if (unquoted === null) {
|
|
211
|
+
const found = token(comment, at);
|
|
212
|
+
return malformed(
|
|
213
|
+
found === "" ? `\`${key}=\` has no value` : `\`${found}\` is not a valid value for \`${key}\``,
|
|
214
|
+
at
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
at += unquoted.length;
|
|
218
|
+
pairs.push({ key, value: unquoted, keyOffset, valueOffset, quoted: false });
|
|
219
|
+
}
|
|
220
|
+
return { kind: "directive", name, nameOffset, pairs };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/rehypeVantageDirectives.ts
|
|
224
|
+
var STYLE_TARGET_TAGS = new Set(VANTAGE_STYLE_TARGETS);
|
|
225
|
+
var ANCHOR_TARGET_TAGS = new Set(VANTAGE_ANCHOR_TARGETS);
|
|
226
|
+
var HEADING_DEPTHS = /* @__PURE__ */ new Map([
|
|
227
|
+
["h1", 1],
|
|
228
|
+
["h2", 2],
|
|
229
|
+
["h3", 3],
|
|
230
|
+
["h4", 4],
|
|
231
|
+
["h5", 5],
|
|
232
|
+
["h6", 6]
|
|
233
|
+
]);
|
|
234
|
+
var RANGE_PROPERTIES = /* @__PURE__ */ new Map([
|
|
235
|
+
["tone", "dataVantageTone"],
|
|
236
|
+
["emphasis", "dataVantageEmphasis"]
|
|
237
|
+
]);
|
|
238
|
+
var POINT_PROPERTIES = /* @__PURE__ */ new Map([["badge", "dataVantageBadge"]]);
|
|
239
|
+
var RUN_PROPERTY = "dataVantageRun";
|
|
240
|
+
var OQ_PROPERTY = "dataVantageOq";
|
|
241
|
+
var LEANING_PROPERTY = "dataVantageLeaning";
|
|
242
|
+
var COLLAPSED_PROPERTY = "dataVantageCollapsed";
|
|
243
|
+
var COLLAPSE_GROUP_PROPERTY = "dataVantageCollapseGroup";
|
|
244
|
+
var COLLAPSE_TOGGLE_PROPERTY = "dataVantageCollapseToggle";
|
|
245
|
+
var MAX_LEANING = 500;
|
|
246
|
+
function isSkippable(node) {
|
|
247
|
+
if (node.type === "comment" || node.type === "doctype") return true;
|
|
248
|
+
if (node.type === "text") return node.value.trim() === "";
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
function headingDepth(node) {
|
|
252
|
+
if (node.type !== "element") return void 0;
|
|
253
|
+
return HEADING_DEPTHS.get(node.tagName);
|
|
254
|
+
}
|
|
255
|
+
function setProperty(element, property, value) {
|
|
256
|
+
element.properties = element.properties ?? {};
|
|
257
|
+
element.properties[property] = value;
|
|
258
|
+
}
|
|
259
|
+
function runValue(index, length) {
|
|
260
|
+
if (length === 1) return "only";
|
|
261
|
+
if (index === 0) return "start";
|
|
262
|
+
return index === length - 1 ? "end" : "middle";
|
|
263
|
+
}
|
|
264
|
+
function vocabularyOf(name, key) {
|
|
265
|
+
return DIRECTIVE_VOCABULARY[name]?.[key];
|
|
266
|
+
}
|
|
267
|
+
function accepts(name, key, value) {
|
|
268
|
+
const values = vocabularyOf(name, key);
|
|
269
|
+
if (values === void 0) return false;
|
|
270
|
+
return values === null || values.includes(value);
|
|
271
|
+
}
|
|
272
|
+
function styleRange(children, targetIndex, name) {
|
|
273
|
+
const range = [targetIndex];
|
|
274
|
+
const depth = name === "section" ? headingDepth(children[targetIndex]) : void 0;
|
|
275
|
+
if (depth === void 0) return range;
|
|
276
|
+
for (let i = targetIndex + 1; i < children.length; i++) {
|
|
277
|
+
const node = children[i];
|
|
278
|
+
const nodeDepth = headingDepth(node);
|
|
279
|
+
if (nodeDepth !== void 0 && nodeDepth <= depth) break;
|
|
280
|
+
if (node.type === "element" && STYLE_TARGET_TAGS.has(node.tagName)) {
|
|
281
|
+
range.push(i);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return range;
|
|
285
|
+
}
|
|
286
|
+
function collapsesSection(name, pairs, target) {
|
|
287
|
+
if (name !== "section") return false;
|
|
288
|
+
if (pairs.get("collapsed") !== "true") return false;
|
|
289
|
+
return headingDepth(target) !== void 0;
|
|
290
|
+
}
|
|
291
|
+
function stampStyle(children, targetIndex, name, pairs, state) {
|
|
292
|
+
const target = children[targetIndex];
|
|
293
|
+
if (!STYLE_TARGET_TAGS.has(target.tagName)) return;
|
|
294
|
+
const rangeStamps = [];
|
|
295
|
+
const targetStamps = [];
|
|
296
|
+
for (const [key, value] of pairs) {
|
|
297
|
+
if (!accepts(name, key, value)) continue;
|
|
298
|
+
const rangeProperty = RANGE_PROPERTIES.get(key);
|
|
299
|
+
if (rangeProperty !== void 0) {
|
|
300
|
+
rangeStamps.push([rangeProperty, value]);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const pointProperty = POINT_PROPERTIES.get(key);
|
|
304
|
+
if (pointProperty !== void 0) targetStamps.push([pointProperty, value]);
|
|
305
|
+
}
|
|
306
|
+
const collapses = collapsesSection(name, pairs, target);
|
|
307
|
+
if (rangeStamps.length === 0 && targetStamps.length === 0 && !collapses) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const range = styleRange(children, targetIndex, name);
|
|
311
|
+
const group = collapses && range.length > 1 ? String(state.nextGroup++) : void 0;
|
|
312
|
+
for (let i = 0; i < range.length; i++) {
|
|
313
|
+
const element = children[range[i]];
|
|
314
|
+
for (const [property, value] of rangeStamps) {
|
|
315
|
+
setProperty(element, property, value);
|
|
316
|
+
}
|
|
317
|
+
if (i === 0) {
|
|
318
|
+
for (const [property, value] of targetStamps) {
|
|
319
|
+
setProperty(element, property, value);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (rangeStamps.length > 0) {
|
|
323
|
+
setProperty(element, RUN_PROPERTY, runValue(i, range.length));
|
|
324
|
+
}
|
|
325
|
+
if (group === void 0) continue;
|
|
326
|
+
if (i === 0) {
|
|
327
|
+
setProperty(element, COLLAPSE_TOGGLE_PROPERTY, group);
|
|
328
|
+
} else {
|
|
329
|
+
setProperty(element, COLLAPSED_PROPERTY, "true");
|
|
330
|
+
setProperty(element, COLLAPSE_GROUP_PROPERTY, group);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function stampOq(target, pairs) {
|
|
335
|
+
setProperty(target, OQ_PROPERTY, "true");
|
|
336
|
+
const leaning = pairs.get("leaning");
|
|
337
|
+
if (leaning === void 0) return;
|
|
338
|
+
const text = leaning.replace(/\s+/g, " ").trim().slice(0, MAX_LEANING);
|
|
339
|
+
if (text !== "") setProperty(target, LEANING_PROPERTY, text);
|
|
340
|
+
}
|
|
341
|
+
function stampRun(children, targetIndex, run, state) {
|
|
342
|
+
const target = children[targetIndex];
|
|
343
|
+
const style = /* @__PURE__ */ new Map();
|
|
344
|
+
const oq = /* @__PURE__ */ new Map();
|
|
345
|
+
let styleName;
|
|
346
|
+
let hasOq = false;
|
|
347
|
+
for (const directive of run) {
|
|
348
|
+
if (directive.name === "section" || directive.name === "block") {
|
|
349
|
+
styleName = directive.name;
|
|
350
|
+
for (const pair of directive.pairs) style.set(pair.key, pair.value);
|
|
351
|
+
} else if (directive.name === "oq") {
|
|
352
|
+
hasOq = true;
|
|
353
|
+
for (const pair of directive.pairs) oq.set(pair.key, pair.value);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (styleName !== void 0) {
|
|
357
|
+
stampStyle(children, targetIndex, styleName, style, state);
|
|
358
|
+
}
|
|
359
|
+
if (hasOq && ANCHOR_TARGET_TAGS.has(target.tagName)) {
|
|
360
|
+
stampOq(target, oq);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function directiveOf(node) {
|
|
364
|
+
if (node.type !== "comment") return void 0;
|
|
365
|
+
const parsed = parseVantageDirective(node.value);
|
|
366
|
+
return parsed !== null && parsed.kind === "directive" ? parsed : void 0;
|
|
367
|
+
}
|
|
368
|
+
function processChildren(parent, state) {
|
|
369
|
+
const children = parent.children;
|
|
370
|
+
let i = 0;
|
|
371
|
+
while (i < children.length) {
|
|
372
|
+
const node = children[i];
|
|
373
|
+
if (node.type === "element") {
|
|
374
|
+
processChildren(node, state);
|
|
375
|
+
i++;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const first = directiveOf(node);
|
|
379
|
+
if (first === void 0) {
|
|
380
|
+
i++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
const run = [first];
|
|
384
|
+
let j = i + 1;
|
|
385
|
+
let targetIndex = -1;
|
|
386
|
+
for (; j < children.length; j++) {
|
|
387
|
+
const next = children[j];
|
|
388
|
+
if (next.type === "element") {
|
|
389
|
+
targetIndex = j;
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
if (!isSkippable(next)) break;
|
|
393
|
+
const directive = directiveOf(next);
|
|
394
|
+
if (directive !== void 0) run.push(directive);
|
|
395
|
+
}
|
|
396
|
+
if (targetIndex >= 0) stampRun(children, targetIndex, run, state);
|
|
397
|
+
i = j;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
var rehypeVantageDirectives = () => {
|
|
401
|
+
return (tree) => {
|
|
402
|
+
processChildren(tree, { nextGroup: 1 });
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
var rehypeVantageDirectives_default = rehypeVantageDirectives;
|
|
406
|
+
|
|
407
|
+
// src/rehypeVantageMathStamps.ts
|
|
408
|
+
var CARRIED_KEY = "vantageDisplayMathStamps";
|
|
409
|
+
function classNames(node) {
|
|
410
|
+
const value = node.properties?.className;
|
|
411
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
412
|
+
}
|
|
413
|
+
function isDisplayMath(node) {
|
|
414
|
+
if (node.type !== "element" || node.tagName !== "pre") return false;
|
|
415
|
+
return node.children.some(
|
|
416
|
+
(child) => child.type === "element" && child.tagName === "code" && classNames(child).includes("language-math")
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
function carriedProperties(properties) {
|
|
420
|
+
const carried = {};
|
|
421
|
+
for (const [key, value] of Object.entries(properties ?? {})) {
|
|
422
|
+
if (key === "dataSourceLine" || key.startsWith("dataVantage")) {
|
|
423
|
+
carried[key] = value;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return carried;
|
|
427
|
+
}
|
|
428
|
+
function collect(parent, out) {
|
|
429
|
+
const children = parent.children;
|
|
430
|
+
for (let i = 0; i < children.length; i++) {
|
|
431
|
+
const node = children[i];
|
|
432
|
+
if (node.type !== "element") continue;
|
|
433
|
+
if (isDisplayMath(node)) {
|
|
434
|
+
const properties = carriedProperties(node.properties);
|
|
435
|
+
if (Object.keys(properties).length > 0) {
|
|
436
|
+
out.push({
|
|
437
|
+
parent,
|
|
438
|
+
anchor: i === 0 ? void 0 : children[i - 1],
|
|
439
|
+
properties
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
collect(node, out);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function reapply(carried) {
|
|
448
|
+
for (const { parent, anchor, properties } of carried) {
|
|
449
|
+
const siblings = parent.children;
|
|
450
|
+
let index = 0;
|
|
451
|
+
if (anchor !== void 0) {
|
|
452
|
+
const at = siblings.indexOf(anchor);
|
|
453
|
+
if (at === -1) continue;
|
|
454
|
+
index = at + 1;
|
|
455
|
+
}
|
|
456
|
+
const replacement = siblings[index];
|
|
457
|
+
if (replacement === void 0 || replacement.type !== "element") continue;
|
|
458
|
+
if (!classNames(replacement).some((name) => name.startsWith("katex"))) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
replacement.properties ??= {};
|
|
462
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
463
|
+
replacement.properties[key] ??= value;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
var rehypeCaptureMathStamps = () => {
|
|
468
|
+
return (tree, file) => {
|
|
469
|
+
const carried = [];
|
|
470
|
+
collect(tree, carried);
|
|
471
|
+
file.data[CARRIED_KEY] = carried;
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
var rehypeRestoreMathStamps = () => {
|
|
475
|
+
return (_tree, file) => {
|
|
476
|
+
const carried = file.data[CARRIED_KEY];
|
|
477
|
+
delete file.data[CARRIED_KEY];
|
|
478
|
+
if (Array.isArray(carried)) reapply(carried);
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
var SAFE_STYLE_PROPERTIES = [
|
|
482
|
+
// Box metrics. `top`/`right`/`bottom`/`left` are inert now that `position` is
|
|
483
|
+
// banned, and they stay only because dropping them would fail the whole
|
|
484
|
+
// attribute for a document that writes one — the all-or-nothing rule below
|
|
485
|
+
// makes every removal a behaviour change. They buy an attacker nothing that
|
|
486
|
+
// negative `margin` does not already buy.
|
|
487
|
+
"height",
|
|
488
|
+
"min-height",
|
|
489
|
+
"max-height",
|
|
490
|
+
"width",
|
|
491
|
+
"min-width",
|
|
492
|
+
"max-width",
|
|
493
|
+
"top",
|
|
494
|
+
"bottom",
|
|
495
|
+
"left",
|
|
496
|
+
"right",
|
|
497
|
+
"margin",
|
|
498
|
+
"margin-top",
|
|
499
|
+
"margin-right",
|
|
500
|
+
"margin-bottom",
|
|
501
|
+
"margin-left",
|
|
502
|
+
"padding",
|
|
503
|
+
"padding-top",
|
|
504
|
+
"padding-right",
|
|
505
|
+
"padding-bottom",
|
|
506
|
+
"padding-left",
|
|
507
|
+
// Rules and boxes.
|
|
508
|
+
"border",
|
|
509
|
+
"border-style",
|
|
510
|
+
"border-color",
|
|
511
|
+
"border-width",
|
|
512
|
+
"border-top-width",
|
|
513
|
+
"border-right-width",
|
|
514
|
+
"border-bottom-width",
|
|
515
|
+
"border-left-width",
|
|
516
|
+
"border-top-style",
|
|
517
|
+
"border-right-style",
|
|
518
|
+
"border-bottom-style",
|
|
519
|
+
"border-left-style",
|
|
520
|
+
"border-top-color",
|
|
521
|
+
"border-right-color",
|
|
522
|
+
"border-bottom-color",
|
|
523
|
+
"border-left-color",
|
|
524
|
+
"border-radius",
|
|
525
|
+
// Typography.
|
|
526
|
+
"color",
|
|
527
|
+
"background-color",
|
|
528
|
+
"font",
|
|
529
|
+
"font-size",
|
|
530
|
+
"font-style",
|
|
531
|
+
"font-weight",
|
|
532
|
+
"font-family",
|
|
533
|
+
"font-variant",
|
|
534
|
+
"line-height",
|
|
535
|
+
"letter-spacing",
|
|
536
|
+
"word-spacing",
|
|
537
|
+
"text-align",
|
|
538
|
+
"text-decoration",
|
|
539
|
+
"text-indent",
|
|
540
|
+
"white-space",
|
|
541
|
+
"vertical-align",
|
|
542
|
+
"list-style-type",
|
|
543
|
+
// Flow.
|
|
544
|
+
"display",
|
|
545
|
+
"float",
|
|
546
|
+
"clear",
|
|
547
|
+
"opacity",
|
|
548
|
+
"overflow"
|
|
549
|
+
];
|
|
550
|
+
var VALUE = `[^;:()"'\\\\]*`;
|
|
551
|
+
var DECLARATION = `(?:(?:${SAFE_STYLE_PROPERTIES.join("|")})\\s*:${VALUE})`;
|
|
552
|
+
var SAFE_STYLE = new RegExp(
|
|
553
|
+
`^\\s*(?:${DECLARATION};\\s*)*${DECLARATION}?$`,
|
|
554
|
+
"i"
|
|
555
|
+
);
|
|
556
|
+
var COLLAPSE_GROUP_ID = /^[0-9]+$/;
|
|
71
557
|
var sanitizeSchema = {
|
|
72
558
|
...rehypeSanitize.defaultSchema,
|
|
73
559
|
tagNames: [
|
|
@@ -103,20 +589,94 @@ var sanitizeSchema = {
|
|
|
103
589
|
"*": [
|
|
104
590
|
...rehypeSanitize.defaultSchema.attributes?.["*"] || [],
|
|
105
591
|
"className",
|
|
106
|
-
"style",
|
|
107
|
-
"dataSourceLine"
|
|
592
|
+
["style", SAFE_STYLE],
|
|
593
|
+
"dataSourceLine",
|
|
594
|
+
// What `rehypeVantageDirectives` compiles a `<!-- vantage: … -->` comment
|
|
595
|
+
// into, named individually — never by a `data-vantage-*` wildcard, which
|
|
596
|
+
// would readmit whatever a future bug emits and whatever a document
|
|
597
|
+
// hand-writes as raw HTML.
|
|
598
|
+
//
|
|
599
|
+
// The value lists are the belt to the plugin's braces: the vocabulary is
|
|
600
|
+
// closed in the plugin *and* here, imported from the one module that
|
|
601
|
+
// defines it, so even if a refactor let an unvalidated value reach the
|
|
602
|
+
// tree the sanitiser still refuses it.
|
|
603
|
+
["dataVantageTone", ...VANTAGE_TONES],
|
|
604
|
+
["dataVantageEmphasis", ...VANTAGE_EMPHASIS],
|
|
605
|
+
["dataVantageBadge", ...VANTAGE_BADGES],
|
|
606
|
+
["dataVantageCollapsed", ...VANTAGE_COLLAPSED],
|
|
607
|
+
// The other half of `collapsed`: which group a hidden block belongs to,
|
|
608
|
+
// and which group a heading toggles. Both are plugin-minted counters with
|
|
609
|
+
// no vocabulary to allowlist, so they take a pattern instead —
|
|
610
|
+
// `hast-util-sanitize` accepts a `RegExp` in place of a literal value.
|
|
611
|
+
// A pattern rather than a bare name because the JS interpolates the value
|
|
612
|
+
// into a selector: anything but digits has no business reaching it.
|
|
613
|
+
["dataVantageCollapseGroup", COLLAPSE_GROUP_ID],
|
|
614
|
+
["dataVantageCollapseToggle", COLLAPSE_GROUP_ID],
|
|
615
|
+
["dataVantageRun", ...VANTAGE_RUNS],
|
|
616
|
+
["dataVantageOq", "true"],
|
|
617
|
+
// The design's one genuinely free-text value: the body of a review
|
|
618
|
+
// comment, so it cannot be value-allowlisted and this entry is name-only.
|
|
619
|
+
// Two defences remain rather than three — `hast` escapes the value on
|
|
620
|
+
// serialisation and React sets it through the DOM property path, so it
|
|
621
|
+
// cannot break out of the attribute — and the honest record of that is in
|
|
622
|
+
// the design doc rather than a third layer implied here.
|
|
623
|
+
"dataVantageLeaning"
|
|
108
624
|
],
|
|
109
625
|
code: [...rehypeSanitize.defaultSchema.attributes?.code || [], "className"],
|
|
110
|
-
span: [
|
|
111
|
-
|
|
626
|
+
span: [
|
|
627
|
+
...rehypeSanitize.defaultSchema.attributes?.span || [],
|
|
628
|
+
"className",
|
|
629
|
+
["style", SAFE_STYLE]
|
|
630
|
+
],
|
|
631
|
+
div: [
|
|
632
|
+
...rehypeSanitize.defaultSchema.attributes?.div || [],
|
|
633
|
+
"className",
|
|
634
|
+
["style", SAFE_STYLE]
|
|
635
|
+
],
|
|
112
636
|
a: [...rehypeSanitize.defaultSchema.attributes?.a || [], "id", "className"],
|
|
113
637
|
math: ["xmlns"],
|
|
114
638
|
annotation: ["encoding"],
|
|
115
639
|
img: [...rehypeSanitize.defaultSchema.attributes?.img || [], "loading"],
|
|
116
|
-
td: [...rehypeSanitize.defaultSchema.attributes?.td || [], "style"],
|
|
117
|
-
th: [...rehypeSanitize.defaultSchema.attributes?.th || [], "style"]
|
|
640
|
+
td: [...rehypeSanitize.defaultSchema.attributes?.td || [], ["style", SAFE_STYLE]],
|
|
641
|
+
th: [...rehypeSanitize.defaultSchema.attributes?.th || [], ["style", SAFE_STYLE]]
|
|
118
642
|
}
|
|
119
643
|
};
|
|
644
|
+
|
|
645
|
+
// src/pipeline.ts
|
|
646
|
+
function buildRemarkPlugins(options = {}) {
|
|
647
|
+
const { gfm = true, math = true } = options;
|
|
648
|
+
const plugins = [];
|
|
649
|
+
if (gfm) plugins.push([remarkGfm__default.default, { singleTilde: false }]);
|
|
650
|
+
if (math) plugins.push([remarkMath__default.default, { singleDollarTextMath: false }]);
|
|
651
|
+
return plugins;
|
|
652
|
+
}
|
|
653
|
+
function buildRehypePlugins(options = {}) {
|
|
654
|
+
const {
|
|
655
|
+
math = true,
|
|
656
|
+
highlight = true,
|
|
657
|
+
sourceLines = true,
|
|
658
|
+
sanitize = true,
|
|
659
|
+
bodyLineOffset = 0
|
|
660
|
+
} = options;
|
|
661
|
+
const plugins = [rehypeRaw__default.default];
|
|
662
|
+
if (sourceLines) {
|
|
663
|
+
plugins.push([rehypeSourceLines_default, { offset: bodyLineOffset }]);
|
|
664
|
+
}
|
|
665
|
+
plugins.push(rehypeVantageDirectives_default);
|
|
666
|
+
if (sanitize) plugins.push([rehypeSanitize__default.default, sanitizeSchema]);
|
|
667
|
+
plugins.push(rehypeSlug__default.default);
|
|
668
|
+
if (highlight) plugins.push(rehypeHighlight__default.default);
|
|
669
|
+
if (math) {
|
|
670
|
+
plugins.push(rehypeCaptureMathStamps, rehypeKatex__default.default, rehypeRestoreMathStamps);
|
|
671
|
+
}
|
|
672
|
+
return plugins;
|
|
673
|
+
}
|
|
674
|
+
function buildPipeline(options = {}) {
|
|
675
|
+
return {
|
|
676
|
+
remarkPlugins: buildRemarkPlugins(options),
|
|
677
|
+
rehypePlugins: buildRehypePlugins(options)
|
|
678
|
+
};
|
|
679
|
+
}
|
|
120
680
|
function parseFrontmatter(content) {
|
|
121
681
|
if (content.startsWith("+++")) {
|
|
122
682
|
return parseFrontmatterWithDelimiter(content, "+++", "toml");
|
|
@@ -124,25 +684,75 @@ function parseFrontmatter(content) {
|
|
|
124
684
|
if (content.startsWith("---")) {
|
|
125
685
|
return parseFrontmatterWithDelimiter(content, "---", "yaml");
|
|
126
686
|
}
|
|
127
|
-
return
|
|
687
|
+
return withOffset(content, {
|
|
688
|
+
frontmatter: {},
|
|
689
|
+
body: content,
|
|
690
|
+
format: "none"
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
function withOffset(content, parsed) {
|
|
694
|
+
const stripped = content.slice(0, content.length - parsed.body.length);
|
|
695
|
+
let bodyLineOffset = 0;
|
|
696
|
+
for (const ch of stripped) {
|
|
697
|
+
if (ch === "\n") bodyLineOffset++;
|
|
698
|
+
}
|
|
699
|
+
return { ...parsed, bodyLineOffset };
|
|
128
700
|
}
|
|
129
701
|
function parseFrontmatterWithDelimiter(content, delimiter, format) {
|
|
130
702
|
const searchStart = delimiter.length;
|
|
131
703
|
const endIndex = content.indexOf(`
|
|
132
704
|
${delimiter}`, searchStart);
|
|
133
705
|
if (endIndex === -1) {
|
|
134
|
-
return
|
|
706
|
+
return withOffset(content, {
|
|
707
|
+
frontmatter: {},
|
|
708
|
+
body: content,
|
|
709
|
+
format: "none",
|
|
710
|
+
problem: { kind: "unterminated", delimiter }
|
|
711
|
+
});
|
|
135
712
|
}
|
|
136
713
|
const raw = content.slice(searchStart + 1, endIndex).trim();
|
|
137
714
|
const bodyStart = endIndex + 1 + delimiter.length;
|
|
138
715
|
const body = content.slice(bodyStart).replace(/^\n/, "");
|
|
139
716
|
try {
|
|
140
|
-
const
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
|
|
717
|
+
const parsed = format === "toml" ? smolToml.parse(raw) : YAML__default.default.parse(raw);
|
|
718
|
+
return withOffset(content, {
|
|
719
|
+
frontmatter: parsed || {},
|
|
720
|
+
body,
|
|
721
|
+
format,
|
|
722
|
+
...isMapping(parsed) ? {} : { problem: { kind: "not-a-mapping", delimiter } }
|
|
723
|
+
});
|
|
724
|
+
} catch (error) {
|
|
725
|
+
return withOffset(content, {
|
|
726
|
+
frontmatter: {},
|
|
727
|
+
body: content,
|
|
728
|
+
format: "none",
|
|
729
|
+
problem: { kind: "invalid", delimiter, ...errorPosition(error) }
|
|
730
|
+
});
|
|
144
731
|
}
|
|
145
732
|
}
|
|
733
|
+
function isMapping(value) {
|
|
734
|
+
return value === null || value === void 0 || typeof value === "object" && !Array.isArray(value);
|
|
735
|
+
}
|
|
736
|
+
function errorPosition(error) {
|
|
737
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
738
|
+
const source = error;
|
|
739
|
+
const yamlPosition = source?.linePos?.[0];
|
|
740
|
+
if (typeof yamlPosition?.line === "number") {
|
|
741
|
+
return {
|
|
742
|
+
message,
|
|
743
|
+
line: yamlPosition.line,
|
|
744
|
+
...typeof yamlPosition.col === "number" ? { column: yamlPosition.col } : {}
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
if (typeof source?.line === "number") {
|
|
748
|
+
return {
|
|
749
|
+
message,
|
|
750
|
+
line: source.line,
|
|
751
|
+
...typeof source.column === "number" ? { column: source.column } : {}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
return { message };
|
|
755
|
+
}
|
|
146
756
|
|
|
147
757
|
// src/renderMarkdown.ts
|
|
148
758
|
async function renderMarkdown(content, options = {}) {
|
|
@@ -158,27 +768,22 @@ async function renderMarkdown(content, options = {}) {
|
|
|
158
768
|
if (parseFm) {
|
|
159
769
|
parsed = parseFrontmatter(content);
|
|
160
770
|
} else {
|
|
161
|
-
parsed = {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
processor = processor.use(remarkRehype__default.default, { allowDangerousHtml: true });
|
|
178
|
-
for (const [plugin, ...args] of rehypePlugins) {
|
|
179
|
-
processor = processor.use(plugin, ...args);
|
|
180
|
-
}
|
|
181
|
-
processor = processor.use(rehypeStringify__default.default);
|
|
771
|
+
parsed = {
|
|
772
|
+
frontmatter: {},
|
|
773
|
+
body: content,
|
|
774
|
+
format: "none",
|
|
775
|
+
bodyLineOffset: 0
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
const { remarkPlugins, rehypePlugins } = buildPipeline({
|
|
779
|
+
gfm,
|
|
780
|
+
math,
|
|
781
|
+
highlight,
|
|
782
|
+
sourceLines,
|
|
783
|
+
sanitize,
|
|
784
|
+
bodyLineOffset: parsed.bodyLineOffset
|
|
785
|
+
});
|
|
786
|
+
const processor = unified.unified().use(remarkParse__default.default).use(remarkPlugins).use(remarkRehype__default.default, { allowDangerousHtml: true }).use(rehypePlugins).use(rehypeStringify__default.default);
|
|
182
787
|
const result = await processor.process(parsed.body);
|
|
183
788
|
return {
|
|
184
789
|
html: String(result),
|
|
@@ -187,8 +792,7 @@ async function renderMarkdown(content, options = {}) {
|
|
|
187
792
|
};
|
|
188
793
|
}
|
|
189
794
|
|
|
190
|
-
// src/
|
|
191
|
-
var HIGHLIGHT_CLASS = "line-anchor-highlight";
|
|
795
|
+
// src/lineAnchor.ts
|
|
192
796
|
function parseLineAnchor(hash) {
|
|
193
797
|
if (!hash) return null;
|
|
194
798
|
const frag = hash.startsWith("#") ? hash.slice(1) : hash;
|
|
@@ -198,6 +802,9 @@ function parseLineAnchor(hash) {
|
|
|
198
802
|
const end = match[2] ? parseInt(match[2], 10) : start;
|
|
199
803
|
return { start: Math.min(start, end), end: Math.max(start, end) };
|
|
200
804
|
}
|
|
805
|
+
|
|
806
|
+
// src/scrollToLineAnchor.ts
|
|
807
|
+
var HIGHLIGHT_CLASS = "line-anchor-highlight";
|
|
201
808
|
function clearLineAnchorHighlights(container) {
|
|
202
809
|
container.querySelectorAll(`.${HIGHLIGHT_CLASS}`).forEach((node) => {
|
|
203
810
|
node.classList.remove(HIGHLIGHT_CLASS);
|
|
@@ -210,10 +817,7 @@ function scrollToLineAnchor(container, hash) {
|
|
|
210
817
|
const blocks = container.querySelectorAll("[data-source-line]");
|
|
211
818
|
let firstMatch = null;
|
|
212
819
|
for (const block of blocks) {
|
|
213
|
-
const line = parseInt(
|
|
214
|
-
block.dataset.sourceLine || "0",
|
|
215
|
-
10
|
|
216
|
-
);
|
|
820
|
+
const line = parseInt(block.dataset.sourceLine || "0", 10);
|
|
217
821
|
if (line >= range.start && line <= range.end) {
|
|
218
822
|
block.classList.add(HIGHLIGHT_CLASS);
|
|
219
823
|
if (!firstMatch) firstMatch = block;
|
|
@@ -260,6 +864,74 @@ function findScrollParent(el) {
|
|
|
260
864
|
return null;
|
|
261
865
|
}
|
|
262
866
|
|
|
867
|
+
// src/vantageFrontmatter.ts
|
|
868
|
+
var DOC_STATUSES = [
|
|
869
|
+
"draft",
|
|
870
|
+
"in-review",
|
|
871
|
+
"accepted",
|
|
872
|
+
"deprecated"
|
|
873
|
+
];
|
|
874
|
+
var VANTAGE_FRONTMATTER_KEYS = ["status-chip"];
|
|
875
|
+
var DOC_STATUS_TONES = {
|
|
876
|
+
draft: "muted",
|
|
877
|
+
"in-review": "warning",
|
|
878
|
+
accepted: "tip",
|
|
879
|
+
deprecated: "caution"
|
|
880
|
+
};
|
|
881
|
+
var STATUS_CHIP_VALUES = [
|
|
882
|
+
...DOC_STATUSES,
|
|
883
|
+
"true",
|
|
884
|
+
"false"
|
|
885
|
+
];
|
|
886
|
+
function isDocStatus(value) {
|
|
887
|
+
return typeof value === "string" && DOC_STATUSES.includes(value);
|
|
888
|
+
}
|
|
889
|
+
function isTable(value) {
|
|
890
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
891
|
+
}
|
|
892
|
+
function readVantageFrontmatter(frontmatter) {
|
|
893
|
+
const issues = [];
|
|
894
|
+
if (!Object.hasOwn(frontmatter, "vantage")) return { issues };
|
|
895
|
+
const value = frontmatter["vantage"];
|
|
896
|
+
if (!isTable(value)) {
|
|
897
|
+
issues.push({ kind: "not-a-table", value });
|
|
898
|
+
return { issues };
|
|
899
|
+
}
|
|
900
|
+
let statusChip;
|
|
901
|
+
for (const key of Object.keys(value)) {
|
|
902
|
+
if (!VANTAGE_FRONTMATTER_KEYS.includes(key)) {
|
|
903
|
+
issues.push({ kind: "unknown-key", key });
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (key === "status-chip") {
|
|
907
|
+
statusChip = readStatusChip(frontmatter, value[key], issues);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return { ...statusChip === void 0 ? {} : { statusChip }, issues };
|
|
911
|
+
}
|
|
912
|
+
function readStatusChip(frontmatter, raw, issues) {
|
|
913
|
+
const status = frontmatter["status"];
|
|
914
|
+
if (raw === false) return void 0;
|
|
915
|
+
if (raw === true) {
|
|
916
|
+
if (isDocStatus(status)) return status;
|
|
917
|
+
issues.push({ kind: "status-chip-orphan", status });
|
|
918
|
+
return void 0;
|
|
919
|
+
}
|
|
920
|
+
if (isDocStatus(raw)) {
|
|
921
|
+
if (isDocStatus(status) && status !== raw) {
|
|
922
|
+
issues.push({ kind: "status-chip-disagrees", chip: raw, status });
|
|
923
|
+
}
|
|
924
|
+
return raw;
|
|
925
|
+
}
|
|
926
|
+
issues.push({
|
|
927
|
+
kind: "bad-value",
|
|
928
|
+
key: "status-chip",
|
|
929
|
+
value: raw,
|
|
930
|
+
legal: STATUS_CHIP_VALUES
|
|
931
|
+
});
|
|
932
|
+
return void 0;
|
|
933
|
+
}
|
|
934
|
+
|
|
263
935
|
// src/mermaidCache.ts
|
|
264
936
|
var svgCache = /* @__PURE__ */ new Map();
|
|
265
937
|
|
|
@@ -357,10 +1029,152 @@ function resolveLinks(html, options = {}) {
|
|
|
357
1029
|
);
|
|
358
1030
|
}
|
|
359
1031
|
|
|
1032
|
+
// src/styleGuide.ts
|
|
1033
|
+
var STYLE_GUIDE = `## Markdown style guide (for Vantage viewer)
|
|
1034
|
+
|
|
1035
|
+
When writing or updating markdown documents that will be viewed in Vantage, follow these conventions:
|
|
1036
|
+
|
|
1037
|
+
### Structure
|
|
1038
|
+
- Use headings (## and ###) to organize content \u2014 they become navigable outline anchors.
|
|
1039
|
+
- Keep paragraphs focused and concise. Break up dense text with subheadings, lists, or tables.
|
|
1040
|
+
|
|
1041
|
+
### Links and cross-references
|
|
1042
|
+
- **Relative paths only**: Always link relative to the *current file's directory*:
|
|
1043
|
+
- Sibling in same folder: \`[Other Doc](./other-doc.md)\` or \`[Other Doc](other-doc.md)\`
|
|
1044
|
+
- Subdirectory: \`[Design Doc](./design/auth.md)\`
|
|
1045
|
+
- Parent / sibling folder: \`[Overview](../overview.md)\` or \`[Spec](../specs/api.md)\`
|
|
1046
|
+
- **Never use leading slashes**:
|
|
1047
|
+
- \u274C \`[Doc](/docs/guide.md)\` (breaks web routing and multi-repo scoping)
|
|
1048
|
+
- \u2705 \`[Doc](../docs/guide.md)\` or \`[Doc](./guide.md)\`
|
|
1049
|
+
- **Never use absolute filesystem paths or URI schemes**:
|
|
1050
|
+
- \u274C \`file:///workspace/docs/guide.md\`, \`/workspace/docs/guide.md\`, \`C:\\...\`
|
|
1051
|
+
- \u2705 \`[Doc](./guide.md)\` or \`[Doc](../guide.md)\`
|
|
1052
|
+
- **Always include the file extension**: Use \`.md\`, \`.ts\`, \`.go\`, etc. (e.g. \`[Model](model.go)\`).
|
|
1053
|
+
- **Line anchors and ranges**:
|
|
1054
|
+
- Link to specific lines: \`[Handler](../server/api.go#L42)\` or \`[Range](../server/api.go#L42-L58)\`
|
|
1055
|
+
- Same-file line anchor: \`[See lines](#L10-L25)\`
|
|
1056
|
+
- Vantage scrolls to and highlights the target lines.
|
|
1057
|
+
- **Section anchors**:
|
|
1058
|
+
- Same doc: \`[Usage](#usage)\`
|
|
1059
|
+
- Cross-doc: \`[Architecture](../overview.md#system-architecture)\`
|
|
1060
|
+
- Anchor slugs are lowercase, hyphenated, and punctuation-stripped.
|
|
1061
|
+
- **Backticks in links**: Place backticks inside the link label, not around the markdown link syntax:
|
|
1062
|
+
- \u2705 \`[\`config.json\`](./config.json)\` or \`[config.json](./config.json)\`
|
|
1063
|
+
- \u274C \`\`[config.json](./config.json)\`\`
|
|
1064
|
+
|
|
1065
|
+
### Frontmatter (Metadata)
|
|
1066
|
+
- Include structured metadata at the very top of docs delimited by \`---\` (YAML) or \`+++\` (TOML). Vantage renders this as a metadata card:
|
|
1067
|
+
\`\`\`yaml
|
|
1068
|
+
---
|
|
1069
|
+
title: "Feature Specification"
|
|
1070
|
+
author: "Agent"
|
|
1071
|
+
date: 2026-08-15
|
|
1072
|
+
status: in-review # draft | in-review | accepted | deprecated
|
|
1073
|
+
tags: [architecture, backend, api]
|
|
1074
|
+
summary: "Brief description of the document purpose."
|
|
1075
|
+
vantage:
|
|
1076
|
+
status-chip: true # show \`status\` as a chip above the metadata card
|
|
1077
|
+
---
|
|
1078
|
+
\`\`\`
|
|
1079
|
+
- **Nothing may sit above the opening delimiter** \u2014 not a blank line, not an editorial comment, not a \`<!-- vantage: \u2026 -->\` directive. Frontmatter is recognised only at the very first byte of the file (in Vantage, on GitHub, and in every other reader), so one line above it turns the whole block into body text: a horizontal rule followed by a heading made of the raw keys, with every field lost. \`vantage-check\` reports it as \`frontmatter/not-at-top\`.
|
|
1080
|
+
- **\`vantage:\` is Vantage's own reserved key.** It holds chrome that belongs to the file rather than to a section, it never shows up in the metadata card, and every other renderer ignores it. One key today: \`status-chip\`.
|
|
1081
|
+
- **Prefer \`status-chip: true\`**, which shows the document's own \`status:\` and therefore cannot disagree with it. A literal \`status-chip: accepted\` is accepted too, but it is a second value that goes stale on its own \u2014 \`vantage-check\` reports the disagreement.
|
|
1082
|
+
- The chip's vocabulary is \`status\`'s, exactly: \`draft | in-review | accepted | deprecated\`, lowercase. \`Draft\` renders no chip at all, silently.
|
|
1083
|
+
|
|
1084
|
+
### Mermaid diagrams
|
|
1085
|
+
- Use \`\`\`mermaid code blocks for flowcharts, sequence diagrams, and architecture diagrams. Vantage provides interactive zoom, pan, dark/light theme adaptation, and SVG export.
|
|
1086
|
+
- **Quote labels with special characters**: Always quote node labels containing parentheses, brackets, or colons to prevent syntax errors:
|
|
1087
|
+
\`\`\`mermaid
|
|
1088
|
+
flowchart TD
|
|
1089
|
+
client["Client (React SPA)"] -->|WebSocket| srv["Vantage Server (Go)"]
|
|
1090
|
+
srv --> git["Git CLI (git diff)"]
|
|
1091
|
+
\`\`\`
|
|
1092
|
+
|
|
1093
|
+
### Code blocks and diffs
|
|
1094
|
+
- Always tag fenced code blocks with language identifiers (\`ts\`, \`go\`, \`python\`, \`bash\`, \`json\`, \`yaml\`, \`diff\`, \`sql\`, etc.) for syntax highlighting.
|
|
1095
|
+
- For proposed code modifications, use \`\`\`diff blocks with \`+\` and \`-\` prefixes:
|
|
1096
|
+
\`\`\`diff
|
|
1097
|
+
-const oldUrl = "/api/v1";
|
|
1098
|
+
+const newUrl = "/api/v2";
|
|
1099
|
+
\`\`\`
|
|
1100
|
+
|
|
1101
|
+
### Callouts and alerts
|
|
1102
|
+
- Use GitHub-style blockquote callouts for notes, tips, and warnings:
|
|
1103
|
+
> [!NOTE]
|
|
1104
|
+
> Background context or helpful explanation.
|
|
1105
|
+
|
|
1106
|
+
> [!TIP]
|
|
1107
|
+
> Best practice advice or optimization suggestions.
|
|
1108
|
+
|
|
1109
|
+
> [!IMPORTANT]
|
|
1110
|
+
> Key requirements or crucial information.
|
|
1111
|
+
|
|
1112
|
+
> [!WARNING]
|
|
1113
|
+
> Urgent caution, breaking changes, or potential pitfalls.
|
|
1114
|
+
|
|
1115
|
+
> [!CAUTION]
|
|
1116
|
+
> High-risk actions that could cause data loss or security issues.
|
|
1117
|
+
|
|
1118
|
+
### Vantage directives (optional, and Vantage-only)
|
|
1119
|
+
|
|
1120
|
+
Vantage reads a few styling hints from ordinary HTML comments. Every other renderer \u2014 GitHub included \u2014 drops them, so a document has to read exactly the same without them: directives decorate, they never carry meaning. One goes on a line of its own, with a blank line after it, and applies to the block that follows:
|
|
1121
|
+
|
|
1122
|
+
\`\`\`markdown
|
|
1123
|
+
<!-- vantage: section tone=warning badge=stale -->
|
|
1124
|
+
|
|
1125
|
+
## Migration path
|
|
1126
|
+
|
|
1127
|
+
The steps below predate the rewrite.
|
|
1128
|
+
\`\`\`
|
|
1129
|
+
|
|
1130
|
+
- **Three names**: \`section\` (the heading and everything under it), \`block\` (the one block after it), \`oq\` (one answerable Open Question).
|
|
1131
|
+
- **The keys and values are a closed set**: \`tone\` = \`note | tip | important | warning | caution | muted\`; \`emphasis\` = \`strong | normal | quiet\`; \`badge\` = \`draft | stale | blocked | done | wip\`; \`collapsed\` = \`true | false\`. Name a *tone*, never a colour \u2014 the theme decides what a warning looks like, in light mode, in dark mode, and in print.
|
|
1132
|
+
- **Use them sparingly.** One or two per document, on the sections that genuinely differ. A document where everything is toned says nothing, and a rainbow one is harder to read than a plain one.
|
|
1133
|
+
- **Anything outside those sets is silently ignored** \u2014 nothing breaks, and nothing styles either. Run \`vantage-check\` on the document: the \`vantage/*\` rules are the only thing that will ever tell you a directive did nothing.
|
|
1134
|
+
- **Always close the comment with \`-->\`.** Never \`--!>\`, and never leave it open: Markdown reads every line below an unclosed \`<!--\` as part of the comment, and the whole rest of the document vanishes from the page. For the same reason \`-->\` cannot appear *inside* a value \u2014 it ends the comment early and spills the remainder into the page as literal text.
|
|
1135
|
+
- **In a list, indent the directive inside the item**, with blank lines around it (below). At the start of a line between two items it ends the list and starts a second one, which changes the numbering and the spacing in every renderer \u2014 the one thing a directive must never do.
|
|
1136
|
+
- **A \`leaning\` restates the leaning; it is never "yes".** The one-click button in review mode files that text as a review comment, and the comment is all the agent reading it has \u2014 nobody remembers which button was clicked. \`leaning="Yes"\` beside a two-branch question is a support ticket.
|
|
1137
|
+
|
|
1138
|
+
\`\`\`markdown
|
|
1139
|
+
1. **OQ-9: Queue position on re-entry.**
|
|
1140
|
+
|
|
1141
|
+
<!-- vantage: oq id=OQ-9 leaning="Back of the queue \u2014 the fix might interact with what merged while it was out." -->
|
|
1142
|
+
|
|
1143
|
+
_Leaning:_ Back of the queue.
|
|
1144
|
+
\`\`\`
|
|
1145
|
+
|
|
1146
|
+
### Tables, task lists, and math
|
|
1147
|
+
- **Tables**: Use standard markdown tables for structured comparisons and schemas.
|
|
1148
|
+
- **Task lists**: Use \`- [ ]\` and \`- [x]\` for actionable checklists and status tracking.
|
|
1149
|
+
- **LaTeX Math**: Use \`$$...$$\` for *all* KaTeX math \u2014 display blocks (\`$$\` alone on its own lines) and inline alike (\`$$E = mc^2$$\` mid-sentence).
|
|
1150
|
+
- Single dollars are **not** math delimiters: \`$HOME\` and \`$100\` stay literal, so prose and shell snippets are safe to write as-is.
|
|
1151
|
+
`;
|
|
1152
|
+
|
|
1153
|
+
exports.DIRECTIVE_NAMES = DIRECTIVE_NAMES;
|
|
1154
|
+
exports.DIRECTIVE_VOCABULARY = DIRECTIVE_VOCABULARY;
|
|
1155
|
+
exports.DOC_STATUSES = DOC_STATUSES;
|
|
1156
|
+
exports.DOC_STATUS_TONES = DOC_STATUS_TONES;
|
|
1157
|
+
exports.SAFE_STYLE = SAFE_STYLE;
|
|
1158
|
+
exports.STYLE_GUIDE = STYLE_GUIDE;
|
|
1159
|
+
exports.VANTAGE_BADGES = VANTAGE_BADGES;
|
|
1160
|
+
exports.VANTAGE_COLLAPSED = VANTAGE_COLLAPSED;
|
|
1161
|
+
exports.VANTAGE_EMPHASIS = VANTAGE_EMPHASIS;
|
|
1162
|
+
exports.VANTAGE_FRONTMATTER_KEYS = VANTAGE_FRONTMATTER_KEYS;
|
|
1163
|
+
exports.VANTAGE_OQ_HOST_TARGETS = VANTAGE_OQ_HOST_TARGETS;
|
|
1164
|
+
exports.VANTAGE_RUNS = VANTAGE_RUNS;
|
|
1165
|
+
exports.VANTAGE_SENTINEL = VANTAGE_SENTINEL;
|
|
1166
|
+
exports.VANTAGE_TONES = VANTAGE_TONES;
|
|
1167
|
+
exports.buildPipeline = buildPipeline;
|
|
1168
|
+
exports.buildRemarkPlugins = buildRemarkPlugins;
|
|
360
1169
|
exports.clearLineAnchorHighlights = clearLineAnchorHighlights;
|
|
1170
|
+
exports.hasVantageSentinel = hasVantageSentinel;
|
|
1171
|
+
exports.isDocStatus = isDocStatus;
|
|
361
1172
|
exports.parseFrontmatter = parseFrontmatter;
|
|
362
1173
|
exports.parseLineAnchor = parseLineAnchor;
|
|
1174
|
+
exports.parseVantageDirective = parseVantageDirective;
|
|
1175
|
+
exports.readVantageFrontmatter = readVantageFrontmatter;
|
|
363
1176
|
exports.rehypeSourceLines = rehypeSourceLines_default;
|
|
1177
|
+
exports.rehypeVantageDirectives = rehypeVantageDirectives_default;
|
|
364
1178
|
exports.renderMarkdown = renderMarkdown;
|
|
365
1179
|
exports.renderMermaidBlocks = renderMermaidBlocks;
|
|
366
1180
|
exports.resolveLinks = resolveLinks;
|