wiki-formant 0.26.0 → 0.26.1

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/markdown.js CHANGED
@@ -81,35 +81,51 @@ export function tableToMarkdown(html) {
81
81
  ...body.map(r => `| ${r.join(' | ')} |`),
82
82
  ].join('\n');
83
83
  }
84
+ // The spaces either side go with it: at a block boundary they are not text.
85
+ const BLOCK_TOKEN = /[ \t]*\u0000(\d+)\u0000[ \t]*/g;
84
86
  /** Block-level HTML → markdown. */
85
87
  export function htmlToMarkdown(html) {
86
88
  let out = html;
89
+ // Each converted block is set aside behind a token and put back at the end.
90
+ // The `<div>` rule reads its body as inline text and collapses whitespace, so
91
+ // a block already converted inside a div lost every newline: a boxed table
92
+ // (`scrollTables`) came out as one line of pipes, and so did a list, a code
93
+ // listing or a callout's paragraphs.
94
+ const blocks = [];
95
+ const block = (md) => `\u0000${blocks.push(`\n\n${md}\n\n`) - 1}\u0000`;
96
+ const restore = (s) => s.replace(BLOCK_TOKEN, (_m, i) => restore(blocks[Number(i)]));
97
+ // A list inside an item is already a block by the time its parent converts;
98
+ // it goes under the item's line, indented, rather than onto it.
99
+ const listItem = (marker, body) => [
100
+ `${marker} ${inlineToMarkdown(body.replace(BLOCK_TOKEN, ''))}`,
101
+ ...[...body.matchAll(BLOCK_TOKEN)].map(m => restore(m[0]).trim().replace(/^/gm, ' ')),
102
+ ].join('\n');
87
103
  // Tables first — their inner markup must not be eaten by the generic rules.
88
- out = out.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, m => `\n\n${tableToMarkdown(m)}\n\n`);
104
+ out = out.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, m => block(tableToMarkdown(m)));
89
105
  // Lists. `<ol>` numbering is computed per-list, which is why this cannot be a
90
106
  // single regex with a `$1` backreference — inside a replace callback `$1` is
91
107
  // a literal, not a substitution. (That exact bug shipped once and rendered
92
108
  // every ordered list as a column of `1. $1`.)
93
109
  out = out.replace(/<ul\b[^>]*>([\s\S]*?)<\/ul>/gi, (_m, body) => {
94
- const items = [...body.matchAll(/<li\b[^>]*>([\s\S]*?)<\/li>/gi)].map(i => `- ${inlineToMarkdown(i[1])}`);
95
- return `\n\n${items.join('\n')}\n\n`;
110
+ const items = [...body.matchAll(/<li\b[^>]*>([\s\S]*?)<\/li>/gi)].map(i => listItem('-', i[1]));
111
+ return block(items.join('\n'));
96
112
  });
97
113
  out = out.replace(/<ol\b[^>]*>([\s\S]*?)<\/ol>/gi, (_m, body) => {
98
- const items = [...body.matchAll(/<li\b[^>]*>([\s\S]*?)<\/li>/gi)].map((i, n) => `${n + 1}. ${inlineToMarkdown(i[1])}`);
99
- return `\n\n${items.join('\n')}\n\n`;
114
+ const items = [...body.matchAll(/<li\b[^>]*>([\s\S]*?)<\/li>/gi)].map((i, n) => listItem(`${n + 1}.`, i[1]));
115
+ return block(items.join('\n'));
100
116
  });
101
117
  out = out
102
- .replace(/<pre\b[^>]*>\s*<code\b[^>]*>([\s\S]*?)<\/code>\s*<\/pre>/gi, (_m, code) => `\n\n\`\`\`\n${decodeEntities(code).trim()}\n\`\`\`\n\n`)
103
- .replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_m, code) => `\n\n\`\`\`\n${decodeEntities(code).trim()}\n\`\`\`\n\n`)
104
- .replace(/<blockquote\b[^>]*>([\s\S]*?)<\/blockquote>/gi, (_m, body) => `\n\n${inlineToMarkdown(body)
118
+ .replace(/<pre\b[^>]*>\s*<code\b[^>]*>([\s\S]*?)<\/code>\s*<\/pre>/gi, (_m, code) => block(`\`\`\`\n${decodeEntities(code).trim()}\n\`\`\``))
119
+ .replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_m, code) => block(`\`\`\`\n${decodeEntities(code).trim()}\n\`\`\``))
120
+ .replace(/<blockquote\b[^>]*>([\s\S]*?)<\/blockquote>/gi, (_m, body) => block(inlineToMarkdown(body)
105
121
  .split('\n')
106
122
  .map(l => `> ${l}`)
107
- .join('\n')}\n\n`)
108
- .replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_m, level, body) => `\n\n${'#'.repeat(Number(level))} ${inlineToMarkdown(body)}\n\n`)
109
- .replace(/<hr\s*\/?>/gi, '\n\n---\n\n')
110
- .replace(/<p\b[^>]*>([\s\S]*?)<\/p>/gi, (_m, body) => `\n\n${inlineToMarkdown(body)}\n\n`)
123
+ .join('\n')))
124
+ .replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_m, level, body) => block(`${'#'.repeat(Number(level))} ${inlineToMarkdown(body)}`))
125
+ .replace(/<hr\s*\/?>/gi, () => block('---'))
126
+ .replace(/<p\b[^>]*>([\s\S]*?)<\/p>/gi, (_m, body) => block(inlineToMarkdown(body)))
111
127
  .replace(/<div\b[^>]*>([\s\S]*?)<\/div>/gi, (_m, body) => `\n\n${inlineToMarkdown(body)}\n\n`);
112
- return decodeEntities(out.replace(/<[^>]+>/g, ''))
128
+ return decodeEntities(restore(out).replace(/<[^>]+>/g, ''))
113
129
  .replace(/[ \t]+$/gm, '')
114
130
  .replace(/\n{3,}/g, '\n\n')
115
131
  .trim();
package/dist/react.d.ts CHANGED
@@ -83,7 +83,8 @@ export interface TableOfContentsProps {
83
83
  offsetVar?: string;
84
84
  /** Fallback offset in px when `offsetVar` is unset or unparseable. */
85
85
  offsetFallback?: number;
86
- /** Indent per heading level, in rem. Set 0 for a flat list. */
86
+ /** Indent per heading level, in rem. Set 0 for a flat list, whose items then
87
+ * take their padding from the stylesheet: an inline `padding-left: 0` beat it. */
87
88
  indentRem?: number;
88
89
  /** Rendered instead of nothing when the article has no headings. */
89
90
  empty?: ReactNode;
package/dist/react.js CHANGED
@@ -248,7 +248,7 @@ export function TableOfContents({ containerSelector, headings: providedHeadings,
248
248
  document.getElementById(h.id)?.scrollIntoView({ behavior: 'smooth' });
249
249
  }, className: currentId === h.id
250
250
  ? [classNames.item, classNames.itemActive].filter(Boolean).join(' ')
251
- : classNames.item, "aria-current": currentId === h.id ? 'location' : undefined, style: { paddingLeft: `${(h.level - 1) * indentRem}rem` }, children: h.text }, h.id))) }))] }));
251
+ : classNames.item, "aria-current": currentId === h.id ? 'location' : undefined, style: (h.level - 1) * indentRem ? { paddingLeft: `${(h.level - 1) * indentRem}rem` } : undefined, children: h.text }, h.id))) }))] }));
252
252
  }
253
253
  // ---- outside clicks ---------------------------------------------------------
254
254
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-formant",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "The portable half of a wiki: derived taxonomy and facet controls, a version-negotiating MCP transport, markdown twins, block rendering, a rich-text editor engine, and conditional-GET plumbing.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -177,8 +177,8 @@
177
177
  }
178
178
  },
179
179
  "bin": {
180
- "check-classes": "./bin/check-classes.mjs",
181
- "check-pages": "./bin/check-pages.mjs"
180
+ "check-classes": "bin/check-classes.mjs",
181
+ "check-pages": "bin/check-pages.mjs"
182
182
  },
183
183
  "files": [
184
184
  "dist",