wiki-formant 0.26.1 → 0.27.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/README.md +31 -0
- package/dist/base.css +13 -0
- package/dist/editor.d.ts +13 -0
- package/dist/editor.js +19 -1
- package/dist/history.d.ts +37 -0
- package/dist/history.js +67 -0
- package/dist/react-server.d.ts +14 -0
- package/dist/react-server.js +18 -0
- package/dist/revisions.d.ts +6 -0
- package/dist/revisions.js +16 -0
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -387,6 +387,35 @@ One copy also built two Maps keyed by a recursive `JSON.stringify` of every
|
|
|
387
387
|
block, on every save, and never read either one. Matching is by id and always
|
|
388
388
|
was. That half is not here.
|
|
389
389
|
|
|
390
|
+
## History
|
|
391
|
+
|
|
392
|
+
What each revision changed, as a history view shows it. `revisions` stays free
|
|
393
|
+
of dependencies because every save imports it; this is the reading half, and it
|
|
394
|
+
needs `fast-diff`, an optional peer.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
import { withChanges } from 'wiki-formant/history';
|
|
398
|
+
import { RevisionChanges } from 'wiki-formant/react-server';
|
|
399
|
+
|
|
400
|
+
const rows = withChanges(revisionsNewestFirst); // each row needs its `content`
|
|
401
|
+
<RevisionChanges changes={rows[0].changes} label={type => BLOCK_LABELS[type]} />
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
- **Diffed on the server, from stored content.** radix-wiki used to send both
|
|
405
|
+
HTML bodies of every modified block to the browser and diff them there: 887 KB
|
|
406
|
+
of payload for a 13-revision page whose drawn diffs came to 25 KB. A row
|
|
407
|
+
leaves `withChanges` without its content.
|
|
408
|
+
- **Words, not letters.** Semantic cleanup is on, so a rewritten sentence reads
|
|
409
|
+
as the old words struck and the new ones added.
|
|
410
|
+
- **Leaves only.** A container's own entry restates its children. A leaf whose
|
|
411
|
+
type changed is a replacement and gets no text diff. `leafText` names the prose
|
|
412
|
+
field of a block beyond the core `content` one.
|
|
413
|
+
- **A window diffs against what precedes it.** The oldest row is diffed against
|
|
414
|
+
an empty page, so fetch one revision more than you show and drop the last row.
|
|
415
|
+
|
|
416
|
+
`RevisionChanges` marks struck and added text as `<del>` and `<ins>` and keys
|
|
417
|
+
each change on `data-action`; `base.css` only resets the list.
|
|
418
|
+
|
|
390
419
|
## Feeds
|
|
391
420
|
|
|
392
421
|
Three repos, four feeds, 47 of 64 significant lines identical — including,
|
|
@@ -512,6 +541,8 @@ The ones that take config take it because that is exactly where the two copies d
|
|
|
512
541
|
|
|
513
542
|
`TOOLBAR_ACTIONS` are the formatting commands with the labels a screen reader announces — two toolbars titled buttons with the internal key, so one said "codeBlock". Pick yours in order with `toolbarActions([...])`, render each through `ToolbarButton` (named, and `aria-pressed`, since these are toggles), and run a table command with `runTableAction`. Upload and embed stay with the caller; they are the parts that differ.
|
|
514
543
|
|
|
544
|
+
A link is stored as its `href` and the `link` class, nothing else (`wikiLink`). Stock Tiptap writes `target="_blank" rel="noopener noreferrer nofollow"` onto every link, internal ones included, which on a site throws away its own link equity and opens in-site navigation in new tabs. Whether a link is external depends on the host serving the page, so `normaliseLinks` decides it at render time. A legacy link loses both attributes the next time its page is saved.
|
|
545
|
+
|
|
515
546
|
The redirect resolves through the wiki's own route, because the editor cannot read it cross-origin. `resolveMapUrl` is the client half and `resolveMapHandler` the whole route: exact shortener hosts, one hop, an allowlisted landing host, a timeout, and your sign-in check as `authorize`. One of the two copies it replaced matched `goo.gl` as a substring and followed every redirect for anyone who asked.
|
|
516
547
|
|
|
517
548
|
```ts
|
package/dist/base.css
CHANGED
|
@@ -103,3 +103,16 @@ pre:hover > .code-copy-btn,
|
|
|
103
103
|
[aria-sort='descending'] > .sort-header::after {
|
|
104
104
|
--sort-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='m19 12-7 7-7-7'/%3E%3C/svg%3E");
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/* ---- RevisionChanges ------------------------------------------------------
|
|
108
|
+
A list of changes rather than a bulleted one, each diff directly under its
|
|
109
|
+
heading line. `del` and `ins` keep their browser defaults, a strike and an
|
|
110
|
+
underline, so a diff still reads before a stylesheet colours it. */
|
|
111
|
+
.revision-changes {
|
|
112
|
+
display: grid;
|
|
113
|
+
gap: 0.5em;
|
|
114
|
+
margin: 0;
|
|
115
|
+
padding: 0;
|
|
116
|
+
list-style: none;
|
|
117
|
+
}
|
|
118
|
+
.revision-diff { margin: 0.25em 0 0; }
|
package/dist/editor.d.ts
CHANGED
|
@@ -15,6 +15,19 @@ export type { StarterKitOptions } from '@tiptap/starter-kit';
|
|
|
15
15
|
export type { LinkOptions } from '@tiptap/extension-link';
|
|
16
16
|
export type { ImageOptions } from '@tiptap/extension-image';
|
|
17
17
|
export type { TableOptions } from '@tiptap/extension-table';
|
|
18
|
+
/**
|
|
19
|
+
* Tiptap's Link, storing an href and the `link` class and nothing else.
|
|
20
|
+
*
|
|
21
|
+
* Stock Link writes `target="_blank" rel="noopener noreferrer nofollow"` onto
|
|
22
|
+
* every link, internal ones included, and the schema then reads both back from
|
|
23
|
+
* stored HTML, so a page edited once kept them for good. Nofollow on a site's
|
|
24
|
+
* own links throws away its link equity, and a new tab for in-site navigation
|
|
25
|
+
* is a bug. Whether a link is external depends on the host it is served from,
|
|
26
|
+
* which the editor cannot know, so the render path decides: `normaliseLinks`
|
|
27
|
+
* from `wiki-formant/links`. Dropping both attributes from the mark also cleans
|
|
28
|
+
* a legacy link the next time its page is saved.
|
|
29
|
+
*/
|
|
30
|
+
export declare const wikiLink: import("@tiptap/core").Mark<import("@tiptap/extension-link").LinkOptions, any>;
|
|
18
31
|
export interface WikiEditorExtensionOptions {
|
|
19
32
|
/** Empty-document prompt. */
|
|
20
33
|
placeholder?: string;
|
package/dist/editor.js
CHANGED
|
@@ -56,6 +56,24 @@ function cleanPastedHtml(html) {
|
|
|
56
56
|
});
|
|
57
57
|
return doc.body.innerHTML;
|
|
58
58
|
}
|
|
59
|
+
// ---- extensions -------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* Tiptap's Link, storing an href and the `link` class and nothing else.
|
|
62
|
+
*
|
|
63
|
+
* Stock Link writes `target="_blank" rel="noopener noreferrer nofollow"` onto
|
|
64
|
+
* every link, internal ones included, and the schema then reads both back from
|
|
65
|
+
* stored HTML, so a page edited once kept them for good. Nofollow on a site's
|
|
66
|
+
* own links throws away its link equity, and a new tab for in-site navigation
|
|
67
|
+
* is a bug. Whether a link is external depends on the host it is served from,
|
|
68
|
+
* which the editor cannot know, so the render path decides: `normaliseLinks`
|
|
69
|
+
* from `wiki-formant/links`. Dropping both attributes from the mark also cleans
|
|
70
|
+
* a legacy link the next time its page is saved.
|
|
71
|
+
*/
|
|
72
|
+
export const wikiLink = TiptapLink.extend({
|
|
73
|
+
addAttributes() {
|
|
74
|
+
return Object.fromEntries(Object.entries(this.parent?.() ?? {}).filter(([name]) => name !== 'target' && name !== 'rel'));
|
|
75
|
+
},
|
|
76
|
+
}).configure({ openOnClick: false, HTMLAttributes: { class: 'link', target: null, rel: null } });
|
|
59
77
|
/**
|
|
60
78
|
* The editor's extension set.
|
|
61
79
|
*
|
|
@@ -67,7 +85,7 @@ function cleanPastedHtml(html) {
|
|
|
67
85
|
function wikiEditorExtensions({ placeholder = '', nodes = [], } = {}) {
|
|
68
86
|
return [
|
|
69
87
|
StarterKit.configure({ heading: { levels: [2, 3, 4] }, codeBlock: false }),
|
|
70
|
-
|
|
88
|
+
wikiLink,
|
|
71
89
|
TiptapImage.configure({
|
|
72
90
|
inline: false,
|
|
73
91
|
allowBase64: true,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type BlockChange, type BlockGroup, type DiffBlock } from './revisions.js';
|
|
2
|
+
/** A stretch of text: -1 struck, 0 kept, 1 added. */
|
|
3
|
+
export type DiffPart = [-1 | 0 | 1, string];
|
|
4
|
+
/** A change as a history view lists it: the diff it draws, not the two bodies behind it. */
|
|
5
|
+
export type HistoryChange = BlockChange<DiffPart[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Two HTML bodies as the words struck and added between them, or `undefined`
|
|
8
|
+
* when their text is the same. Semantic cleanup is on: without it a rewritten
|
|
9
|
+
* sentence comes out as fragments of the letters the two versions share.
|
|
10
|
+
*/
|
|
11
|
+
export declare function textDiff(from: string, to: string): DiffPart[] | undefined;
|
|
12
|
+
export interface HistoryOptions<B> {
|
|
13
|
+
/** Defaults to `coreBlockGroups`, as in `diffBlocks`. */
|
|
14
|
+
containers?: (block: B) => BlockGroup<B>[] | null;
|
|
15
|
+
/**
|
|
16
|
+
* The HTML a leaf's diff is drawn from, or `undefined` for a block with no
|
|
17
|
+
* prose. Defaults to a `content` block's `text`, the core model's one prose leaf.
|
|
18
|
+
*/
|
|
19
|
+
leafText?: (block: B) => string | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What changed between two versions of a page, leaves only: a container's own
|
|
23
|
+
* entry restates its children. A leaf whose type changed is a replacement, and
|
|
24
|
+
* one side's text against the other's would read as an edit, so it gets none.
|
|
25
|
+
*/
|
|
26
|
+
export declare function historyChanges<B extends DiffBlock>(oldContent: readonly B[], newContent: readonly B[], opts?: HistoryOptions<B>): HistoryChange[];
|
|
27
|
+
/**
|
|
28
|
+
* A page's revisions, newest first, each carrying what changed against the one
|
|
29
|
+
* after it in the list and without the content it was diffed from. The last is
|
|
30
|
+
* diffed against an empty page, so a caller showing a window of the history
|
|
31
|
+
* passes one revision more than it shows and drops the last result.
|
|
32
|
+
*/
|
|
33
|
+
export declare function withChanges<R extends {
|
|
34
|
+
content: unknown;
|
|
35
|
+
}, B extends DiffBlock = DiffBlock>(revisions: readonly R[], opts?: HistoryOptions<B>): (Omit<R, 'content'> & {
|
|
36
|
+
changes: HistoryChange[];
|
|
37
|
+
})[];
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// history.ts – what changed between revisions, as a history view draws it.
|
|
2
|
+
//
|
|
3
|
+
// `revisions.ts` decides which bump a save asks for and stays free of
|
|
4
|
+
// dependencies, because every write path imports it. This is the reading half:
|
|
5
|
+
// the same walk, turned into what a reviewer is shown. It needs a text diff, so
|
|
6
|
+
// it needs `fast-diff`, an optional peer; hence its own subpath.
|
|
7
|
+
//
|
|
8
|
+
// radix-wiki had it first and shipped both HTML bodies of every modified block
|
|
9
|
+
// to the browser to diff there: 887 KB of payload for a 13-revision page whose
|
|
10
|
+
// drawn diffs came to 25 KB. The other two wikis showed a revision's message
|
|
11
|
+
// and nothing of what it changed.
|
|
12
|
+
import fastDiff from 'fast-diff';
|
|
13
|
+
import { diffBlocks, extractBlocks } from './revisions.js';
|
|
14
|
+
import { coreBlockGroups } from './blocks.js';
|
|
15
|
+
import { stripHtml } from './text.js';
|
|
16
|
+
/** Kept text is context, so a run longer than this keeps only its ends. */
|
|
17
|
+
const KEPT_MAX = 60;
|
|
18
|
+
const clip = (t) => (t.length > KEPT_MAX ? `${t.slice(0, 30)}…${t.slice(-30)}` : t);
|
|
19
|
+
/**
|
|
20
|
+
* Two HTML bodies as the words struck and added between them, or `undefined`
|
|
21
|
+
* when their text is the same. Semantic cleanup is on: without it a rewritten
|
|
22
|
+
* sentence comes out as fragments of the letters the two versions share.
|
|
23
|
+
*/
|
|
24
|
+
export function textDiff(from, to) {
|
|
25
|
+
const a = stripHtml(from);
|
|
26
|
+
const b = stripHtml(to);
|
|
27
|
+
if (a === b)
|
|
28
|
+
return undefined;
|
|
29
|
+
const parts = fastDiff(a, b, undefined, true)
|
|
30
|
+
.filter(([, t]) => t.trim())
|
|
31
|
+
.map(([op, t]) => [op, op === 0 ? clip(t) : t]);
|
|
32
|
+
return parts.length ? parts : undefined;
|
|
33
|
+
}
|
|
34
|
+
const contentText = (block) => block.type === 'content' ? block.text : undefined;
|
|
35
|
+
/**
|
|
36
|
+
* What changed between two versions of a page, leaves only: a container's own
|
|
37
|
+
* entry restates its children. A leaf whose type changed is a replacement, and
|
|
38
|
+
* one side's text against the other's would read as an edit, so it gets none.
|
|
39
|
+
*/
|
|
40
|
+
export function historyChanges(oldContent, newContent, opts = {}) {
|
|
41
|
+
const containers = opts.containers ?? (coreBlockGroups);
|
|
42
|
+
const leafText = opts.leafText ?? contentText;
|
|
43
|
+
const containerIds = new Set([...extractBlocks(oldContent, containers), ...extractBlocks(newContent, containers)]
|
|
44
|
+
.filter(({ block }) => containers(block))
|
|
45
|
+
.map(({ block }) => block.id));
|
|
46
|
+
return diffBlocks(oldContent, newContent, {
|
|
47
|
+
containers,
|
|
48
|
+
leafDiff: (from, to) => {
|
|
49
|
+
const a = from ? leafText(from) : '';
|
|
50
|
+
const b = to ? leafText(to) : '';
|
|
51
|
+
return a === undefined || b === undefined ? undefined : textDiff(a, b);
|
|
52
|
+
},
|
|
53
|
+
}).filter(c => !containerIds.has(c.id));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A page's revisions, newest first, each carrying what changed against the one
|
|
57
|
+
* after it in the list and without the content it was diffed from. The last is
|
|
58
|
+
* diffed against an empty page, so a caller showing a window of the history
|
|
59
|
+
* passes one revision more than it shows and drops the last result.
|
|
60
|
+
*/
|
|
61
|
+
export function withChanges(revisions, opts = {}) {
|
|
62
|
+
const blocks = (r) => (r?.content ?? []);
|
|
63
|
+
return revisions.map((revision, i) => {
|
|
64
|
+
const { content: _, ...rest } = revision;
|
|
65
|
+
return { ...rest, changes: historyChanges(blocks(revisions[i + 1]), blocks(revision), opts) };
|
|
66
|
+
});
|
|
67
|
+
}
|
package/dist/react-server.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type { Control, FacetControlGroup, MetadataRow } from './taxonomy.js';
|
|
3
|
+
import type { HistoryChange } from './history.js';
|
|
3
4
|
/**
|
|
4
5
|
* The shape a router's link component has to satisfy here.
|
|
5
6
|
*
|
|
@@ -253,3 +254,16 @@ export interface InfoboxFactsProps {
|
|
|
253
254
|
* shares its value — the way into the set, not dead text.
|
|
254
255
|
*/
|
|
255
256
|
export declare function InfoboxFacts({ rows, link: Link, formatValue }: InfoboxFactsProps): import("react").JSX.Element | null;
|
|
257
|
+
export interface RevisionChangesProps {
|
|
258
|
+
/** One revision's changes, from `historyChanges` or `withChanges` in `wiki-formant/history`. */
|
|
259
|
+
changes: readonly HistoryChange[];
|
|
260
|
+
/** What a block type is called where a reader sees it, such as the editor's label. Defaults to the type. */
|
|
261
|
+
label?: (type: string) => string;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* What one revision changed, block by block, each prose leaf with its text
|
|
265
|
+
* diff. Struck and added text are `<del>` and `<ins>`, which a screen reader
|
|
266
|
+
* announces as such; their colours, and the action's, keyed on `data-action`,
|
|
267
|
+
* are the importing stylesheet's.
|
|
268
|
+
*/
|
|
269
|
+
export declare function RevisionChanges({ changes, label }: RevisionChangesProps): import("react").JSX.Element | null;
|
package/dist/react-server.js
CHANGED
|
@@ -21,6 +21,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
21
21
|
import { Fragment } from 'react';
|
|
22
22
|
import { safeLinkHref } from './validation.js';
|
|
23
23
|
import { isoDate } from './html.js';
|
|
24
|
+
import { blockLocation } from './revisions.js';
|
|
24
25
|
/**
|
|
25
26
|
* The default for anything that does not need client-side navigation.
|
|
26
27
|
*
|
|
@@ -207,3 +208,20 @@ export function InfoboxFacts({ rows, link: Link = Anchor, formatValue = formatFa
|
|
|
207
208
|
return null;
|
|
208
209
|
return (_jsx("table", { className: "infobox-facts", children: _jsx("tbody", { children: rows.map(row => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: row.label }), _jsx("td", { children: row.href ? _jsx(Link, { href: row.href, children: row.value }) : formatValue(row.value, row.type) })] }, row.label))) }) }));
|
|
209
210
|
}
|
|
211
|
+
const ACTION_LABEL = {
|
|
212
|
+
added: 'Added',
|
|
213
|
+
removed: 'Removed',
|
|
214
|
+
modified: 'Modified',
|
|
215
|
+
moved: 'Moved',
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* What one revision changed, block by block, each prose leaf with its text
|
|
219
|
+
* diff. Struck and added text are `<del>` and `<ins>`, which a screen reader
|
|
220
|
+
* announces as such; their colours, and the action's, keyed on `data-action`,
|
|
221
|
+
* are the importing stylesheet's.
|
|
222
|
+
*/
|
|
223
|
+
export function RevisionChanges({ changes, label = type => type }) {
|
|
224
|
+
if (!changes.length)
|
|
225
|
+
return null;
|
|
226
|
+
return (_jsx("ul", { className: "revision-changes", children: changes.map((c, i) => (_jsxs("li", { className: "revision-change", "data-action": c.action, children: [_jsx("span", { className: "revision-change-action", children: ACTION_LABEL[c.action] }), ` – ${label(c.type)} at ${blockLocation(c.path)}`, c.leafDiff && (_jsx("p", { className: "revision-diff", children: c.leafDiff.map(([op, text], j) => op === -1 ? _jsx("del", { children: text }, j) : op === 1 ? _jsx("ins", { children: text }, j) : _jsx("span", { children: text }, j)) }))] }, i))) }));
|
|
227
|
+
}
|
package/dist/revisions.d.ts
CHANGED
|
@@ -56,6 +56,12 @@ interface Located<B> {
|
|
|
56
56
|
block: B;
|
|
57
57
|
path: string;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A path as a reader says it: `root.1.columns.0.blocks.2` is
|
|
61
|
+
* "Block 2 → Column 1 → Block 3". Positions count from one; a segment that
|
|
62
|
+
* names a group (`blocks`, `columns`) only says what the next number counts.
|
|
63
|
+
*/
|
|
64
|
+
export declare function blockLocation(path: string): string;
|
|
59
65
|
/** Every block in the tree, flattened, each with the path that addresses it. */
|
|
60
66
|
export declare function extractBlocks<B>(blocks: readonly B[], containers: (block: B) => BlockGroup<B>[] | null, basePath?: string): Located<B>[];
|
|
61
67
|
/**
|
package/dist/revisions.js
CHANGED
|
@@ -6,6 +6,22 @@
|
|
|
6
6
|
// Matching is by id.
|
|
7
7
|
import { incrementVersion, parseVersion } from './versioning.js';
|
|
8
8
|
import { coreBlockGroups } from './blocks.js';
|
|
9
|
+
/**
|
|
10
|
+
* A path as a reader says it: `root.1.columns.0.blocks.2` is
|
|
11
|
+
* "Block 2 → Column 1 → Block 3". Positions count from one; a segment that
|
|
12
|
+
* names a group (`blocks`, `columns`) only says what the next number counts.
|
|
13
|
+
*/
|
|
14
|
+
export function blockLocation(path) {
|
|
15
|
+
let group = '';
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const seg of path.split('.').slice(1)) {
|
|
18
|
+
if (/^\d+$/.test(seg))
|
|
19
|
+
out.push(`${group === 'columns' ? 'Column' : 'Block'} ${Number(seg) + 1}`);
|
|
20
|
+
else
|
|
21
|
+
group = seg;
|
|
22
|
+
}
|
|
23
|
+
return out.join(' → ');
|
|
24
|
+
}
|
|
9
25
|
/** Every block in the tree, flattened, each with the path that addresses it. */
|
|
10
26
|
export function extractBlocks(blocks, containers, basePath = 'root') {
|
|
11
27
|
const out = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-formant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.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",
|
|
@@ -78,6 +78,10 @@
|
|
|
78
78
|
"types": "./dist/revisions.d.ts",
|
|
79
79
|
"import": "./dist/revisions.js"
|
|
80
80
|
},
|
|
81
|
+
"./history": {
|
|
82
|
+
"types": "./dist/history.d.ts",
|
|
83
|
+
"import": "./dist/history.js"
|
|
84
|
+
},
|
|
81
85
|
"./search": {
|
|
82
86
|
"types": "./dist/search.d.ts",
|
|
83
87
|
"import": "./dist/search.js"
|
|
@@ -189,7 +193,8 @@
|
|
|
189
193
|
},
|
|
190
194
|
"scripts": {
|
|
191
195
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/base.css dist/base.css",
|
|
192
|
-
"test": "npm run build && node --test 'test/*.test.js'"
|
|
196
|
+
"test": "npm run build && node --test 'test/*.test.js'",
|
|
197
|
+
"prepare": "git config core.hooksPath .githooks || true"
|
|
193
198
|
},
|
|
194
199
|
"devDependencies": {
|
|
195
200
|
"@radixdlt/rola": "^2.1.0",
|
|
@@ -209,6 +214,7 @@
|
|
|
209
214
|
"@types/node": "^22.0.0",
|
|
210
215
|
"@types/react": "^19.0.0",
|
|
211
216
|
"@types/sanitize-html": "^2.16.1",
|
|
217
|
+
"fast-diff": "^1.3.0",
|
|
212
218
|
"jose": "^6.2.10",
|
|
213
219
|
"jsdom": "^30.1.0",
|
|
214
220
|
"react": "^19.2.4",
|
|
@@ -227,12 +233,13 @@
|
|
|
227
233
|
"@tiptap/extension-table-header": ">=2.10",
|
|
228
234
|
"@tiptap/extension-table-row": ">=2.10",
|
|
229
235
|
"@tiptap/extension-youtube": ">=2.10",
|
|
236
|
+
"@tiptap/pm": ">=2.10",
|
|
230
237
|
"@tiptap/react": ">=2.10",
|
|
231
238
|
"@tiptap/starter-kit": ">=2.10",
|
|
239
|
+
"fast-diff": ">=1.3",
|
|
232
240
|
"jose": ">=5",
|
|
233
241
|
"react": ">=18",
|
|
234
|
-
"sanitize-html": ">=2"
|
|
235
|
-
"@tiptap/pm": ">=2.10"
|
|
242
|
+
"sanitize-html": ">=2"
|
|
236
243
|
},
|
|
237
244
|
"peerDependenciesMeta": {
|
|
238
245
|
"react": {
|
|
@@ -285,6 +292,9 @@
|
|
|
285
292
|
},
|
|
286
293
|
"@tiptap/pm": {
|
|
287
294
|
"optional": true
|
|
295
|
+
},
|
|
296
|
+
"fast-diff": {
|
|
297
|
+
"optional": true
|
|
288
298
|
}
|
|
289
299
|
}
|
|
290
300
|
}
|