wiki-formant 0.82.0 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/base.css +3 -1
- package/dist/dom.d.ts +8 -0
- package/dist/dom.js +23 -0
- package/dist/react.d.ts +2 -2
- package/dist/react.js +4 -3
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -693,12 +693,13 @@ export const X = xAccount({ siteName: 'AcuiQ', handle: '@acu_iq', posts: "a name
|
|
|
693
693
|
addCopyButtons(el); // every <pre> gets one, once
|
|
694
694
|
sortTables(el); // every column-headed table sorts by its headers
|
|
695
695
|
hydrateTweetEmbeds(el); // placeholders get a live src
|
|
696
|
+
await renderMath(el); // $$…$$, \\[…\\] and \\(…\\) typeset by KaTeX, loaded only when there is maths
|
|
696
697
|
const off = onTweetResize((h, source) => sizeTweetEmbed(el, h, source));
|
|
697
698
|
```
|
|
698
699
|
|
|
699
700
|
`addCopyButton` was **byte-identical** in two BlockRenderers, down to the SVG path data. Its idempotence guard now lives inside the function rather than in a `pre:not(:has(…))` at the call site, where it can be — and was — retyped.
|
|
700
701
|
|
|
701
|
-
In React, `useArticlePasses(ref, [blocks])` (`wiki-formant/react`) runs them all, the embed pair included. Three renderers had the effect written out and disagreed on its dependencies: two ran once on mount, so a page swapped in without a remount kept its old tables unsortable.
|
|
702
|
+
In React, `useArticlePasses(ref, [blocks])` (`wiki-formant/react`) runs them all, the embed pair and the maths included. KaTeX is an optional peer: an app installs `katex`, and a page with no delimiter never fetches it. There is no single-`$` delimiter, since prose writes $XRD. Three renderers had the effect written out and disagreed on its dependencies: two ran once on mount, so a page swapped in without a remount kept its old tables unsortable.
|
|
702
703
|
|
|
703
704
|
`activateTabGroups` turns stored `[data-tabs]` markup into a working tab group. The editor persists tabs as nested divs, which is the right thing to store — it survives a markdown twin, a plain HTML render and a reader with JavaScript off, all of which show every tab in order. Making one of them pressable is a reader-side job, and it sits beside the other passes rather than inside a component.
|
|
704
705
|
|
package/dist/base.css
CHANGED
|
@@ -200,7 +200,6 @@ html[data-sidebar='closed'] .wiki-sidebar { width: 0; }
|
|
|
200
200
|
.tabs-editor .tab-button { display: inline-flex; align-items: center; padding: 0.15em 0.3em; border-bottom: 0; border-radius: 0.3rem; }
|
|
201
201
|
.tabs-editor .tab-button.active { background: color-mix(in srgb, currentColor 10%, transparent); }
|
|
202
202
|
.tab-title { width: 7em; outline: none; }
|
|
203
|
-
.tab-remove, .tab-add { opacity: 0.7; }
|
|
204
203
|
.tabs-content { padding: 1rem; }
|
|
205
204
|
.tabs-editor [data-tab-item] { display: none; }
|
|
206
205
|
[data-tabs][data-active-tab='0'] .tabs-editor [data-tab-item]:nth-of-type(1),
|
|
@@ -212,6 +211,9 @@ html[data-sidebar='closed'] .wiki-sidebar { width: 0; }
|
|
|
212
211
|
[data-tabs][data-active-tab='6'] .tabs-editor [data-tab-item]:nth-of-type(7),
|
|
213
212
|
[data-tabs][data-active-tab='7'] .tabs-editor [data-tab-item]:nth-of-type(8) { display: block; }
|
|
214
213
|
|
|
214
|
+
/* ---- maths (renderMath): a display wider than its column scrolls rather than spilling. */
|
|
215
|
+
.katex-display { overflow-x: auto; overflow-y: hidden; }
|
|
216
|
+
|
|
215
217
|
/* ---- one-line fields (WikiLine) --------------------------------------------- */
|
|
216
218
|
[data-wiki-line] { outline: none; cursor: text; white-space: pre-wrap; }
|
|
217
219
|
[data-wiki-line]:is(:empty, :has(> .ProseMirror-trailingBreak))::before { content: attr(data-placeholder); opacity: 0.6; }
|
package/dist/dom.d.ts
CHANGED
|
@@ -40,6 +40,14 @@ export declare function onTweetResize(resize: (height: number, source: MessageEv
|
|
|
40
40
|
export declare function hydrateTweetEmbeds(root: ParentNode): void;
|
|
41
41
|
/** Apply a posted height to the embed iframe under `root` that posted it, and to no other. */
|
|
42
42
|
export declare function sizeTweetEmbed(root: ParentNode, height: number, source: MessageEventSource | null): void;
|
|
43
|
+
/**
|
|
44
|
+
* Typeset the maths under `root` with KaTeX: `$$…$$` and `\[…\]` as a display,
|
|
45
|
+
* `\(…\)` inline. KaTeX and its stylesheet load only when the text holds a
|
|
46
|
+
* delimiter, so a page with no maths fetches nothing. KaTeX is an optional peer
|
|
47
|
+
* the app installs. Typeset maths carries no delimiter, so a second pass finds
|
|
48
|
+
* nothing to do.
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderMath(root: HTMLElement): Promise<void>;
|
|
43
51
|
export interface TabGroupClassNames {
|
|
44
52
|
list?: string;
|
|
45
53
|
panels?: string;
|
package/dist/dom.js
CHANGED
|
@@ -113,6 +113,29 @@ export function sizeTweetEmbed(root, height, source) {
|
|
|
113
113
|
iframe.style.height = `${height}px`;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
+
// ---- maths ------------------------------------------------------------------
|
|
117
|
+
/** What marks maths in stored prose. No single `$`: prose writes $XRD, and two cashtags in one block would read as a formula. */
|
|
118
|
+
const MATH = /\$\$|\\\(|\\\[/;
|
|
119
|
+
/**
|
|
120
|
+
* Typeset the maths under `root` with KaTeX: `$$…$$` and `\[…\]` as a display,
|
|
121
|
+
* `\(…\)` inline. KaTeX and its stylesheet load only when the text holds a
|
|
122
|
+
* delimiter, so a page with no maths fetches nothing. KaTeX is an optional peer
|
|
123
|
+
* the app installs. Typeset maths carries no delimiter, so a second pass finds
|
|
124
|
+
* nothing to do.
|
|
125
|
+
*/
|
|
126
|
+
export async function renderMath(root) {
|
|
127
|
+
if (!MATH.test(root.textContent ?? ''))
|
|
128
|
+
return;
|
|
129
|
+
const [{ default: render }] = await Promise.all([import('katex/contrib/auto-render'), import('katex/dist/katex.min.css')]);
|
|
130
|
+
render(root, {
|
|
131
|
+
delimiters: [
|
|
132
|
+
{ left: '$$', right: '$$', display: true },
|
|
133
|
+
{ left: '\\(', right: '\\)', display: false },
|
|
134
|
+
{ left: '\\[', right: '\\]', display: true },
|
|
135
|
+
],
|
|
136
|
+
throwOnError: false,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
116
139
|
/**
|
|
117
140
|
* Turn stored `[data-tabs]` markup into a working tab group.
|
|
118
141
|
*
|
package/dist/react.d.ts
CHANGED
|
@@ -479,8 +479,8 @@ export declare function isRailLinkActive(activePath: string, href: string, tree?
|
|
|
479
479
|
export type { WikiLinkComponent, WikiLinkProps } from './react-server.js';
|
|
480
480
|
/**
|
|
481
481
|
* The `wiki-formant/dom` passes over a rendered article: tab groups, copy
|
|
482
|
-
* buttons, sortable tables, and each post on X hydrated and kept
|
|
483
|
-
* it posts. All three renderers ran the same passes in an effect, and they
|
|
482
|
+
* buttons, sortable tables, maths typeset, and each post on X hydrated and kept
|
|
483
|
+
* at the height it posts. All three renderers ran the same passes in an effect, and they
|
|
484
484
|
* disagreed on when: two ran it once on mount, so a page swapped in without a
|
|
485
485
|
* remount kept its old tables unsortable. Each pass is idempotent, so running
|
|
486
486
|
* on every `deps` change is safe.
|
package/dist/react.js
CHANGED
|
@@ -26,7 +26,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
26
26
|
// state on <html> before first paint. The hook keeps that attribute in
|
|
27
27
|
// sync afterwards, so CSS has one source of truth either side of hydration.
|
|
28
28
|
import { railBreakpointMismatch, readRailFloating, resolveSidebarOpen, SIDEBAR_ATTRIBUTE, } from './sidebar.js';
|
|
29
|
-
import { activateTabGroups, addCopyButtons, hydrateTweetEmbeds, onTweetResize, sizeTweetEmbed, sortTables, startBeacon } from './dom.js';
|
|
29
|
+
import { activateTabGroups, addCopyButtons, hydrateTweetEmbeds, onTweetResize, renderMath, sizeTweetEmbed, sortTables, startBeacon } from './dom.js';
|
|
30
30
|
import { comboboxAria } from './combobox.js';
|
|
31
31
|
import { Component, createContext, createElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
32
32
|
const readStored = (key) => {
|
|
@@ -883,8 +883,8 @@ const decodePath = (path) => {
|
|
|
883
883
|
// ---- rendered-article passes --------------------------------------------------
|
|
884
884
|
/**
|
|
885
885
|
* The `wiki-formant/dom` passes over a rendered article: tab groups, copy
|
|
886
|
-
* buttons, sortable tables, and each post on X hydrated and kept
|
|
887
|
-
* it posts. All three renderers ran the same passes in an effect, and they
|
|
886
|
+
* buttons, sortable tables, maths typeset, and each post on X hydrated and kept
|
|
887
|
+
* at the height it posts. All three renderers ran the same passes in an effect, and they
|
|
888
888
|
* disagreed on when: two ran it once on mount, so a page swapped in without a
|
|
889
889
|
* remount kept its old tables unsortable. Each pass is idempotent, so running
|
|
890
890
|
* on every `deps` change is safe.
|
|
@@ -897,6 +897,7 @@ export function useArticlePasses(ref, deps) {
|
|
|
897
897
|
activateTabGroups(root);
|
|
898
898
|
addCopyButtons(root);
|
|
899
899
|
sortTables(root);
|
|
900
|
+
void renderMath(root);
|
|
900
901
|
hydrateTweetEmbeds(root);
|
|
901
902
|
return onTweetResize((height, source) => sizeTweetEmbed(root, height, source));
|
|
902
903
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-formant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
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, conditional-GET plumbing, and the headers and CSP every origin sends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -328,12 +328,14 @@
|
|
|
328
328
|
"devDependencies": {
|
|
329
329
|
"@electric-sql/pglite": "^0.3.9",
|
|
330
330
|
"@radixdlt/rola": "^2.1.0",
|
|
331
|
+
"@types/katex": "^0.16.8",
|
|
331
332
|
"@types/node": "^22.0.0",
|
|
332
333
|
"@types/react": "^19.0.0",
|
|
333
334
|
"@types/sanitize-html": "^2.16.1",
|
|
334
335
|
"@vercel/blob": "^2.8.2",
|
|
335
336
|
"jose": "^6.2.12",
|
|
336
337
|
"jsdom": "^30.1.0",
|
|
338
|
+
"katex": "^0.16.47",
|
|
337
339
|
"react": "^19.2.4",
|
|
338
340
|
"sharp": "^0.35.5",
|
|
339
341
|
"typescript": "^5.9.0"
|
|
@@ -344,7 +346,8 @@
|
|
|
344
346
|
"jose": ">=6",
|
|
345
347
|
"pg": ">=8",
|
|
346
348
|
"react": ">=18",
|
|
347
|
-
"sharp": ">=0.33"
|
|
349
|
+
"sharp": ">=0.33",
|
|
350
|
+
"katex": "^0.16.0"
|
|
348
351
|
},
|
|
349
352
|
"peerDependenciesMeta": {
|
|
350
353
|
"react": {
|
|
@@ -364,6 +367,9 @@
|
|
|
364
367
|
},
|
|
365
368
|
"@vercel/blob": {
|
|
366
369
|
"optional": true
|
|
370
|
+
},
|
|
371
|
+
"katex": {
|
|
372
|
+
"optional": true
|
|
367
373
|
}
|
|
368
374
|
}
|
|
369
375
|
}
|