nizel-kit 0.1.9 → 0.1.11

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/nizel-kit.js CHANGED
@@ -32,8 +32,36 @@ var blockTags = /* @__PURE__ */ new Set([
32
32
  ]);
33
33
  var passthroughInlineTags = /* @__PURE__ */ new Set(["abbr", "bdi", "bdo", "cite", "data", "dfn", "kbd", "mark", "q", "samp", "small", "sub", "sup", "time", "u", "var"]);
34
34
  function htmlToMarkdown(html, options = {}) {
35
+ const resolved = {
36
+ unsupported: options.unsupported ?? "preserve",
37
+ plugins: options.plugins ?? []
38
+ };
39
+ const handlers = resolved.plugins.flatMap((plugin) => {
40
+ const handler = plugin?.htmlToMarkdown;
41
+ return handler ? Array.isArray(handler) ? handler : [handler] : [];
42
+ });
43
+ const ctx = { options: resolved, handlers, epilogue: [] };
35
44
  const root = parseHtml(html);
36
- return normalizeDocument(renderChildren(root.children, { unsupported: options.unsupported ?? "preserve" }, 0));
45
+ const body = normalizeDocument(renderChildren(root.children, ctx, 0));
46
+ return ctx.epilogue.length ? normalizeDocument(`${body}
47
+
48
+ ${ctx.epilogue.join("\n\n")}`) : body;
49
+ }
50
+ function buildHandlerContext(node, ctx, depth) {
51
+ return {
52
+ options: ctx.options,
53
+ inline: (target) => normalizeInline(renderChildren(childrenOf(target), ctx, depth)),
54
+ block: (target) => normalizeDocument(renderChildren(childrenOf(target), ctx, depth)),
55
+ text: (target) => textContent(target),
56
+ indent: (value, size) => indentContinuation(value, size),
57
+ epilogue: (value) => {
58
+ if (value)
59
+ ctx.epilogue.push(value);
60
+ }
61
+ };
62
+ }
63
+ function childrenOf(node) {
64
+ return node.type === "element" || node.type === "root" ? node.children : [];
37
65
  }
38
66
  function parseHtml(html) {
39
67
  const root = { type: "root", children: [] };
@@ -135,25 +163,31 @@ function hasUnsupportedAttrs(node) {
135
163
  function slugifyText(value) {
136
164
  return value.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
137
165
  }
138
- function renderChildren(nodes, options, depth) {
139
- return nodes.map((node) => renderNode(node, options, depth)).join("");
166
+ function renderChildren(nodes, ctx, depth) {
167
+ return nodes.map((node) => renderNode(node, ctx, depth)).join("");
140
168
  }
141
- function renderNode(node, options, depth) {
169
+ function renderNode(node, ctx, depth) {
142
170
  if (node.type === "text")
143
171
  return collapseText(decodeEntities(node.value));
144
172
  if (node.type === "comment")
145
- return options.unsupported === "preserve" ? node.value : " ";
173
+ return ctx.options.unsupported === "preserve" ? node.value : " ";
146
174
  if (node.type === "root")
147
- return renderChildren(node.children, options, depth);
148
- const inline = () => normalizeInline(renderChildren(node.children, options, depth));
149
- const block = () => normalizeDocument(renderChildren(node.children, options, depth));
150
- if (options.unsupported === "preserve" && hasUnsupportedAttrs(node)) {
175
+ return renderChildren(node.children, ctx, depth);
176
+ const inline = () => normalizeInline(renderChildren(node.children, ctx, depth));
177
+ const block = () => normalizeDocument(renderChildren(node.children, ctx, depth));
178
+ for (const handler of ctx.handlers) {
179
+ const result = handler(node, buildHandlerContext(node, ctx, depth));
180
+ if (result !== void 0) {
181
+ return blockTags.has(node.tag) ? blockWrap(result) : result;
182
+ }
183
+ }
184
+ if (ctx.options.unsupported === "preserve" && hasUnsupportedAttrs(node)) {
151
185
  return blockTags.has(node.tag) ? blockWrap(node.raw) : node.raw;
152
186
  }
153
187
  if (/^h[1-6]$/.test(node.tag))
154
188
  return blockWrap(`${"#".repeat(Number(node.tag[1]))} ${inline()}`);
155
189
  if (node.tag === "p")
156
- return blockWrap(normalizeParagraph(renderChildren(node.children, options, depth)));
190
+ return blockWrap(normalizeParagraph(renderChildren(node.children, ctx, depth)));
157
191
  if (node.tag === "br")
158
192
  return " \n";
159
193
  if (node.tag === "hr")
@@ -175,13 +209,13 @@ function renderNode(node, options, depth) {
175
209
  if (node.tag === "blockquote")
176
210
  return blockWrap(prefixLines(block(), "> "));
177
211
  if (node.tag === "ul" || node.tag === "ol")
178
- return blockWrap(renderList(node, options, depth));
212
+ return blockWrap(renderList(node, ctx, depth));
179
213
  if (node.tag === "li")
180
214
  return inline();
181
215
  if (node.tag === "table") {
182
- if (options.unsupported === "preserve" && tableHasUnsupportedStructure(node))
216
+ if (ctx.options.unsupported === "preserve" && tableHasUnsupportedStructure(node))
183
217
  return blockWrap(node.raw);
184
- return blockWrap(renderTable(node, options));
218
+ return blockWrap(renderTable(node, ctx));
185
219
  }
186
220
  if (node.tag === "thead" || node.tag === "tbody" || node.tag === "tr" || node.tag === "th" || node.tag === "td")
187
221
  return inline();
@@ -190,9 +224,9 @@ function renderNode(node, options, depth) {
190
224
  if (node.tag === "span")
191
225
  return inline();
192
226
  if (passthroughInlineTags.has(node.tag) || blockTags.has(node.tag)) {
193
- return options.unsupported === "preserve" ? node.raw : block();
227
+ return ctx.options.unsupported === "preserve" ? node.raw : block();
194
228
  }
195
- return options.unsupported === "preserve" ? node.raw : inline();
229
+ return ctx.options.unsupported === "preserve" ? node.raw : inline();
196
230
  }
197
231
  function renderLink(node, text) {
198
232
  const href = node.attrs.href;
@@ -225,19 +259,19 @@ function renderCodeBlock(node) {
225
259
  ${code}
226
260
  ${fence}`;
227
261
  }
228
- function renderList(node, options, depth) {
262
+ function renderList(node, ctx, depth) {
229
263
  return node.children.filter((child) => child.type === "element" && child.tag === "li").map((item, index) => {
230
264
  const marker = node.tag === "ol" ? `${Number(node.attrs.start ?? 1) + index}. ` : "- ";
231
- const body = normalizeListItemBody(renderChildren(item.children, options, depth + 1));
265
+ const body = normalizeListItemBody(renderChildren(item.children, ctx, depth + 1));
232
266
  return indentContinuation(`${marker}${body}`, marker.length);
233
267
  }).join("\n");
234
268
  }
235
- function renderTable(node, options) {
269
+ function renderTable(node, ctx) {
236
270
  const rows = collectRows(node).map((row) => row.children.filter((cell) => cell.type === "element" && (cell.tag === "th" || cell.tag === "td"))).filter((row) => row.length > 0);
237
271
  if (rows.length === 0)
238
272
  return "";
239
273
  const width = Math.max(...rows.map((row) => row.length));
240
- const values = rows.map((row) => Array.from({ length: width }, (_, index) => normalizeInline(renderChildren(row[index]?.children ?? [], options, 0))));
274
+ const values = rows.map((row) => Array.from({ length: width }, (_, index) => normalizeInline(renderChildren(row[index]?.children ?? [], ctx, 0))));
241
275
  const header = values[0];
242
276
  const separator = Array.from({ length: width }, () => "---");
243
277
  const body = values.slice(1);
@@ -334,6 +368,20 @@ function longestRun(value, char) {
334
368
  }
335
369
  return longest;
336
370
  }
371
+ var htmlChildElements = (node) => node.type === "element" || node.type === "root" ? node.children.filter((child) => child.type === "element") : [];
372
+ var findHtmlElement = (node, predicate) => htmlChildElements(node).find(predicate);
373
+ var hasHtmlClass = (node, className) => node.type === "element" && (node.attrs.class ?? "").split(/\s+/).includes(className);
374
+ var findHtmlDescendant = (node, predicate) => {
375
+ for (const child of htmlChildElements(node)) {
376
+ if (predicate(child))
377
+ return child;
378
+ const nested = findHtmlDescendant(child, predicate);
379
+ if (nested)
380
+ return nested;
381
+ }
382
+ return void 0;
383
+ };
384
+ var htmlRoot = (children) => ({ type: "root", children });
337
385
 
338
386
  // ../nizel/dist/collect.js
339
387
  function collect(ast) {
@@ -2374,6 +2422,10 @@ function mergeOptions(left, right) {
2374
2422
  ...left.variables ?? {},
2375
2423
  ...right.variables ?? {}
2376
2424
  },
2425
+ meta: {
2426
+ ...left.meta ?? {},
2427
+ ...right.meta ?? {}
2428
+ },
2377
2429
  elements: {
2378
2430
  ...left.elements ?? {},
2379
2431
  ...right.elements ?? {}
@@ -4845,7 +4897,22 @@ var abbrPlugin = (options = {}) => {
4845
4897
  afterParse(ast) {
4846
4898
  return replaceAbbreviations(ast, definitions);
4847
4899
  }
4848
- }
4900
+ },
4901
+ htmlToMarkdown: abbrToMarkdown()
4902
+ };
4903
+ };
4904
+ var abbrToMarkdown = () => {
4905
+ const seen = /* @__PURE__ */ new Set();
4906
+ return (node, ctx) => {
4907
+ if (node.type !== "element" || node.tag !== "abbr")
4908
+ return void 0;
4909
+ const term = ctx.text(node).trim();
4910
+ const title = node.attrs.title;
4911
+ if (title !== void 0 && term && !seen.has(term)) {
4912
+ seen.add(term);
4913
+ ctx.epilogue(`*[${term}]: ${title}`);
4914
+ }
4915
+ return term;
4849
4916
  };
4850
4917
  };
4851
4918
  var extractAbbreviations = (markdown) => {
@@ -4913,7 +4980,25 @@ var alertPlugin = (options = {}) => {
4913
4980
  blocks,
4914
4981
  hooks: {
4915
4982
  beforeParse: transformGitHubAlerts
4916
- }
4983
+ },
4984
+ htmlToMarkdown: alertToMarkdown(options)
4985
+ };
4986
+ };
4987
+ var alertToMarkdown = (options = {}) => {
4988
+ const rootClass = options.className ?? "alert";
4989
+ return (node, ctx) => {
4990
+ if (node.type !== "element" || node.tag !== "div")
4991
+ return void 0;
4992
+ const type = node.attrs["data-alert"];
4993
+ if (!type)
4994
+ return void 0;
4995
+ const titleEl = findHtmlElement(node, (el) => el.tag === "p" && hasHtmlClass(el, `${rootClass}__title`));
4996
+ const contentEl = findHtmlElement(node, (el) => hasHtmlClass(el, `${rootClass}__content`));
4997
+ const title = titleEl ? ctx.text(titleEl).trim() : "";
4998
+ const body = contentEl ? ctx.block(contentEl) : "";
4999
+ return `${title ? `::${type} ${title}` : `::${type}`}
5000
+ ${body}
5001
+ ::`;
4917
5002
  };
4918
5003
  };
4919
5004
  var transformGitHubAlerts = (markdown) => {
@@ -4998,9 +5083,21 @@ var autolinkPlugin = (options = {}) => {
4998
5083
  name: "autolink",
4999
5084
  options: {
5000
5085
  autolinks: resolveAutolinkPluginOptions(options)
5001
- }
5086
+ },
5087
+ htmlToMarkdown: autolinkToMarkdown()
5002
5088
  });
5003
5089
  };
5090
+ var autolinkToMarkdown = () => (node, ctx) => {
5091
+ if (node.type !== "element" || node.tag !== "a")
5092
+ return void 0;
5093
+ const href = node.attrs.href;
5094
+ if (!href)
5095
+ return void 0;
5096
+ const extras = Object.keys(node.attrs).filter((key) => key !== "href" && key !== "target" && key !== "rel");
5097
+ if (extras.length > 0)
5098
+ return void 0;
5099
+ return ctx.text(node).trim() === href ? href : void 0;
5100
+ };
5004
5101
 
5005
5102
  // ../plugin-code-copy/dist/index.js
5006
5103
  var codeCopyPlugin = (options = {}) => {
@@ -5023,9 +5120,34 @@ var codeCopyPlugin = (options = {}) => {
5023
5120
  },
5024
5121
  hooks: {
5025
5122
  afterParse: wrapCodeBlocks
5026
- }
5123
+ },
5124
+ htmlToMarkdown: codeCopyToMarkdown()
5027
5125
  });
5028
5126
  };
5127
+ var codeCopyToMarkdown = () => (node, ctx) => {
5128
+ if (node.type !== "element" || node.tag !== "figure" || node.attrs["data-nizel-code-copy"] === void 0)
5129
+ return void 0;
5130
+ const source = findHtmlDescendant(node, (el) => el.tag === "textarea" && el.attrs["data-nizel-copy-source"] !== void 0);
5131
+ if (!source) {
5132
+ const body = htmlChildElements(node).filter((el) => !["figcaption", "textarea", "button"].includes(el.tag));
5133
+ return ctx.block(htmlRoot(body));
5134
+ }
5135
+ const code = ctx.text(source).replace(/\n$/, "");
5136
+ const pre = findHtmlDescendant(node, (el) => el.tag === "pre");
5137
+ const codeEl = pre ? findHtmlElement(pre, (el) => el.tag === "code") : void 0;
5138
+ const codeClass = codeEl?.attrs.class ?? "";
5139
+ const langClass = codeClass.match(/\blanguage-([A-Za-z0-9_-]+)/)?.[1];
5140
+ const isMermaid = findHtmlDescendant(node, (el) => el.tag === "div" && el.attrs.class === "mermaid") !== void 0;
5141
+ const lang = langClass ?? pre?.attrs["data-language"] ?? (isMermaid ? "mermaid" : "");
5142
+ const fence = fenceFor(code);
5143
+ return `${fence}${lang}
5144
+ ${code}
5145
+ ${fence}`;
5146
+ };
5147
+ var fenceFor = (code) => {
5148
+ const longest = Math.max(0, ...(code.match(/`+/g) ?? []).map((run) => run.length));
5149
+ return "`".repeat(Math.max(3, longest + 1));
5150
+ };
5029
5151
  var wrapCodeBlocks = (ast) => ({
5030
5152
  ...ast,
5031
5153
  children: ast.children.map(wrapCodeBlock)
@@ -5102,7 +5224,28 @@ var citationsPlugin = (options = {}) => {
5102
5224
  afterParse(ast) {
5103
5225
  return appendBibliography(replaceCitationRefs(ast, citations), citations);
5104
5226
  }
5227
+ },
5228
+ htmlToMarkdown: citationsToMarkdown(options)
5229
+ };
5230
+ };
5231
+ var citationsToMarkdown = (options = {}) => {
5232
+ const className = options.className ?? "citations";
5233
+ return (node, ctx) => {
5234
+ if (node.type !== "element")
5235
+ return void 0;
5236
+ if (node.tag === "a" && hasHtmlClass(node, "citation")) {
5237
+ const match = /^#cite-(.+)$/.exec(node.attrs.href ?? "");
5238
+ return match ? `[@${match[1]}]` : void 0;
5239
+ }
5240
+ if (node.tag === "section" && hasHtmlClass(node, className)) {
5241
+ const list = findHtmlElement(node, (el) => el.tag === "ol");
5242
+ const items = list ? htmlChildElements(list).filter((el) => el.tag === "li") : [];
5243
+ return items.map((li) => {
5244
+ const id = /^cite-(.+)$/.exec(li.attrs.id ?? "")?.[1] ?? "";
5245
+ return `[@${id}]: ${ctx.block(htmlRoot(li.children)).trim()}`;
5246
+ }).join("\n");
5105
5247
  }
5248
+ return void 0;
5106
5249
  };
5107
5250
  };
5108
5251
  var extractCitations = (markdown) => {
@@ -5204,9 +5347,28 @@ var deflistPlugin = (options = {}) => {
5204
5347
  beforeParse(markdown) {
5205
5348
  return transformDefinitionLists(markdown, options);
5206
5349
  }
5207
- }
5350
+ },
5351
+ htmlToMarkdown: deflistToMarkdown(options)
5208
5352
  };
5209
5353
  };
5354
+ var deflistToMarkdown = (options = {}) => (node, ctx) => {
5355
+ if (node.type !== "element" || node.tag !== "dl")
5356
+ return void 0;
5357
+ if (options.className) {
5358
+ if (node.attrs.class !== options.className)
5359
+ return void 0;
5360
+ } else if (node.attrs.class !== void 0) {
5361
+ return void 0;
5362
+ }
5363
+ const lines = [];
5364
+ for (const el of htmlChildElements(node)) {
5365
+ if (el.tag === "dt")
5366
+ lines.push(ctx.inline(el));
5367
+ else if (el.tag === "dd")
5368
+ lines.push(`: ${ctx.inline(el)}`);
5369
+ }
5370
+ return lines.join("\n");
5371
+ };
5210
5372
  var transformDefinitionLists = (markdown, _options = {}) => {
5211
5373
  const lines = markdown.split("\n");
5212
5374
  const result = [];
@@ -5293,8 +5455,23 @@ var detailsPlugin = (options = {}) => ({
5293
5455
  },
5294
5456
  hooks: {
5295
5457
  beforeParse: transformDetails
5296
- }
5458
+ },
5459
+ htmlToMarkdown: detailsToMarkdown(options)
5297
5460
  });
5461
+ var detailsToMarkdown = (options = {}) => {
5462
+ const className = options.className ?? "details";
5463
+ return (node, ctx) => {
5464
+ if (node.type !== "element" || node.tag !== "details" || node.attrs.class !== className)
5465
+ return void 0;
5466
+ const summaryEl = findHtmlElement(node, (el) => el.tag === "summary");
5467
+ const summary = summaryEl ? ctx.text(summaryEl).trim() : "Details";
5468
+ const body = ctx.block(htmlRoot(htmlChildElements(node).filter((el) => el.tag !== "summary"))).trim();
5469
+ return body ? `::details ${summary}
5470
+ ${body}
5471
+ ::` : `::details ${summary}
5472
+ ::`;
5473
+ };
5474
+ };
5298
5475
  var transformDetails = (markdown) => {
5299
5476
  return markdown.replace(/^:::details\s*(.*?)\s*$/gm, (_match, summary) => `::details ${summary || "Details"}`).replace(/^:::\s*$/gm, "::");
5300
5477
  };
@@ -5324,7 +5501,19 @@ var diagramsPlugin = (options = {}) => {
5324
5501
  afterParse(ast) {
5325
5502
  return transformDiagramCodes(ast, options);
5326
5503
  }
5327
- }
5504
+ },
5505
+ htmlToMarkdown: diagramsToMarkdown(options)
5506
+ };
5507
+ };
5508
+ var diagramsToMarkdown = (options = {}) => {
5509
+ const className = options.className ?? "mermaid";
5510
+ return (node, ctx) => {
5511
+ if (node.type !== "element" || node.tag !== "div" || node.attrs.class !== className)
5512
+ return void 0;
5513
+ const code = ctx.text(node).trim();
5514
+ return `\`\`\`mermaid
5515
+ ${code}
5516
+ \`\`\``;
5328
5517
  };
5329
5518
  };
5330
5519
  var transformDiagramCodes = (ast, options = {}) => ({
@@ -5469,9 +5658,45 @@ var footnotesPlugin = (options = {}) => {
5469
5658
  hooks: {
5470
5659
  beforeParse: transformFootnotes,
5471
5660
  afterParse: transformFootnoteRefs
5661
+ },
5662
+ htmlToMarkdown: footnotesToMarkdown(options)
5663
+ };
5664
+ };
5665
+ var footnotesToMarkdown = (options = {}) => {
5666
+ const className = options.className ?? "footnotes";
5667
+ return (node, ctx) => {
5668
+ if (node.type !== "element")
5669
+ return void 0;
5670
+ if (node.tag === "sup") {
5671
+ const anchor = findHtmlElement(node, (el) => el.tag === "a" && hasHtmlClass(el, "footnote-ref"));
5672
+ const match = anchor ? /^#fn-(.+)$/.exec(anchor.attrs.href ?? "") : void 0;
5673
+ return match ? `[^${match[1]}]` : void 0;
5674
+ }
5675
+ if (node.tag === "section" && hasHtmlClass(node, className)) {
5676
+ const list = findHtmlElement(node, (el) => el.tag === "ol");
5677
+ const items = list ? htmlChildElements(list).filter((el) => el.tag === "li") : [];
5678
+ return items.map((li) => {
5679
+ const id = /^fn-(.+)$/.exec(li.attrs.id ?? "")?.[1] ?? "";
5680
+ const body = ctx.block(htmlRoot(stripBackrefs(li.children, className))).trim();
5681
+ return `[^${id}]: ${body}`;
5682
+ }).join("\n");
5472
5683
  }
5684
+ return void 0;
5473
5685
  };
5474
5686
  };
5687
+ var stripBackrefs = (nodes, className) => {
5688
+ const result = [];
5689
+ for (const node of nodes) {
5690
+ if (node.type === "element") {
5691
+ if (hasHtmlClass(node, `${className}__backref`))
5692
+ continue;
5693
+ result.push({ ...node, children: stripBackrefs(node.children, className) });
5694
+ } else {
5695
+ result.push(node);
5696
+ }
5697
+ }
5698
+ return result;
5699
+ };
5475
5700
  var transformFootnotes = (markdown) => {
5476
5701
  const lines = markdown.split("\n");
5477
5702
  const body = [];
@@ -5617,9 +5842,46 @@ var taskListPlugin = (options = {}) => {
5617
5842
  afterParse(ast) {
5618
5843
  return transformTaskLists(ast, mode);
5619
5844
  }
5620
- }
5845
+ },
5846
+ htmlToMarkdown: taskListToMarkdown()
5621
5847
  };
5622
5848
  };
5849
+ var CHECKBOX_ATTR = "data-nizel-task-checkbox";
5850
+ var isCheckbox = (node) => node.type === "element" && node.tag === "input" && node.attrs?.[CHECKBOX_ATTR] !== void 0;
5851
+ var taskListToMarkdown = () => (node, ctx) => {
5852
+ if (node.type !== "element" || node.tag !== "ul" && node.tag !== "ol")
5853
+ return void 0;
5854
+ const items = htmlChildElements(node).filter((el) => el.tag === "li");
5855
+ if (!items.some((item) => findHtmlDescendant(item, isCheckbox)))
5856
+ return void 0;
5857
+ const ordered = node.tag === "ol";
5858
+ const start = Number(node.attrs.start ?? 1);
5859
+ return items.map((item, index) => {
5860
+ const checkbox = findHtmlDescendant(item, isCheckbox);
5861
+ const body = ctx.block(htmlRoot(cleanTaskChildren(item.children))).trim();
5862
+ const baseMarker = ordered ? `${start + index}. ` : "- ";
5863
+ const marker = checkbox ? `${baseMarker}${checkbox.attrs.checked !== void 0 ? "[x] " : "[ ] "}` : baseMarker;
5864
+ return ctx.indent(`${marker}${body}`, marker.length);
5865
+ }).join("\n");
5866
+ };
5867
+ var cleanTaskChildren = (nodes) => {
5868
+ const result = [];
5869
+ for (const node of nodes) {
5870
+ if (node.type === "element") {
5871
+ if (isCheckbox(node))
5872
+ continue;
5873
+ const cleaned = { ...node, children: cleanTaskChildren(node.children) };
5874
+ if (hasHtmlClass(cleaned, "nizel-task-list__label") || hasHtmlClass(cleaned, "nizel-task-list__content")) {
5875
+ result.push(...cleaned.children);
5876
+ } else {
5877
+ result.push(cleaned);
5878
+ }
5879
+ } else {
5880
+ result.push(node);
5881
+ }
5882
+ }
5883
+ return result;
5884
+ };
5623
5885
  var transformTaskLists = (ast, mode = "view") => {
5624
5886
  return { ...ast, children: ast.children.map((node) => transformBlock5(node, mode)) };
5625
5887
  };
@@ -5701,8 +5963,17 @@ var headingAnchorsPlugin = (options = {}) => ({
5701
5963
  afterParse(ast) {
5702
5964
  return addHeadingAnchors(ast, options);
5703
5965
  }
5704
- }
5966
+ },
5967
+ htmlToMarkdown: headingAnchorsToMarkdown(options)
5705
5968
  });
5969
+ var headingAnchorsToMarkdown = (options = {}) => {
5970
+ const className = options.className ?? "heading-anchor";
5971
+ return (node) => {
5972
+ if (node.type !== "element" || node.tag !== "a" || !hasHtmlClass(node, className))
5973
+ return void 0;
5974
+ return "";
5975
+ };
5976
+ };
5706
5977
  var addHeadingAnchors = (ast, options = {}) => ({
5707
5978
  ...ast,
5708
5979
  children: ast.children.map((node) => {
@@ -5722,6 +5993,177 @@ var addHeadingAnchors = (ast, options = {}) => ({
5722
5993
  });
5723
5994
  var escapeHtml8 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5724
5995
 
5996
+ // ../nizel-plugin-hidden-comments/dist/index.js
5997
+ var hiddenCommentsPlugin = (options = {}) => {
5998
+ const normalized = normalizeOptions(options);
5999
+ let comments = /* @__PURE__ */ new Map();
6000
+ let nextIndex = 0;
6001
+ return {
6002
+ name: "hidden-comments",
6003
+ hooks: {
6004
+ beforeParse(markdown) {
6005
+ comments = /* @__PURE__ */ new Map();
6006
+ nextIndex = 0;
6007
+ return transformMarkdownComments(markdown, normalized, (comment) => {
6008
+ const token = `NIZEL_HIDDEN_COMMENT_${nextIndex}_${Math.random().toString(36).slice(2)}`;
6009
+ nextIndex += 1;
6010
+ comments.set(token, comment);
6011
+ return token;
6012
+ });
6013
+ },
6014
+ afterRender(html) {
6015
+ let nextHtml = restoreCommentTokens(html, comments, normalized);
6016
+ if (normalized.injectCss && normalized.mode !== "remove") {
6017
+ nextHtml = `${hiddenCommentsStyleTag(normalized.className)}
6018
+ ${nextHtml}`;
6019
+ }
6020
+ return nextHtml;
6021
+ }
6022
+ },
6023
+ htmlToMarkdown: hiddenCommentsToMarkdown(normalized)
6024
+ };
6025
+ };
6026
+ var transformMarkdownComments = (markdown, options = {}, createToken = (comment) => renderCommentElement(comment, normalizeOptions(options))) => {
6027
+ const normalized = normalizeOptions(options);
6028
+ if (typeof markdown !== "string" || markdown.indexOf("<!--") === -1)
6029
+ return markdown;
6030
+ return replaceMarkdownCommentsOutsideFences(markdown, (comment) => {
6031
+ if (normalized.mode === "remove")
6032
+ return "";
6033
+ return createToken(comment);
6034
+ });
6035
+ };
6036
+ var hiddenCommentsToMarkdown = (options = {}) => {
6037
+ const normalized = normalizeOptions(options);
6038
+ return (node, ctx) => {
6039
+ if (node.type !== "element" || node.tag !== "span")
6040
+ return void 0;
6041
+ if (!isHiddenCommentElement(node.attrs, normalized.className))
6042
+ return void 0;
6043
+ const text = ctx.text(node).trim();
6044
+ if (/^<!--[\s\S]*-->$/.test(text))
6045
+ return text;
6046
+ return `<!-- ${text.replace(/^<!--|-->$/g, "").trim()} -->`;
6047
+ };
6048
+ };
6049
+ var isHiddenCommentElement = (attrs, className) => {
6050
+ if ("data-nizel-hidden-comment" in attrs)
6051
+ return true;
6052
+ return (attrs.class ?? "").split(/\s+/).includes(className);
6053
+ };
6054
+ var normalizeOptions = (options) => ({
6055
+ mode: options.mode ?? "hide",
6056
+ injectCss: options.injectCss ?? false,
6057
+ className: options.className ?? "nizel-hidden-comment"
6058
+ });
6059
+ var replaceMarkdownCommentsOutsideFences = (markdown, replacer) => {
6060
+ const lines = markdown.split(/(\n)/);
6061
+ let result = "";
6062
+ let line = "";
6063
+ let fence;
6064
+ for (const part of lines) {
6065
+ line += part;
6066
+ if (part !== "\n")
6067
+ continue;
6068
+ const next = processLine(line, fence, replacer);
6069
+ result += next.line;
6070
+ fence = next.fence;
6071
+ line = "";
6072
+ }
6073
+ if (line) {
6074
+ const next = processLine(line, fence, replacer);
6075
+ result += next.line;
6076
+ }
6077
+ return result;
6078
+ };
6079
+ var processLine = (line, fence, replacer) => {
6080
+ if (fence) {
6081
+ return {
6082
+ line,
6083
+ fence: isClosingFenceLine(line, fence) ? void 0 : fence
6084
+ };
6085
+ }
6086
+ const openingFence = getOpeningFenceLine(line);
6087
+ if (openingFence)
6088
+ return { line, fence: openingFence };
6089
+ return {
6090
+ line: line.replace(/<!--[\s\S]*?-->/g, replacer),
6091
+ fence
6092
+ };
6093
+ };
6094
+ var getOpeningFenceLine = (line) => {
6095
+ const match = /^( {0,3})(`{3,}|~{3,})/.exec(line);
6096
+ if (!match)
6097
+ return void 0;
6098
+ return { marker: match[2].charAt(0), length: match[2].length };
6099
+ };
6100
+ var isClosingFenceLine = (line, fence) => {
6101
+ const lineText = line.replace(/\r?\n$/, "");
6102
+ const escapedMarker = fence.marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6103
+ const pattern = new RegExp(`^ {0,3}${escapedMarker}{${fence.length},} *$`);
6104
+ return pattern.test(lineText);
6105
+ };
6106
+ var renderCommentElement = (comment, options) => {
6107
+ const classes = [
6108
+ options.className,
6109
+ `${options.className}--${options.mode}`,
6110
+ "print-hidden-comment"
6111
+ ];
6112
+ const mode = escapeHtml9(options.mode);
6113
+ return `<span class="${classes.map(escapeHtml9).join(" ")}" data-nizel-hidden-comment="" data-hidden-comment-mode="${mode}">${escapeHtml9(comment)}</span>`;
6114
+ };
6115
+ var restoreCommentTokens = (html, comments, options) => {
6116
+ if (!comments.size || options.mode === "remove")
6117
+ return html;
6118
+ let nextHtml = html;
6119
+ for (const [token, comment] of comments) {
6120
+ const tokenPattern = new RegExp(escapeRegExp2(token), "g");
6121
+ const blockPattern = new RegExp(`<p>\\s*${escapeRegExp2(token)}\\s*</p>`, "g");
6122
+ const rendered = renderCommentElement(comment, options);
6123
+ nextHtml = nextHtml.replace(blockPattern, rendered).replace(tokenPattern, rendered);
6124
+ }
6125
+ return nextHtml;
6126
+ };
6127
+ var hiddenCommentsStyleTag = (className) => {
6128
+ return `<style data-nizel-plugin="hidden-comments">${hiddenCommentsCss(className)}</style>`;
6129
+ };
6130
+ var hiddenCommentsCss = (className) => {
6131
+ const selector = `.${className.replace(/[^A-Za-z0-9_-]/g, "")}`;
6132
+ return `${selector} {
6133
+ user-select: text;
6134
+ }
6135
+ ${selector}--hide {
6136
+ border: 0;
6137
+ color: rgba(255, 255, 255, 0);
6138
+ display: inline-block;
6139
+ font-size: 1px;
6140
+ height: 0;
6141
+ line-height: 0;
6142
+ margin: 0;
6143
+ max-height: 0;
6144
+ max-width: 0;
6145
+ overflow: hidden;
6146
+ padding: 0;
6147
+ vertical-align: baseline;
6148
+ width: 0;
6149
+ }
6150
+ ${selector}--small {
6151
+ color: color-mix(in srgb, currentColor 60%, transparent);
6152
+ display: inline;
6153
+ font-size: 0.75em;
6154
+ line-height: 1;
6155
+ }
6156
+ ${selector}--render {
6157
+ display: inline;
6158
+ }`;
6159
+ };
6160
+ var escapeHtml9 = (value) => {
6161
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6162
+ };
6163
+ var escapeRegExp2 = (value) => {
6164
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6165
+ };
6166
+
5725
6167
  // ../plugin-math/dist/index.js
5726
6168
  var mathPlugin = (options = {}) => {
5727
6169
  return {
@@ -5734,7 +6176,26 @@ var mathPlugin = (options = {}) => {
5734
6176
  afterParse(ast) {
5735
6177
  return transformInlineMath(ast, options);
5736
6178
  }
6179
+ },
6180
+ htmlToMarkdown: mathToMarkdown(options)
6181
+ };
6182
+ };
6183
+ var mathToMarkdown = (options = {}) => {
6184
+ const inlineTokens = (options.inlineClassName ?? "math math-inline").split(/\s+/);
6185
+ const blockTokens = (options.blockClassName ?? "math math-display").split(/\s+/);
6186
+ return (node, ctx) => {
6187
+ if (node.type !== "element")
6188
+ return void 0;
6189
+ const tokens = node.attrs.class ? node.attrs.class.split(/\s+/) : [];
6190
+ if (node.tag === "span" && inlineTokens.every((token) => tokens.includes(token))) {
6191
+ return `$${ctx.text(node)}$`;
6192
+ }
6193
+ if (node.tag === "div" && blockTokens.every((token) => tokens.includes(token))) {
6194
+ return `$$
6195
+ ${ctx.text(node).trim()}
6196
+ $$`;
5737
6197
  }
6198
+ return void 0;
5738
6199
  };
5739
6200
  };
5740
6201
  var transformBlockMath = (markdown) => {
@@ -5820,7 +6281,7 @@ var transformText2 = (value, options) => {
5820
6281
  nodes.push({ type: "text", value: value.slice(lastIndex, match.index) });
5821
6282
  nodes.push({
5822
6283
  type: "inlineHtml",
5823
- value: `<span class="${escapeHtml9(options.inlineClassName ?? "math math-inline")}">${escapeHtml9(match[1])}</span>`
6284
+ value: `<span class="${escapeHtml10(options.inlineClassName ?? "math math-inline")}">${escapeHtml10(match[1])}</span>`
5824
6285
  });
5825
6286
  lastIndex = pattern.lastIndex;
5826
6287
  }
@@ -5828,7 +6289,7 @@ var transformText2 = (value, options) => {
5828
6289
  nodes.push({ type: "text", value: value.slice(lastIndex) });
5829
6290
  return nodes;
5830
6291
  };
5831
- var escapeHtml9 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6292
+ var escapeHtml10 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5832
6293
 
5833
6294
  // ../plugin-media/dist/index.js
5834
6295
  var mediaPlugin = (options = {}) => ({
@@ -5837,8 +6298,26 @@ var mediaPlugin = (options = {}) => ({
5837
6298
  afterRender(html) {
5838
6299
  return enhanceImages(html, options);
5839
6300
  }
5840
- }
6301
+ },
6302
+ htmlToMarkdown: mediaToMarkdown(options)
5841
6303
  });
6304
+ var mediaToMarkdown = (options = {}) => {
6305
+ const className = options.figureClassName ?? "media-figure";
6306
+ return (node, ctx) => {
6307
+ if (node.type !== "element" || node.tag !== "figure" || node.attrs.class !== className)
6308
+ return void 0;
6309
+ const img = findHtmlElement(node, (el) => el.tag === "img");
6310
+ if (!img)
6311
+ return void 0;
6312
+ const attrs = { src: img.attrs.src ?? "" };
6313
+ if (img.attrs.alt !== void 0)
6314
+ attrs.alt = img.attrs.alt;
6315
+ if (img.attrs.title !== void 0)
6316
+ attrs.title = img.attrs.title;
6317
+ const cleanImg = { type: "element", tag: "img", attrs, children: [], raw: "" };
6318
+ return ctx.inline({ type: "root", children: [cleanImg] });
6319
+ };
6320
+ };
5842
6321
  var enhanceImages = (html, options = {}) => {
5843
6322
  const lazy = options.lazy !== false;
5844
6323
  const responsive = options.responsive !== false;
@@ -5846,7 +6325,7 @@ var enhanceImages = (html, options = {}) => {
5846
6325
  return html.replace(/<p>(<img\b[^>]*>)<\/p>/g, (_match, img) => {
5847
6326
  const enhanced = enhanceImageTag(img, { lazy, responsive });
5848
6327
  const caption = /alt="([^"]+)"/.exec(enhanced)?.[1];
5849
- return `<figure class="${escapeHtml10(className)}">${enhanced}${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
6328
+ return `<figure class="${escapeHtml11(className)}">${enhanced}${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
5850
6329
  });
5851
6330
  };
5852
6331
  var enhanceImageTag = (img, options) => {
@@ -5857,7 +6336,7 @@ var enhanceImageTag = (img, options) => {
5857
6336
  next = next.replace(/\/?>$/, ' decoding="async" />');
5858
6337
  return next;
5859
6338
  };
5860
- var escapeHtml10 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6339
+ var escapeHtml11 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5861
6340
 
5862
6341
  // ../plugin-sanitize/dist/index.js
5863
6342
  var sanitizePlugin = (options = {}) => ({
@@ -5901,9 +6380,30 @@ var shikiPlugin = (options = {}) => {
5901
6380
  }
5902
6381
  }
5903
6382
  }
5904
- }
6383
+ },
6384
+ htmlToMarkdown: shikiToMarkdown()
5905
6385
  });
5906
6386
  };
6387
+ var shikiToMarkdown = () => (node, ctx) => {
6388
+ if (node.type !== "element" || node.tag !== "pre")
6389
+ return void 0;
6390
+ const isShiki = hasHtmlClass(node, "shiki") || node.attrs.style !== void 0 || node.attrs["data-language"] !== void 0;
6391
+ if (!isShiki)
6392
+ return void 0;
6393
+ const codeEl = findHtmlElement(node, (el) => el.tag === "code");
6394
+ const codeClass = codeEl?.attrs.class ?? "";
6395
+ const langClass = codeClass.match(/\blanguage-([A-Za-z0-9_-]+)/)?.[1];
6396
+ const lang = node.attrs["data-language"] ?? langClass ?? "";
6397
+ const code = ctx.text(node).replace(/\n$/, "");
6398
+ const fence = fenceFor2(code);
6399
+ return `${fence}${lang}
6400
+ ${code}
6401
+ ${fence}`;
6402
+ };
6403
+ var fenceFor2 = (code) => {
6404
+ const longest = Math.max(0, ...(code.match(/`+/g) ?? []).map((run) => run.length));
6405
+ return "`".repeat(Math.max(3, longest + 1));
6406
+ };
5907
6407
  var fallback = (node, ctx) => {
5908
6408
  const language = node.lang ? ` class="language-${ctx.escape(node.lang)}"` : "";
5909
6409
  return `<pre><code${language}>${ctx.escape(node.code)}</code></pre>`;
@@ -5922,8 +6422,17 @@ var tocPlugin = (options = {}) => ({
5922
6422
  afterParse(ast) {
5923
6423
  return fillTocBlocks(ast, options);
5924
6424
  }
5925
- }
6425
+ },
6426
+ htmlToMarkdown: tocToMarkdown(options)
5926
6427
  });
6428
+ var tocToMarkdown = (options = {}) => {
6429
+ const className = options.className ?? "toc";
6430
+ return (node) => {
6431
+ if (node.type !== "element" || node.tag !== "nav" || !hasHtmlClass(node, className))
6432
+ return void 0;
6433
+ return "[[toc]]";
6434
+ };
6435
+ };
5927
6436
  var fillTocBlocks = (ast, options = {}) => {
5928
6437
  const minDepth = options.minDepth ?? 2;
5929
6438
  const maxDepth = options.maxDepth ?? 6;
@@ -5957,7 +6466,24 @@ var typographyPlugin = (options = {}) => {
5957
6466
  afterParse(ast) {
5958
6467
  return transformTypography(ast, options);
5959
6468
  }
5960
- }
6469
+ },
6470
+ htmlToMarkdown: typographyToMarkdown(options)
6471
+ };
6472
+ };
6473
+ var typographyToMarkdown = (options = {}) => {
6474
+ const mark = options.mark !== false;
6475
+ const subscript = options.subscript !== false;
6476
+ const superscript = options.superscript !== false;
6477
+ return (node, ctx) => {
6478
+ if (node.type !== "element" || Object.keys(node.attrs).length > 0)
6479
+ return void 0;
6480
+ if (node.tag === "mark" && mark)
6481
+ return `==${ctx.inline(node)}==`;
6482
+ if (node.tag === "sub" && subscript)
6483
+ return `~${ctx.inline(node)}~`;
6484
+ if (node.tag === "sup" && superscript)
6485
+ return `^${ctx.inline(node)}^`;
6486
+ return void 0;
5961
6487
  };
5962
6488
  };
5963
6489
  var transformTypography = (ast, options = {}) => {
@@ -6013,11 +6539,11 @@ var transformText3 = (value, options) => {
6013
6539
  if (match.index > lastIndex)
6014
6540
  nodes.push({ type: "text", value: value.slice(lastIndex, match.index) });
6015
6541
  if (match[1] !== void 0 && mark)
6016
- nodes.push({ type: "inlineHtml", value: `<mark>${escapeHtml11(match[1])}</mark>` });
6542
+ nodes.push({ type: "inlineHtml", value: `<mark>${escapeHtml12(match[1])}</mark>` });
6017
6543
  else if (match[2] !== void 0 && subscript)
6018
- nodes.push({ type: "inlineHtml", value: `<sub>${escapeHtml11(match[2])}</sub>` });
6544
+ nodes.push({ type: "inlineHtml", value: `<sub>${escapeHtml12(match[2])}</sub>` });
6019
6545
  else if (match[3] !== void 0 && superscript)
6020
- nodes.push({ type: "inlineHtml", value: `<sup>${escapeHtml11(match[3])}</sup>` });
6546
+ nodes.push({ type: "inlineHtml", value: `<sup>${escapeHtml12(match[3])}</sup>` });
6021
6547
  else
6022
6548
  nodes.push({ type: "text", value: match[0] });
6023
6549
  lastIndex = pattern.lastIndex;
@@ -6026,7 +6552,7 @@ var transformText3 = (value, options) => {
6026
6552
  nodes.push({ type: "text", value: value.slice(lastIndex) });
6027
6553
  return nodes;
6028
6554
  };
6029
- var escapeHtml11 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6555
+ var escapeHtml12 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6030
6556
 
6031
6557
  // src/index.ts
6032
6558
  var supportedPlugins = [
@@ -6107,6 +6633,13 @@ var supportedPlugins = [
6107
6633
  defaultEnabled: false,
6108
6634
  category: "block"
6109
6635
  },
6636
+ {
6637
+ id: "hidden-comments",
6638
+ label: "Hidden Comments",
6639
+ description: "Control how Markdown HTML comments render in documents.",
6640
+ defaultEnabled: false,
6641
+ category: "core-behavior"
6642
+ },
6110
6643
  {
6111
6644
  id: "media",
6112
6645
  label: "Media",
@@ -6185,6 +6718,7 @@ var factories = {
6185
6718
  "frontmatter-ui": (options) => frontmatterUiPlugin(options["frontmatter-ui"]),
6186
6719
  gfm: (options) => gfmPlugins(options.gfm),
6187
6720
  "heading-anchors": (options) => headingAnchorsPlugin(options["heading-anchors"]),
6721
+ "hidden-comments": (options) => hiddenCommentsPlugin(options["hidden-comments"]),
6188
6722
  math: (options) => mathPlugin(options.math),
6189
6723
  media: (options) => mediaPlugin(options.media),
6190
6724
  sanitize: (options) => sanitizePlugin(options.sanitize),
@@ -6201,11 +6735,31 @@ var createPlugins = (enabledPlugins = defaultEnabledPlugins(), options = {}) =>
6201
6735
  };
6202
6736
  function useNizelKit(options = {}) {
6203
6737
  const { enabledPlugins, pluginOptions, plugins = [], ...nizelOptions } = options;
6204
- return useBrowserNizel({
6738
+ const resolvedPlugins = [...createPlugins(enabledPlugins, pluginOptions), ...plugins];
6739
+ const processor = useBrowserNizel({
6205
6740
  ...nizelOptions,
6206
- plugins: [...createPlugins(enabledPlugins, pluginOptions), ...plugins]
6741
+ plugins: resolvedPlugins
6207
6742
  });
6743
+ const htmlToMarkdown3 = processor.htmlToMarkdown.bind(processor);
6744
+ processor.htmlToMarkdown = (source, reverseOptions) => {
6745
+ return htmlToMarkdown3(source, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6746
+ };
6747
+ const nodeToMarkdown2 = processor.nodeToMarkdown.bind(processor);
6748
+ processor.nodeToMarkdown = (source, reverseOptions) => {
6749
+ return nodeToMarkdown2(source, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6750
+ };
6751
+ const selectionToMarkdown2 = processor.selectionToMarkdown.bind(processor);
6752
+ processor.selectionToMarkdown = (selection, reverseOptions) => {
6753
+ return selectionToMarkdown2(selection, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6754
+ };
6755
+ return processor;
6208
6756
  }
6757
+ var withKitHtmlToMarkdownPlugins = (options, plugins) => {
6758
+ return {
6759
+ ...options,
6760
+ plugins: [...plugins, ...options?.plugins ?? []]
6761
+ };
6762
+ };
6209
6763
  async function markdownToHtml(markdown, options) {
6210
6764
  return useNizelKit(options).html(markdown);
6211
6765
  }