vantage-md 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -39,6 +39,7 @@ let rehype_katex = require("rehype-katex");
39
39
  rehype_katex = __toESM(rehype_katex, 1);
40
40
  let rehype_slug = require("rehype-slug");
41
41
  rehype_slug = __toESM(rehype_slug, 1);
42
+ let unist_util_visit = require("unist-util-visit");
42
43
  let yaml = require("yaml");
43
44
  yaml = __toESM(yaml, 1);
44
45
  let smol_toml = require("smol-toml");
@@ -51,6 +52,15 @@ remark_rehype = __toESM(remark_rehype, 1);
51
52
  let rehype_stringify = require("rehype-stringify");
52
53
  rehype_stringify = __toESM(rehype_stringify, 1);
53
54
  //#region src/rehypeSourceLines.ts
55
+ /**
56
+ * Tags that get a `data-source-line`.
57
+ *
58
+ * `td`/`th` are here for review mode: a comment anchors to the cell it was
59
+ * written on, so the cell needs a line of its own to be found again. Every cell
60
+ * in a row reports the *row's* start line — a GFM row is one source line — so a
61
+ * line no longer names at most one anchorable element, and whatever resolves an
62
+ * anchor has to break the tie by block hash (`useReviewHighlights`).
63
+ */
54
64
  const BLOCK_TAGS = /* @__PURE__ */ new Set([
55
65
  "p",
56
66
  "h1",
@@ -63,30 +73,153 @@ const BLOCK_TAGS = /* @__PURE__ */ new Set([
63
73
  "blockquote",
64
74
  "pre",
65
75
  "table",
76
+ "td",
77
+ "th",
66
78
  "tr",
67
79
  "ul",
68
80
  "ol",
69
81
  "hr",
70
82
  "div"
71
83
  ]);
72
- function visit(node, offset) {
84
+ function visit$1(node, offset) {
73
85
  if ("children" in node) {
74
86
  for (const child of node.children) if (child.type === "element") {
75
87
  if (BLOCK_TAGS.has(child.tagName) && child.position?.start?.line) {
76
88
  child.properties = child.properties || {};
77
89
  child.properties["dataSourceLine"] = child.position.start.line + offset;
78
90
  }
79
- visit(child, offset);
91
+ visit$1(child, offset);
80
92
  }
81
93
  }
82
94
  }
83
95
  const rehypeSourceLines = (options) => {
84
96
  const offset = options?.offset ?? 0;
85
97
  return (tree) => {
86
- visit(tree, offset);
98
+ visit$1(tree, offset);
87
99
  };
88
100
  };
89
101
  //#endregion
102
+ //#region src/rehypeVantageAlerts.ts
103
+ /**
104
+ * GFM alerts — `> [!WARNING]` — compiled into `data-vantage-alert`.
105
+ *
106
+ * `remark-gfm` does not implement alerts, so until this plugin existed a
107
+ * `> [!WARNING]` rendered as an ordinary blockquote with the literal marker
108
+ * visible as its first words. Worse than merely unstyled: `@tailwindcss/typography`
109
+ * italicises blockquotes and draws `open-quote`/`close-quote` around the first
110
+ * paragraph, so a callout came out as an italic *quotation* whose opening words
111
+ * were `"[!WARNING]`. That was the "Known gaps" entry in
112
+ * `docs/reference/inline-markup.md` and OQ-10, filed rather than fixed, while
113
+ * `styleGuide.ts` went on telling every agent to write them.
114
+ *
115
+ * The tokens are deliberately the ones the `tone` vocabulary already resolves —
116
+ * an alert *is* the six-colour light/dark treatment `tone` shipped, which is
117
+ * exactly what the gap entry said whoever fixed this should do rather than
118
+ * building a second palette. `[!WARNING]` and `<!-- vantage: block tone=warning -->`
119
+ * therefore agree by construction, and adding a theme still touches one
120
+ * custom-property block.
121
+ *
122
+ * **This runs in the shared pipeline, so all four renderers get it** — the live
123
+ * viewer, the package's exported viewer, the static export and the CLI checker's
124
+ * `renderMarkdown`. That is what makes an injected title element acceptable here
125
+ * where the collapse caret's glyph had to be drawn in CSS: the caret is injected
126
+ * by app JS that may never run, and this is not (D5).
127
+ *
128
+ * ## What it does not do
129
+ *
130
+ * It does not touch a blockquote that carries no marker, and an unrecognised
131
+ * marker (`[!HINT]`) is left exactly as it was — visible literal text, which is
132
+ * the honest rendering of something GitHub also would not style. Silently
133
+ * swallowing it would hide a typo that reads as a callout on neither renderer.
134
+ */
135
+ /**
136
+ * The five GFM alert kinds, lowercased.
137
+ *
138
+ * Deliberately *not* re-derived from `VANTAGE_TONES`: that list carries a sixth
139
+ * token, `muted`, which is ours and is not an alert word. The overlap is the
140
+ * point — the five that coincide share a palette — but the two vocabularies are
141
+ * closed by different authorities and a change to one must not silently move the
142
+ * other. A test asserts the five are a subset of the tones.
143
+ */
144
+ const VANTAGE_ALERTS = [
145
+ "note",
146
+ "tip",
147
+ "important",
148
+ "warning",
149
+ "caution"
150
+ ];
151
+ /** The visible label per kind. Title case, as GitHub renders it. */
152
+ const ALERT_TITLES = {
153
+ note: "Note",
154
+ tip: "Tip",
155
+ important: "Important",
156
+ warning: "Warning",
157
+ caution: "Caution"
158
+ };
159
+ /**
160
+ * The marker, anchored and requiring the rest of its line to be empty.
161
+ *
162
+ * GFM puts the marker alone on the blockquote's first line, and holding to that
163
+ * is what keeps a paragraph that merely *begins* with bracketed text from being
164
+ * eaten. The trailing newline is optional only for the degenerate blockquote
165
+ * whose entire content is the marker.
166
+ *
167
+ * Measured against the real chain rather than assumed: `remark-parse` reads
168
+ * `[!TIP]` as a shortcut link reference, and because no definition matches,
169
+ * `mdast-util-to-hast` puts it back as **one** leading text node —
170
+ * `"[!TIP]\nThe generalization: "` — not as a `[`/label/`]` triple. So a single
171
+ * anchored test on the first text node is enough, and the plugin does not have
172
+ * to reassemble the marker across siblings.
173
+ */
174
+ const MARKER = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*(?:\r?\n|$)/;
175
+ /** The first child, if it is an element. */
176
+ function firstElement(node) {
177
+ const child = node.children.find((c) => c.type === "element" || c.type === "text" && c.value.trim() !== "");
178
+ return child?.type === "element" ? child : void 0;
179
+ }
180
+ /**
181
+ * Compile `> [!KIND]` blockquotes into `data-vantage-alert="kind"`.
182
+ *
183
+ * Order in the chain matters twice, and both are stated in `pipeline.ts`:
184
+ *
185
+ * - **after `rehypeSourceLines`**, so the injected title carries no
186
+ * `data-source-line`. That is what keeps it out of `anchorBlockWithin`, which
187
+ * filters candidates to those with a finite line — otherwise a review comment
188
+ * on an alert would anchor to the word "Warning" instead of to the prose.
189
+ * - **before `rehypeSanitize`**, so nothing reaches the DOM the schema has not
190
+ * passed. `dataVantageAlert` is allowlisted there by name *and* value, like
191
+ * every other `data-vantage-*` attribute.
192
+ */
193
+ function rehypeVantageAlerts() {
194
+ return (tree) => {
195
+ (0, unist_util_visit.visit)(tree, "element", (node) => {
196
+ if (node.tagName !== "blockquote") return;
197
+ const paragraph = firstElement(node);
198
+ if (paragraph === void 0 || paragraph.tagName !== "p") return;
199
+ const lead = paragraph.children[0];
200
+ if (lead === void 0 || lead.type !== "text") return;
201
+ const match = MARKER.exec(lead.value);
202
+ if (match === null) return;
203
+ const kind = match[1].toLowerCase();
204
+ lead.value = lead.value.slice(match[0].length);
205
+ if (lead.value === "" && paragraph.children.length === 1) node.children = node.children.filter((c) => c !== paragraph);
206
+ node.properties = {
207
+ ...node.properties,
208
+ dataVantageAlert: kind
209
+ };
210
+ node.children.unshift({
211
+ type: "element",
212
+ tagName: "div",
213
+ properties: { className: ["vantage-alert-title"] },
214
+ children: [{
215
+ type: "text",
216
+ value: ALERT_TITLES[kind]
217
+ }]
218
+ });
219
+ });
220
+ };
221
+ }
222
+ //#endregion
90
223
  //#region src/vantageDirectives.ts
91
224
  /**
92
225
  * The `tone` vocabulary: GitHub's alert words plus `muted`.
@@ -827,6 +960,7 @@ const sanitizeSchema = {
827
960
  ["dataVantageCollapseToggle", COLLAPSE_GROUP_ID],
828
961
  ["dataVantageRun", ...VANTAGE_RUNS],
829
962
  ["dataVantageOq", "true"],
963
+ ["dataVantageAlert", ...VANTAGE_ALERTS],
830
964
  "dataVantageLeaning"
831
965
  ],
832
966
  code: [...rehype_sanitize.defaultSchema.attributes?.code || [], "className"],
@@ -872,6 +1006,7 @@ function buildRehypePlugins(options = {}) {
872
1006
  const { math = true, highlight = true, sourceLines = true, sanitize = true, bodyLineOffset = 0 } = options;
873
1007
  const plugins = [rehype_raw.default];
874
1008
  if (sourceLines) plugins.push([rehypeSourceLines, { offset: bodyLineOffset }]);
1009
+ plugins.push(rehypeVantageAlerts);
875
1010
  plugins.push(rehypeVantageDirectives);
876
1011
  if (sanitize) plugins.push([rehype_sanitize.default, sanitizeSchema]);
877
1012
  plugins.push(rehype_slug.default);