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.
@@ -64,8 +64,36 @@ var NizelKit = (() => {
64
64
  ]);
65
65
  var passthroughInlineTags = /* @__PURE__ */ new Set(["abbr", "bdi", "bdo", "cite", "data", "dfn", "kbd", "mark", "q", "samp", "small", "sub", "sup", "time", "u", "var"]);
66
66
  function htmlToMarkdown(html, options = {}) {
67
+ const resolved = {
68
+ unsupported: options.unsupported ?? "preserve",
69
+ plugins: options.plugins ?? []
70
+ };
71
+ const handlers = resolved.plugins.flatMap((plugin) => {
72
+ const handler = plugin?.htmlToMarkdown;
73
+ return handler ? Array.isArray(handler) ? handler : [handler] : [];
74
+ });
75
+ const ctx = { options: resolved, handlers, epilogue: [] };
67
76
  const root = parseHtml(html);
68
- return normalizeDocument(renderChildren(root.children, { unsupported: options.unsupported ?? "preserve" }, 0));
77
+ const body = normalizeDocument(renderChildren(root.children, ctx, 0));
78
+ return ctx.epilogue.length ? normalizeDocument(`${body}
79
+
80
+ ${ctx.epilogue.join("\n\n")}`) : body;
81
+ }
82
+ function buildHandlerContext(node, ctx, depth) {
83
+ return {
84
+ options: ctx.options,
85
+ inline: (target) => normalizeInline(renderChildren(childrenOf(target), ctx, depth)),
86
+ block: (target) => normalizeDocument(renderChildren(childrenOf(target), ctx, depth)),
87
+ text: (target) => textContent(target),
88
+ indent: (value, size) => indentContinuation(value, size),
89
+ epilogue: (value) => {
90
+ if (value)
91
+ ctx.epilogue.push(value);
92
+ }
93
+ };
94
+ }
95
+ function childrenOf(node) {
96
+ return node.type === "element" || node.type === "root" ? node.children : [];
69
97
  }
70
98
  function parseHtml(html) {
71
99
  const root = { type: "root", children: [] };
@@ -167,25 +195,31 @@ var NizelKit = (() => {
167
195
  function slugifyText(value) {
168
196
  return value.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
169
197
  }
170
- function renderChildren(nodes, options, depth) {
171
- return nodes.map((node) => renderNode(node, options, depth)).join("");
198
+ function renderChildren(nodes, ctx, depth) {
199
+ return nodes.map((node) => renderNode(node, ctx, depth)).join("");
172
200
  }
173
- function renderNode(node, options, depth) {
201
+ function renderNode(node, ctx, depth) {
174
202
  if (node.type === "text")
175
203
  return collapseText(decodeEntities(node.value));
176
204
  if (node.type === "comment")
177
- return options.unsupported === "preserve" ? node.value : " ";
205
+ return ctx.options.unsupported === "preserve" ? node.value : " ";
178
206
  if (node.type === "root")
179
- return renderChildren(node.children, options, depth);
180
- const inline = () => normalizeInline(renderChildren(node.children, options, depth));
181
- const block = () => normalizeDocument(renderChildren(node.children, options, depth));
182
- if (options.unsupported === "preserve" && hasUnsupportedAttrs(node)) {
207
+ return renderChildren(node.children, ctx, depth);
208
+ const inline = () => normalizeInline(renderChildren(node.children, ctx, depth));
209
+ const block = () => normalizeDocument(renderChildren(node.children, ctx, depth));
210
+ for (const handler of ctx.handlers) {
211
+ const result = handler(node, buildHandlerContext(node, ctx, depth));
212
+ if (result !== void 0) {
213
+ return blockTags.has(node.tag) ? blockWrap(result) : result;
214
+ }
215
+ }
216
+ if (ctx.options.unsupported === "preserve" && hasUnsupportedAttrs(node)) {
183
217
  return blockTags.has(node.tag) ? blockWrap(node.raw) : node.raw;
184
218
  }
185
219
  if (/^h[1-6]$/.test(node.tag))
186
220
  return blockWrap(`${"#".repeat(Number(node.tag[1]))} ${inline()}`);
187
221
  if (node.tag === "p")
188
- return blockWrap(normalizeParagraph(renderChildren(node.children, options, depth)));
222
+ return blockWrap(normalizeParagraph(renderChildren(node.children, ctx, depth)));
189
223
  if (node.tag === "br")
190
224
  return " \n";
191
225
  if (node.tag === "hr")
@@ -207,13 +241,13 @@ var NizelKit = (() => {
207
241
  if (node.tag === "blockquote")
208
242
  return blockWrap(prefixLines(block(), "> "));
209
243
  if (node.tag === "ul" || node.tag === "ol")
210
- return blockWrap(renderList(node, options, depth));
244
+ return blockWrap(renderList(node, ctx, depth));
211
245
  if (node.tag === "li")
212
246
  return inline();
213
247
  if (node.tag === "table") {
214
- if (options.unsupported === "preserve" && tableHasUnsupportedStructure(node))
248
+ if (ctx.options.unsupported === "preserve" && tableHasUnsupportedStructure(node))
215
249
  return blockWrap(node.raw);
216
- return blockWrap(renderTable(node, options));
250
+ return blockWrap(renderTable(node, ctx));
217
251
  }
218
252
  if (node.tag === "thead" || node.tag === "tbody" || node.tag === "tr" || node.tag === "th" || node.tag === "td")
219
253
  return inline();
@@ -222,9 +256,9 @@ var NizelKit = (() => {
222
256
  if (node.tag === "span")
223
257
  return inline();
224
258
  if (passthroughInlineTags.has(node.tag) || blockTags.has(node.tag)) {
225
- return options.unsupported === "preserve" ? node.raw : block();
259
+ return ctx.options.unsupported === "preserve" ? node.raw : block();
226
260
  }
227
- return options.unsupported === "preserve" ? node.raw : inline();
261
+ return ctx.options.unsupported === "preserve" ? node.raw : inline();
228
262
  }
229
263
  function renderLink(node, text) {
230
264
  const href = node.attrs.href;
@@ -257,19 +291,19 @@ var NizelKit = (() => {
257
291
  ${code}
258
292
  ${fence}`;
259
293
  }
260
- function renderList(node, options, depth) {
294
+ function renderList(node, ctx, depth) {
261
295
  return node.children.filter((child) => child.type === "element" && child.tag === "li").map((item, index) => {
262
296
  const marker = node.tag === "ol" ? `${Number(node.attrs.start ?? 1) + index}. ` : "- ";
263
- const body = normalizeListItemBody(renderChildren(item.children, options, depth + 1));
297
+ const body = normalizeListItemBody(renderChildren(item.children, ctx, depth + 1));
264
298
  return indentContinuation(`${marker}${body}`, marker.length);
265
299
  }).join("\n");
266
300
  }
267
- function renderTable(node, options) {
301
+ function renderTable(node, ctx) {
268
302
  const rows = collectRows(node).map((row) => row.children.filter((cell) => cell.type === "element" && (cell.tag === "th" || cell.tag === "td"))).filter((row) => row.length > 0);
269
303
  if (rows.length === 0)
270
304
  return "";
271
305
  const width = Math.max(...rows.map((row) => row.length));
272
- const values = rows.map((row) => Array.from({ length: width }, (_, index) => normalizeInline(renderChildren(row[index]?.children ?? [], options, 0))));
306
+ const values = rows.map((row) => Array.from({ length: width }, (_, index) => normalizeInline(renderChildren(row[index]?.children ?? [], ctx, 0))));
273
307
  const header = values[0];
274
308
  const separator = Array.from({ length: width }, () => "---");
275
309
  const body = values.slice(1);
@@ -366,6 +400,20 @@ ${trimmed}
366
400
  }
367
401
  return longest;
368
402
  }
403
+ var htmlChildElements = (node) => node.type === "element" || node.type === "root" ? node.children.filter((child) => child.type === "element") : [];
404
+ var findHtmlElement = (node, predicate) => htmlChildElements(node).find(predicate);
405
+ var hasHtmlClass = (node, className) => node.type === "element" && (node.attrs.class ?? "").split(/\s+/).includes(className);
406
+ var findHtmlDescendant = (node, predicate) => {
407
+ for (const child of htmlChildElements(node)) {
408
+ if (predicate(child))
409
+ return child;
410
+ const nested = findHtmlDescendant(child, predicate);
411
+ if (nested)
412
+ return nested;
413
+ }
414
+ return void 0;
415
+ };
416
+ var htmlRoot = (children) => ({ type: "root", children });
369
417
 
370
418
  // ../nizel/dist/collect.js
371
419
  function collect(ast) {
@@ -2406,6 +2454,10 @@ ${trimmed}
2406
2454
  ...left.variables ?? {},
2407
2455
  ...right.variables ?? {}
2408
2456
  },
2457
+ meta: {
2458
+ ...left.meta ?? {},
2459
+ ...right.meta ?? {}
2460
+ },
2409
2461
  elements: {
2410
2462
  ...left.elements ?? {},
2411
2463
  ...right.elements ?? {}
@@ -4877,7 +4929,22 @@ ${body}
4877
4929
  afterParse(ast) {
4878
4930
  return replaceAbbreviations(ast, definitions);
4879
4931
  }
4880
- }
4932
+ },
4933
+ htmlToMarkdown: abbrToMarkdown()
4934
+ };
4935
+ };
4936
+ var abbrToMarkdown = () => {
4937
+ const seen = /* @__PURE__ */ new Set();
4938
+ return (node, ctx) => {
4939
+ if (node.type !== "element" || node.tag !== "abbr")
4940
+ return void 0;
4941
+ const term = ctx.text(node).trim();
4942
+ const title = node.attrs.title;
4943
+ if (title !== void 0 && term && !seen.has(term)) {
4944
+ seen.add(term);
4945
+ ctx.epilogue(`*[${term}]: ${title}`);
4946
+ }
4947
+ return term;
4881
4948
  };
4882
4949
  };
4883
4950
  var extractAbbreviations = (markdown) => {
@@ -4945,7 +5012,25 @@ ${body}
4945
5012
  blocks,
4946
5013
  hooks: {
4947
5014
  beforeParse: transformGitHubAlerts
4948
- }
5015
+ },
5016
+ htmlToMarkdown: alertToMarkdown(options)
5017
+ };
5018
+ };
5019
+ var alertToMarkdown = (options = {}) => {
5020
+ const rootClass = options.className ?? "alert";
5021
+ return (node, ctx) => {
5022
+ if (node.type !== "element" || node.tag !== "div")
5023
+ return void 0;
5024
+ const type = node.attrs["data-alert"];
5025
+ if (!type)
5026
+ return void 0;
5027
+ const titleEl = findHtmlElement(node, (el) => el.tag === "p" && hasHtmlClass(el, `${rootClass}__title`));
5028
+ const contentEl = findHtmlElement(node, (el) => hasHtmlClass(el, `${rootClass}__content`));
5029
+ const title = titleEl ? ctx.text(titleEl).trim() : "";
5030
+ const body = contentEl ? ctx.block(contentEl) : "";
5031
+ return `${title ? `::${type} ${title}` : `::${type}`}
5032
+ ${body}
5033
+ ::`;
4949
5034
  };
4950
5035
  };
4951
5036
  var transformGitHubAlerts = (markdown) => {
@@ -5030,9 +5115,21 @@ ${body}
5030
5115
  name: "autolink",
5031
5116
  options: {
5032
5117
  autolinks: resolveAutolinkPluginOptions(options)
5033
- }
5118
+ },
5119
+ htmlToMarkdown: autolinkToMarkdown()
5034
5120
  });
5035
5121
  };
5122
+ var autolinkToMarkdown = () => (node, ctx) => {
5123
+ if (node.type !== "element" || node.tag !== "a")
5124
+ return void 0;
5125
+ const href = node.attrs.href;
5126
+ if (!href)
5127
+ return void 0;
5128
+ const extras = Object.keys(node.attrs).filter((key) => key !== "href" && key !== "target" && key !== "rel");
5129
+ if (extras.length > 0)
5130
+ return void 0;
5131
+ return ctx.text(node).trim() === href ? href : void 0;
5132
+ };
5036
5133
 
5037
5134
  // ../plugin-code-copy/dist/index.js
5038
5135
  var codeCopyPlugin = (options = {}) => {
@@ -5055,9 +5152,34 @@ ${body}
5055
5152
  },
5056
5153
  hooks: {
5057
5154
  afterParse: wrapCodeBlocks
5058
- }
5155
+ },
5156
+ htmlToMarkdown: codeCopyToMarkdown()
5059
5157
  });
5060
5158
  };
5159
+ var codeCopyToMarkdown = () => (node, ctx) => {
5160
+ if (node.type !== "element" || node.tag !== "figure" || node.attrs["data-nizel-code-copy"] === void 0)
5161
+ return void 0;
5162
+ const source = findHtmlDescendant(node, (el) => el.tag === "textarea" && el.attrs["data-nizel-copy-source"] !== void 0);
5163
+ if (!source) {
5164
+ const body = htmlChildElements(node).filter((el) => !["figcaption", "textarea", "button"].includes(el.tag));
5165
+ return ctx.block(htmlRoot(body));
5166
+ }
5167
+ const code = ctx.text(source).replace(/\n$/, "");
5168
+ const pre = findHtmlDescendant(node, (el) => el.tag === "pre");
5169
+ const codeEl = pre ? findHtmlElement(pre, (el) => el.tag === "code") : void 0;
5170
+ const codeClass = codeEl?.attrs.class ?? "";
5171
+ const langClass = codeClass.match(/\blanguage-([A-Za-z0-9_-]+)/)?.[1];
5172
+ const isMermaid = findHtmlDescendant(node, (el) => el.tag === "div" && el.attrs.class === "mermaid") !== void 0;
5173
+ const lang = langClass ?? pre?.attrs["data-language"] ?? (isMermaid ? "mermaid" : "");
5174
+ const fence = fenceFor(code);
5175
+ return `${fence}${lang}
5176
+ ${code}
5177
+ ${fence}`;
5178
+ };
5179
+ var fenceFor = (code) => {
5180
+ const longest = Math.max(0, ...(code.match(/`+/g) ?? []).map((run) => run.length));
5181
+ return "`".repeat(Math.max(3, longest + 1));
5182
+ };
5061
5183
  var wrapCodeBlocks = (ast) => ({
5062
5184
  ...ast,
5063
5185
  children: ast.children.map(wrapCodeBlock)
@@ -5134,7 +5256,28 @@ ${body}
5134
5256
  afterParse(ast) {
5135
5257
  return appendBibliography(replaceCitationRefs(ast, citations), citations);
5136
5258
  }
5259
+ },
5260
+ htmlToMarkdown: citationsToMarkdown(options)
5261
+ };
5262
+ };
5263
+ var citationsToMarkdown = (options = {}) => {
5264
+ const className = options.className ?? "citations";
5265
+ return (node, ctx) => {
5266
+ if (node.type !== "element")
5267
+ return void 0;
5268
+ if (node.tag === "a" && hasHtmlClass(node, "citation")) {
5269
+ const match = /^#cite-(.+)$/.exec(node.attrs.href ?? "");
5270
+ return match ? `[@${match[1]}]` : void 0;
5271
+ }
5272
+ if (node.tag === "section" && hasHtmlClass(node, className)) {
5273
+ const list = findHtmlElement(node, (el) => el.tag === "ol");
5274
+ const items = list ? htmlChildElements(list).filter((el) => el.tag === "li") : [];
5275
+ return items.map((li) => {
5276
+ const id = /^cite-(.+)$/.exec(li.attrs.id ?? "")?.[1] ?? "";
5277
+ return `[@${id}]: ${ctx.block(htmlRoot(li.children)).trim()}`;
5278
+ }).join("\n");
5137
5279
  }
5280
+ return void 0;
5138
5281
  };
5139
5282
  };
5140
5283
  var extractCitations = (markdown) => {
@@ -5236,9 +5379,28 @@ ${body}
5236
5379
  beforeParse(markdown) {
5237
5380
  return transformDefinitionLists(markdown, options);
5238
5381
  }
5239
- }
5382
+ },
5383
+ htmlToMarkdown: deflistToMarkdown(options)
5240
5384
  };
5241
5385
  };
5386
+ var deflistToMarkdown = (options = {}) => (node, ctx) => {
5387
+ if (node.type !== "element" || node.tag !== "dl")
5388
+ return void 0;
5389
+ if (options.className) {
5390
+ if (node.attrs.class !== options.className)
5391
+ return void 0;
5392
+ } else if (node.attrs.class !== void 0) {
5393
+ return void 0;
5394
+ }
5395
+ const lines = [];
5396
+ for (const el of htmlChildElements(node)) {
5397
+ if (el.tag === "dt")
5398
+ lines.push(ctx.inline(el));
5399
+ else if (el.tag === "dd")
5400
+ lines.push(`: ${ctx.inline(el)}`);
5401
+ }
5402
+ return lines.join("\n");
5403
+ };
5242
5404
  var transformDefinitionLists = (markdown, _options = {}) => {
5243
5405
  const lines = markdown.split("\n");
5244
5406
  const result = [];
@@ -5325,8 +5487,23 @@ ${body}
5325
5487
  },
5326
5488
  hooks: {
5327
5489
  beforeParse: transformDetails
5328
- }
5490
+ },
5491
+ htmlToMarkdown: detailsToMarkdown(options)
5329
5492
  });
5493
+ var detailsToMarkdown = (options = {}) => {
5494
+ const className = options.className ?? "details";
5495
+ return (node, ctx) => {
5496
+ if (node.type !== "element" || node.tag !== "details" || node.attrs.class !== className)
5497
+ return void 0;
5498
+ const summaryEl = findHtmlElement(node, (el) => el.tag === "summary");
5499
+ const summary = summaryEl ? ctx.text(summaryEl).trim() : "Details";
5500
+ const body = ctx.block(htmlRoot(htmlChildElements(node).filter((el) => el.tag !== "summary"))).trim();
5501
+ return body ? `::details ${summary}
5502
+ ${body}
5503
+ ::` : `::details ${summary}
5504
+ ::`;
5505
+ };
5506
+ };
5330
5507
  var transformDetails = (markdown) => {
5331
5508
  return markdown.replace(/^:::details\s*(.*?)\s*$/gm, (_match, summary) => `::details ${summary || "Details"}`).replace(/^:::\s*$/gm, "::");
5332
5509
  };
@@ -5356,7 +5533,19 @@ ${body}
5356
5533
  afterParse(ast) {
5357
5534
  return transformDiagramCodes(ast, options);
5358
5535
  }
5359
- }
5536
+ },
5537
+ htmlToMarkdown: diagramsToMarkdown(options)
5538
+ };
5539
+ };
5540
+ var diagramsToMarkdown = (options = {}) => {
5541
+ const className = options.className ?? "mermaid";
5542
+ return (node, ctx) => {
5543
+ if (node.type !== "element" || node.tag !== "div" || node.attrs.class !== className)
5544
+ return void 0;
5545
+ const code = ctx.text(node).trim();
5546
+ return `\`\`\`mermaid
5547
+ ${code}
5548
+ \`\`\``;
5360
5549
  };
5361
5550
  };
5362
5551
  var transformDiagramCodes = (ast, options = {}) => ({
@@ -5501,9 +5690,45 @@ ${body}
5501
5690
  hooks: {
5502
5691
  beforeParse: transformFootnotes,
5503
5692
  afterParse: transformFootnoteRefs
5693
+ },
5694
+ htmlToMarkdown: footnotesToMarkdown(options)
5695
+ };
5696
+ };
5697
+ var footnotesToMarkdown = (options = {}) => {
5698
+ const className = options.className ?? "footnotes";
5699
+ return (node, ctx) => {
5700
+ if (node.type !== "element")
5701
+ return void 0;
5702
+ if (node.tag === "sup") {
5703
+ const anchor = findHtmlElement(node, (el) => el.tag === "a" && hasHtmlClass(el, "footnote-ref"));
5704
+ const match = anchor ? /^#fn-(.+)$/.exec(anchor.attrs.href ?? "") : void 0;
5705
+ return match ? `[^${match[1]}]` : void 0;
5706
+ }
5707
+ if (node.tag === "section" && hasHtmlClass(node, className)) {
5708
+ const list = findHtmlElement(node, (el) => el.tag === "ol");
5709
+ const items = list ? htmlChildElements(list).filter((el) => el.tag === "li") : [];
5710
+ return items.map((li) => {
5711
+ const id = /^fn-(.+)$/.exec(li.attrs.id ?? "")?.[1] ?? "";
5712
+ const body = ctx.block(htmlRoot(stripBackrefs(li.children, className))).trim();
5713
+ return `[^${id}]: ${body}`;
5714
+ }).join("\n");
5504
5715
  }
5716
+ return void 0;
5505
5717
  };
5506
5718
  };
5719
+ var stripBackrefs = (nodes, className) => {
5720
+ const result = [];
5721
+ for (const node of nodes) {
5722
+ if (node.type === "element") {
5723
+ if (hasHtmlClass(node, `${className}__backref`))
5724
+ continue;
5725
+ result.push({ ...node, children: stripBackrefs(node.children, className) });
5726
+ } else {
5727
+ result.push(node);
5728
+ }
5729
+ }
5730
+ return result;
5731
+ };
5507
5732
  var transformFootnotes = (markdown) => {
5508
5733
  const lines = markdown.split("\n");
5509
5734
  const body = [];
@@ -5649,9 +5874,46 @@ ${body}
5649
5874
  afterParse(ast) {
5650
5875
  return transformTaskLists(ast, mode);
5651
5876
  }
5652
- }
5877
+ },
5878
+ htmlToMarkdown: taskListToMarkdown()
5653
5879
  };
5654
5880
  };
5881
+ var CHECKBOX_ATTR = "data-nizel-task-checkbox";
5882
+ var isCheckbox = (node) => node.type === "element" && node.tag === "input" && node.attrs?.[CHECKBOX_ATTR] !== void 0;
5883
+ var taskListToMarkdown = () => (node, ctx) => {
5884
+ if (node.type !== "element" || node.tag !== "ul" && node.tag !== "ol")
5885
+ return void 0;
5886
+ const items = htmlChildElements(node).filter((el) => el.tag === "li");
5887
+ if (!items.some((item) => findHtmlDescendant(item, isCheckbox)))
5888
+ return void 0;
5889
+ const ordered = node.tag === "ol";
5890
+ const start = Number(node.attrs.start ?? 1);
5891
+ return items.map((item, index) => {
5892
+ const checkbox = findHtmlDescendant(item, isCheckbox);
5893
+ const body = ctx.block(htmlRoot(cleanTaskChildren(item.children))).trim();
5894
+ const baseMarker = ordered ? `${start + index}. ` : "- ";
5895
+ const marker = checkbox ? `${baseMarker}${checkbox.attrs.checked !== void 0 ? "[x] " : "[ ] "}` : baseMarker;
5896
+ return ctx.indent(`${marker}${body}`, marker.length);
5897
+ }).join("\n");
5898
+ };
5899
+ var cleanTaskChildren = (nodes) => {
5900
+ const result = [];
5901
+ for (const node of nodes) {
5902
+ if (node.type === "element") {
5903
+ if (isCheckbox(node))
5904
+ continue;
5905
+ const cleaned = { ...node, children: cleanTaskChildren(node.children) };
5906
+ if (hasHtmlClass(cleaned, "nizel-task-list__label") || hasHtmlClass(cleaned, "nizel-task-list__content")) {
5907
+ result.push(...cleaned.children);
5908
+ } else {
5909
+ result.push(cleaned);
5910
+ }
5911
+ } else {
5912
+ result.push(node);
5913
+ }
5914
+ }
5915
+ return result;
5916
+ };
5655
5917
  var transformTaskLists = (ast, mode = "view") => {
5656
5918
  return { ...ast, children: ast.children.map((node) => transformBlock5(node, mode)) };
5657
5919
  };
@@ -5733,8 +5995,17 @@ ${body}
5733
5995
  afterParse(ast) {
5734
5996
  return addHeadingAnchors(ast, options);
5735
5997
  }
5736
- }
5998
+ },
5999
+ htmlToMarkdown: headingAnchorsToMarkdown(options)
5737
6000
  });
6001
+ var headingAnchorsToMarkdown = (options = {}) => {
6002
+ const className = options.className ?? "heading-anchor";
6003
+ return (node) => {
6004
+ if (node.type !== "element" || node.tag !== "a" || !hasHtmlClass(node, className))
6005
+ return void 0;
6006
+ return "";
6007
+ };
6008
+ };
5738
6009
  var addHeadingAnchors = (ast, options = {}) => ({
5739
6010
  ...ast,
5740
6011
  children: ast.children.map((node) => {
@@ -5754,6 +6025,177 @@ ${body}
5754
6025
  });
5755
6026
  var escapeHtml8 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5756
6027
 
6028
+ // ../nizel-plugin-hidden-comments/dist/index.js
6029
+ var hiddenCommentsPlugin = (options = {}) => {
6030
+ const normalized = normalizeOptions(options);
6031
+ let comments = /* @__PURE__ */ new Map();
6032
+ let nextIndex = 0;
6033
+ return {
6034
+ name: "hidden-comments",
6035
+ hooks: {
6036
+ beforeParse(markdown) {
6037
+ comments = /* @__PURE__ */ new Map();
6038
+ nextIndex = 0;
6039
+ return transformMarkdownComments(markdown, normalized, (comment) => {
6040
+ const token = `NIZEL_HIDDEN_COMMENT_${nextIndex}_${Math.random().toString(36).slice(2)}`;
6041
+ nextIndex += 1;
6042
+ comments.set(token, comment);
6043
+ return token;
6044
+ });
6045
+ },
6046
+ afterRender(html) {
6047
+ let nextHtml = restoreCommentTokens(html, comments, normalized);
6048
+ if (normalized.injectCss && normalized.mode !== "remove") {
6049
+ nextHtml = `${hiddenCommentsStyleTag(normalized.className)}
6050
+ ${nextHtml}`;
6051
+ }
6052
+ return nextHtml;
6053
+ }
6054
+ },
6055
+ htmlToMarkdown: hiddenCommentsToMarkdown(normalized)
6056
+ };
6057
+ };
6058
+ var transformMarkdownComments = (markdown, options = {}, createToken = (comment) => renderCommentElement(comment, normalizeOptions(options))) => {
6059
+ const normalized = normalizeOptions(options);
6060
+ if (typeof markdown !== "string" || markdown.indexOf("<!--") === -1)
6061
+ return markdown;
6062
+ return replaceMarkdownCommentsOutsideFences(markdown, (comment) => {
6063
+ if (normalized.mode === "remove")
6064
+ return "";
6065
+ return createToken(comment);
6066
+ });
6067
+ };
6068
+ var hiddenCommentsToMarkdown = (options = {}) => {
6069
+ const normalized = normalizeOptions(options);
6070
+ return (node, ctx) => {
6071
+ if (node.type !== "element" || node.tag !== "span")
6072
+ return void 0;
6073
+ if (!isHiddenCommentElement(node.attrs, normalized.className))
6074
+ return void 0;
6075
+ const text = ctx.text(node).trim();
6076
+ if (/^<!--[\s\S]*-->$/.test(text))
6077
+ return text;
6078
+ return `<!-- ${text.replace(/^<!--|-->$/g, "").trim()} -->`;
6079
+ };
6080
+ };
6081
+ var isHiddenCommentElement = (attrs, className) => {
6082
+ if ("data-nizel-hidden-comment" in attrs)
6083
+ return true;
6084
+ return (attrs.class ?? "").split(/\s+/).includes(className);
6085
+ };
6086
+ var normalizeOptions = (options) => ({
6087
+ mode: options.mode ?? "hide",
6088
+ injectCss: options.injectCss ?? false,
6089
+ className: options.className ?? "nizel-hidden-comment"
6090
+ });
6091
+ var replaceMarkdownCommentsOutsideFences = (markdown, replacer) => {
6092
+ const lines = markdown.split(/(\n)/);
6093
+ let result = "";
6094
+ let line = "";
6095
+ let fence;
6096
+ for (const part of lines) {
6097
+ line += part;
6098
+ if (part !== "\n")
6099
+ continue;
6100
+ const next = processLine(line, fence, replacer);
6101
+ result += next.line;
6102
+ fence = next.fence;
6103
+ line = "";
6104
+ }
6105
+ if (line) {
6106
+ const next = processLine(line, fence, replacer);
6107
+ result += next.line;
6108
+ }
6109
+ return result;
6110
+ };
6111
+ var processLine = (line, fence, replacer) => {
6112
+ if (fence) {
6113
+ return {
6114
+ line,
6115
+ fence: isClosingFenceLine(line, fence) ? void 0 : fence
6116
+ };
6117
+ }
6118
+ const openingFence = getOpeningFenceLine(line);
6119
+ if (openingFence)
6120
+ return { line, fence: openingFence };
6121
+ return {
6122
+ line: line.replace(/<!--[\s\S]*?-->/g, replacer),
6123
+ fence
6124
+ };
6125
+ };
6126
+ var getOpeningFenceLine = (line) => {
6127
+ const match = /^( {0,3})(`{3,}|~{3,})/.exec(line);
6128
+ if (!match)
6129
+ return void 0;
6130
+ return { marker: match[2].charAt(0), length: match[2].length };
6131
+ };
6132
+ var isClosingFenceLine = (line, fence) => {
6133
+ const lineText = line.replace(/\r?\n$/, "");
6134
+ const escapedMarker = fence.marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6135
+ const pattern = new RegExp(`^ {0,3}${escapedMarker}{${fence.length},} *$`);
6136
+ return pattern.test(lineText);
6137
+ };
6138
+ var renderCommentElement = (comment, options) => {
6139
+ const classes = [
6140
+ options.className,
6141
+ `${options.className}--${options.mode}`,
6142
+ "print-hidden-comment"
6143
+ ];
6144
+ const mode = escapeHtml9(options.mode);
6145
+ return `<span class="${classes.map(escapeHtml9).join(" ")}" data-nizel-hidden-comment="" data-hidden-comment-mode="${mode}">${escapeHtml9(comment)}</span>`;
6146
+ };
6147
+ var restoreCommentTokens = (html, comments, options) => {
6148
+ if (!comments.size || options.mode === "remove")
6149
+ return html;
6150
+ let nextHtml = html;
6151
+ for (const [token, comment] of comments) {
6152
+ const tokenPattern = new RegExp(escapeRegExp2(token), "g");
6153
+ const blockPattern = new RegExp(`<p>\\s*${escapeRegExp2(token)}\\s*</p>`, "g");
6154
+ const rendered = renderCommentElement(comment, options);
6155
+ nextHtml = nextHtml.replace(blockPattern, rendered).replace(tokenPattern, rendered);
6156
+ }
6157
+ return nextHtml;
6158
+ };
6159
+ var hiddenCommentsStyleTag = (className) => {
6160
+ return `<style data-nizel-plugin="hidden-comments">${hiddenCommentsCss(className)}</style>`;
6161
+ };
6162
+ var hiddenCommentsCss = (className) => {
6163
+ const selector = `.${className.replace(/[^A-Za-z0-9_-]/g, "")}`;
6164
+ return `${selector} {
6165
+ user-select: text;
6166
+ }
6167
+ ${selector}--hide {
6168
+ border: 0;
6169
+ color: rgba(255, 255, 255, 0);
6170
+ display: inline-block;
6171
+ font-size: 1px;
6172
+ height: 0;
6173
+ line-height: 0;
6174
+ margin: 0;
6175
+ max-height: 0;
6176
+ max-width: 0;
6177
+ overflow: hidden;
6178
+ padding: 0;
6179
+ vertical-align: baseline;
6180
+ width: 0;
6181
+ }
6182
+ ${selector}--small {
6183
+ color: color-mix(in srgb, currentColor 60%, transparent);
6184
+ display: inline;
6185
+ font-size: 0.75em;
6186
+ line-height: 1;
6187
+ }
6188
+ ${selector}--render {
6189
+ display: inline;
6190
+ }`;
6191
+ };
6192
+ var escapeHtml9 = (value) => {
6193
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6194
+ };
6195
+ var escapeRegExp2 = (value) => {
6196
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6197
+ };
6198
+
5757
6199
  // ../plugin-math/dist/index.js
5758
6200
  var mathPlugin = (options = {}) => {
5759
6201
  return {
@@ -5766,7 +6208,26 @@ ${body}
5766
6208
  afterParse(ast) {
5767
6209
  return transformInlineMath(ast, options);
5768
6210
  }
6211
+ },
6212
+ htmlToMarkdown: mathToMarkdown(options)
6213
+ };
6214
+ };
6215
+ var mathToMarkdown = (options = {}) => {
6216
+ const inlineTokens = (options.inlineClassName ?? "math math-inline").split(/\s+/);
6217
+ const blockTokens = (options.blockClassName ?? "math math-display").split(/\s+/);
6218
+ return (node, ctx) => {
6219
+ if (node.type !== "element")
6220
+ return void 0;
6221
+ const tokens = node.attrs.class ? node.attrs.class.split(/\s+/) : [];
6222
+ if (node.tag === "span" && inlineTokens.every((token) => tokens.includes(token))) {
6223
+ return `$${ctx.text(node)}$`;
6224
+ }
6225
+ if (node.tag === "div" && blockTokens.every((token) => tokens.includes(token))) {
6226
+ return `$$
6227
+ ${ctx.text(node).trim()}
6228
+ $$`;
5769
6229
  }
6230
+ return void 0;
5770
6231
  };
5771
6232
  };
5772
6233
  var transformBlockMath = (markdown) => {
@@ -5852,7 +6313,7 @@ ${body}
5852
6313
  nodes.push({ type: "text", value: value.slice(lastIndex, match.index) });
5853
6314
  nodes.push({
5854
6315
  type: "inlineHtml",
5855
- value: `<span class="${escapeHtml9(options.inlineClassName ?? "math math-inline")}">${escapeHtml9(match[1])}</span>`
6316
+ value: `<span class="${escapeHtml10(options.inlineClassName ?? "math math-inline")}">${escapeHtml10(match[1])}</span>`
5856
6317
  });
5857
6318
  lastIndex = pattern.lastIndex;
5858
6319
  }
@@ -5860,7 +6321,7 @@ ${body}
5860
6321
  nodes.push({ type: "text", value: value.slice(lastIndex) });
5861
6322
  return nodes;
5862
6323
  };
5863
- var escapeHtml9 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6324
+ var escapeHtml10 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5864
6325
 
5865
6326
  // ../plugin-media/dist/index.js
5866
6327
  var mediaPlugin = (options = {}) => ({
@@ -5869,8 +6330,26 @@ ${body}
5869
6330
  afterRender(html) {
5870
6331
  return enhanceImages(html, options);
5871
6332
  }
5872
- }
6333
+ },
6334
+ htmlToMarkdown: mediaToMarkdown(options)
5873
6335
  });
6336
+ var mediaToMarkdown = (options = {}) => {
6337
+ const className = options.figureClassName ?? "media-figure";
6338
+ return (node, ctx) => {
6339
+ if (node.type !== "element" || node.tag !== "figure" || node.attrs.class !== className)
6340
+ return void 0;
6341
+ const img = findHtmlElement(node, (el) => el.tag === "img");
6342
+ if (!img)
6343
+ return void 0;
6344
+ const attrs = { src: img.attrs.src ?? "" };
6345
+ if (img.attrs.alt !== void 0)
6346
+ attrs.alt = img.attrs.alt;
6347
+ if (img.attrs.title !== void 0)
6348
+ attrs.title = img.attrs.title;
6349
+ const cleanImg = { type: "element", tag: "img", attrs, children: [], raw: "" };
6350
+ return ctx.inline({ type: "root", children: [cleanImg] });
6351
+ };
6352
+ };
5874
6353
  var enhanceImages = (html, options = {}) => {
5875
6354
  const lazy = options.lazy !== false;
5876
6355
  const responsive = options.responsive !== false;
@@ -5878,7 +6357,7 @@ ${body}
5878
6357
  return html.replace(/<p>(<img\b[^>]*>)<\/p>/g, (_match, img) => {
5879
6358
  const enhanced = enhanceImageTag(img, { lazy, responsive });
5880
6359
  const caption = /alt="([^"]+)"/.exec(enhanced)?.[1];
5881
- return `<figure class="${escapeHtml10(className)}">${enhanced}${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
6360
+ return `<figure class="${escapeHtml11(className)}">${enhanced}${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
5882
6361
  });
5883
6362
  };
5884
6363
  var enhanceImageTag = (img, options) => {
@@ -5889,7 +6368,7 @@ ${body}
5889
6368
  next = next.replace(/\/?>$/, ' decoding="async" />');
5890
6369
  return next;
5891
6370
  };
5892
- var escapeHtml10 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6371
+ var escapeHtml11 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5893
6372
 
5894
6373
  // ../plugin-sanitize/dist/index.js
5895
6374
  var sanitizePlugin = (options = {}) => ({
@@ -5933,9 +6412,30 @@ ${body}
5933
6412
  }
5934
6413
  }
5935
6414
  }
5936
- }
6415
+ },
6416
+ htmlToMarkdown: shikiToMarkdown()
5937
6417
  });
5938
6418
  };
6419
+ var shikiToMarkdown = () => (node, ctx) => {
6420
+ if (node.type !== "element" || node.tag !== "pre")
6421
+ return void 0;
6422
+ const isShiki = hasHtmlClass(node, "shiki") || node.attrs.style !== void 0 || node.attrs["data-language"] !== void 0;
6423
+ if (!isShiki)
6424
+ return void 0;
6425
+ const codeEl = findHtmlElement(node, (el) => el.tag === "code");
6426
+ const codeClass = codeEl?.attrs.class ?? "";
6427
+ const langClass = codeClass.match(/\blanguage-([A-Za-z0-9_-]+)/)?.[1];
6428
+ const lang = node.attrs["data-language"] ?? langClass ?? "";
6429
+ const code = ctx.text(node).replace(/\n$/, "");
6430
+ const fence = fenceFor2(code);
6431
+ return `${fence}${lang}
6432
+ ${code}
6433
+ ${fence}`;
6434
+ };
6435
+ var fenceFor2 = (code) => {
6436
+ const longest = Math.max(0, ...(code.match(/`+/g) ?? []).map((run) => run.length));
6437
+ return "`".repeat(Math.max(3, longest + 1));
6438
+ };
5939
6439
  var fallback = (node, ctx) => {
5940
6440
  const language = node.lang ? ` class="language-${ctx.escape(node.lang)}"` : "";
5941
6441
  return `<pre><code${language}>${ctx.escape(node.code)}</code></pre>`;
@@ -5954,8 +6454,17 @@ ${body}
5954
6454
  afterParse(ast) {
5955
6455
  return fillTocBlocks(ast, options);
5956
6456
  }
5957
- }
6457
+ },
6458
+ htmlToMarkdown: tocToMarkdown(options)
5958
6459
  });
6460
+ var tocToMarkdown = (options = {}) => {
6461
+ const className = options.className ?? "toc";
6462
+ return (node) => {
6463
+ if (node.type !== "element" || node.tag !== "nav" || !hasHtmlClass(node, className))
6464
+ return void 0;
6465
+ return "[[toc]]";
6466
+ };
6467
+ };
5959
6468
  var fillTocBlocks = (ast, options = {}) => {
5960
6469
  const minDepth = options.minDepth ?? 2;
5961
6470
  const maxDepth = options.maxDepth ?? 6;
@@ -5989,7 +6498,24 @@ ${body}
5989
6498
  afterParse(ast) {
5990
6499
  return transformTypography(ast, options);
5991
6500
  }
5992
- }
6501
+ },
6502
+ htmlToMarkdown: typographyToMarkdown(options)
6503
+ };
6504
+ };
6505
+ var typographyToMarkdown = (options = {}) => {
6506
+ const mark = options.mark !== false;
6507
+ const subscript = options.subscript !== false;
6508
+ const superscript = options.superscript !== false;
6509
+ return (node, ctx) => {
6510
+ if (node.type !== "element" || Object.keys(node.attrs).length > 0)
6511
+ return void 0;
6512
+ if (node.tag === "mark" && mark)
6513
+ return `==${ctx.inline(node)}==`;
6514
+ if (node.tag === "sub" && subscript)
6515
+ return `~${ctx.inline(node)}~`;
6516
+ if (node.tag === "sup" && superscript)
6517
+ return `^${ctx.inline(node)}^`;
6518
+ return void 0;
5993
6519
  };
5994
6520
  };
5995
6521
  var transformTypography = (ast, options = {}) => {
@@ -6045,11 +6571,11 @@ ${body}
6045
6571
  if (match.index > lastIndex)
6046
6572
  nodes.push({ type: "text", value: value.slice(lastIndex, match.index) });
6047
6573
  if (match[1] !== void 0 && mark)
6048
- nodes.push({ type: "inlineHtml", value: `<mark>${escapeHtml11(match[1])}</mark>` });
6574
+ nodes.push({ type: "inlineHtml", value: `<mark>${escapeHtml12(match[1])}</mark>` });
6049
6575
  else if (match[2] !== void 0 && subscript)
6050
- nodes.push({ type: "inlineHtml", value: `<sub>${escapeHtml11(match[2])}</sub>` });
6576
+ nodes.push({ type: "inlineHtml", value: `<sub>${escapeHtml12(match[2])}</sub>` });
6051
6577
  else if (match[3] !== void 0 && superscript)
6052
- nodes.push({ type: "inlineHtml", value: `<sup>${escapeHtml11(match[3])}</sup>` });
6578
+ nodes.push({ type: "inlineHtml", value: `<sup>${escapeHtml12(match[3])}</sup>` });
6053
6579
  else
6054
6580
  nodes.push({ type: "text", value: match[0] });
6055
6581
  lastIndex = pattern.lastIndex;
@@ -6058,7 +6584,7 @@ ${body}
6058
6584
  nodes.push({ type: "text", value: value.slice(lastIndex) });
6059
6585
  return nodes;
6060
6586
  };
6061
- var escapeHtml11 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6587
+ var escapeHtml12 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6062
6588
 
6063
6589
  // src/index.ts
6064
6590
  var supportedPlugins = [
@@ -6139,6 +6665,13 @@ ${body}
6139
6665
  defaultEnabled: false,
6140
6666
  category: "block"
6141
6667
  },
6668
+ {
6669
+ id: "hidden-comments",
6670
+ label: "Hidden Comments",
6671
+ description: "Control how Markdown HTML comments render in documents.",
6672
+ defaultEnabled: false,
6673
+ category: "core-behavior"
6674
+ },
6142
6675
  {
6143
6676
  id: "media",
6144
6677
  label: "Media",
@@ -6217,6 +6750,7 @@ ${body}
6217
6750
  "frontmatter-ui": (options) => frontmatterUiPlugin(options["frontmatter-ui"]),
6218
6751
  gfm: (options) => gfmPlugins(options.gfm),
6219
6752
  "heading-anchors": (options) => headingAnchorsPlugin(options["heading-anchors"]),
6753
+ "hidden-comments": (options) => hiddenCommentsPlugin(options["hidden-comments"]),
6220
6754
  math: (options) => mathPlugin(options.math),
6221
6755
  media: (options) => mediaPlugin(options.media),
6222
6756
  sanitize: (options) => sanitizePlugin(options.sanitize),
@@ -6233,11 +6767,31 @@ ${body}
6233
6767
  };
6234
6768
  function useNizelKit(options = {}) {
6235
6769
  const { enabledPlugins, pluginOptions, plugins = [], ...nizelOptions } = options;
6236
- return useBrowserNizel({
6770
+ const resolvedPlugins = [...createPlugins(enabledPlugins, pluginOptions), ...plugins];
6771
+ const processor = useBrowserNizel({
6237
6772
  ...nizelOptions,
6238
- plugins: [...createPlugins(enabledPlugins, pluginOptions), ...plugins]
6773
+ plugins: resolvedPlugins
6239
6774
  });
6775
+ const htmlToMarkdown3 = processor.htmlToMarkdown.bind(processor);
6776
+ processor.htmlToMarkdown = (source, reverseOptions) => {
6777
+ return htmlToMarkdown3(source, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6778
+ };
6779
+ const nodeToMarkdown2 = processor.nodeToMarkdown.bind(processor);
6780
+ processor.nodeToMarkdown = (source, reverseOptions) => {
6781
+ return nodeToMarkdown2(source, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6782
+ };
6783
+ const selectionToMarkdown2 = processor.selectionToMarkdown.bind(processor);
6784
+ processor.selectionToMarkdown = (selection, reverseOptions) => {
6785
+ return selectionToMarkdown2(selection, withKitHtmlToMarkdownPlugins(reverseOptions, resolvedPlugins));
6786
+ };
6787
+ return processor;
6240
6788
  }
6789
+ var withKitHtmlToMarkdownPlugins = (options, plugins) => {
6790
+ return {
6791
+ ...options,
6792
+ plugins: [...plugins, ...options?.plugins ?? []]
6793
+ };
6794
+ };
6241
6795
  async function markdownToHtml(markdown, options) {
6242
6796
  return useNizelKit(options).html(markdown);
6243
6797
  }