noteloom 0.3.2 → 0.4.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 +344 -53
- package/bin/noteloom.mjs +139 -0
- package/dist/canvas.cjs +2 -0
- package/dist/canvas.cjs.map +1 -0
- package/dist/canvas.d.ts +2 -0
- package/dist/canvas.js +5 -0
- package/dist/canvas.js.map +1 -0
- package/dist/collab.cjs +2 -0
- package/dist/collab.cjs.map +1 -0
- package/dist/collab.d.ts +19 -0
- package/dist/collab.js +379 -0
- package/dist/collab.js.map +1 -0
- package/dist/comments.cjs +2 -0
- package/dist/comments.cjs.map +1 -0
- package/dist/comments.d.ts +17 -0
- package/dist/comments.js +19 -0
- package/dist/comments.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +392 -32
- package/dist/index.js +3983 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence.cjs +2 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.ts +11 -0
- package/dist/persistence.js +12 -0
- package/dist/persistence.js.map +1 -0
- package/dist/shared/CommentAvatar-TKqsM8vG.js +53 -0
- package/dist/shared/CommentAvatar-TKqsM8vG.js.map +1 -0
- package/dist/shared/CommentAvatar-V457IgoQ.cjs +2 -0
- package/dist/shared/CommentAvatar-V457IgoQ.cjs.map +1 -0
- package/dist/shared/CommentThreadCard-CAJ3oPdr.cjs +2 -0
- package/dist/shared/CommentThreadCard-CAJ3oPdr.cjs.map +1 -0
- package/dist/shared/CommentThreadCard-Cxf8GwHJ.js +213 -0
- package/dist/shared/CommentThreadCard-Cxf8GwHJ.js.map +1 -0
- package/dist/shared/CommentsPanel-BwVVyMOC.js +76 -0
- package/dist/shared/CommentsPanel-BwVVyMOC.js.map +1 -0
- package/dist/shared/CommentsPanel-CES4uzye.cjs +2 -0
- package/dist/shared/CommentsPanel-CES4uzye.cjs.map +1 -0
- package/dist/shared/VersionHistory-Bg6fufP5.js +1120 -0
- package/dist/shared/VersionHistory-Bg6fufP5.js.map +1 -0
- package/dist/shared/VersionHistory-DELa2Wbq.cjs +12 -0
- package/dist/shared/VersionHistory-DELa2Wbq.cjs.map +1 -0
- package/dist/shared/VoiceListeningIndicator-BOfVmLK0.js +338 -0
- package/dist/shared/VoiceListeningIndicator-BOfVmLK0.js.map +1 -0
- package/dist/shared/VoiceListeningIndicator-DNX8JlQM.cjs +2 -0
- package/dist/shared/VoiceListeningIndicator-DNX8JlQM.cjs.map +1 -0
- package/dist/shared/fieldRegistry-BoYyW3GH.cjs +2 -0
- package/dist/shared/fieldRegistry-BoYyW3GH.cjs.map +1 -0
- package/dist/shared/fieldRegistry-PjJWXFoB.js +223 -0
- package/dist/shared/fieldRegistry-PjJWXFoB.js.map +1 -0
- package/dist/shared/icons-C1_s0jKK.js +4269 -0
- package/dist/shared/icons-C1_s0jKK.js.map +1 -0
- package/dist/shared/icons-DtCsXuU6.cjs +3531 -0
- package/dist/shared/icons-DtCsXuU6.cjs.map +1 -0
- package/dist/shared/index-B9jyyL8u.js +2145 -0
- package/dist/shared/index-B9jyyL8u.js.map +1 -0
- package/dist/shared/index-D9WEXpYM.cjs +2 -0
- package/dist/shared/index-D9WEXpYM.cjs.map +1 -0
- package/dist/shared/indexedDbPersistence-BKlGPFTP.js +112 -0
- package/dist/shared/indexedDbPersistence-BKlGPFTP.js.map +1 -0
- package/dist/shared/indexedDbPersistence-BWJ1h5Vp.cjs +2 -0
- package/dist/shared/indexedDbPersistence-BWJ1h5Vp.cjs.map +1 -0
- package/dist/shared/leafBlockFactory-A9eUhSe-.js +335 -0
- package/dist/shared/leafBlockFactory-A9eUhSe-.js.map +1 -0
- package/dist/shared/leafBlockFactory-Dp382eP8.cjs +2 -0
- package/dist/shared/leafBlockFactory-Dp382eP8.cjs.map +1 -0
- package/dist/shared/serialize-DWEfk0vU.cjs +3 -0
- package/dist/shared/serialize-DWEfk0vU.cjs.map +1 -0
- package/dist/shared/serialize-T9Odwx24.js +86 -0
- package/dist/shared/serialize-T9Odwx24.js.map +1 -0
- package/dist/shared/starter-kit-CY5wzYDf.js +4980 -0
- package/dist/shared/starter-kit-CY5wzYDf.js.map +1 -0
- package/dist/shared/starter-kit-lPN-NpJ5.cjs +27 -0
- package/dist/shared/starter-kit-lPN-NpJ5.cjs.map +1 -0
- package/dist/shared/usePopoverEdgeClamp-CDG26jBM.cjs +2 -0
- package/dist/shared/usePopoverEdgeClamp-CDG26jBM.cjs.map +1 -0
- package/dist/shared/usePopoverEdgeClamp-gmFm0CnI.js +109 -0
- package/dist/shared/usePopoverEdgeClamp-gmFm0CnI.js.map +1 -0
- package/dist/shared/useServiceWorkerUpdate-BduabXv6.js +94 -0
- package/dist/shared/useServiceWorkerUpdate-BduabXv6.js.map +1 -0
- package/dist/shared/useServiceWorkerUpdate-DQboGesi.cjs +2 -0
- package/dist/shared/useServiceWorkerUpdate-DQboGesi.cjs.map +1 -0
- package/dist/starter-kit.cjs +2 -0
- package/dist/starter-kit.cjs.map +1 -0
- package/dist/starter-kit.d.ts +9 -0
- package/dist/starter-kit.js +10 -0
- package/dist/starter-kit.js.map +1 -0
- package/dist/style.css +3529 -3200
- package/dist/versions.cjs +2 -0
- package/dist/versions.cjs.map +1 -0
- package/dist/versions.d.ts +11 -0
- package/dist/versions.js +13 -0
- package/dist/versions.js.map +1 -0
- package/dist/voice.cjs +2 -0
- package/dist/voice.cjs.map +1 -0
- package/dist/voice.d.ts +7 -0
- package/dist/voice.js +8 -0
- package/dist/voice.js.map +1 -0
- package/package.json +159 -92
- package/dist/noteloom.cjs +0 -3209
- package/dist/noteloom.cjs.map +0 -1
- package/dist/noteloom.es.js +0 -14622
- package/dist/noteloom.es.js.map +0 -1
|
@@ -0,0 +1,1120 @@
|
|
|
1
|
+
import { jsxs as b, Fragment as D, jsx as k } from "react/jsx-runtime";
|
|
2
|
+
import { useState as v, useCallback as Z, useEffect as M, useRef as H, useMemo as $ } from "react";
|
|
3
|
+
import { createPortal as tt } from "react-dom";
|
|
4
|
+
import { g as et, O as m, p as st, q as nt, t as it, f as rt, e as ot, v as ct, c as dt, d as at, w as lt, x as ht, y as ut, o as mt, r as ft, s as kt, m as bt, n as pt, i as yt, j as It, z, l as K, k as _t, a as gt, A as wt, B as Tt, D as Rt, X as Et, R as St } from "./icons-C1_s0jKK.js";
|
|
5
|
+
import { f as Ct, c as V, b as Ot } from "./indexedDbPersistence-BKlGPFTP.js";
|
|
6
|
+
import { c as q, r as vt, s as J } from "./serialize-T9Odwx24.js";
|
|
7
|
+
import { g as xt, H as Pt, F as At, L } from "./fieldRegistry-PjJWXFoB.js";
|
|
8
|
+
import { C as j, f as Mt } from "./CommentAvatar-TKqsM8vG.js";
|
|
9
|
+
function Nt(a, t) {
|
|
10
|
+
const i = Array.from(a ?? ""), e = Array.from(t ?? "");
|
|
11
|
+
let s = 0;
|
|
12
|
+
const n = Math.min(i.length, e.length);
|
|
13
|
+
for (; s < n && i[s] === e[s]; ) s += 1;
|
|
14
|
+
let r = i.length, o = e.length;
|
|
15
|
+
for (; r > s && o > s && i[r - 1] === e[o - 1]; )
|
|
16
|
+
r -= 1, o -= 1;
|
|
17
|
+
return { start: s, oldEnd: r, newEnd: o, oldChars: i, newChars: e };
|
|
18
|
+
}
|
|
19
|
+
const B = "$fieldTypes", R = "$comments", O = "$people";
|
|
20
|
+
class F {
|
|
21
|
+
constructor(t) {
|
|
22
|
+
this.blocks = new Map(((t == null ? void 0 : t.blocks) ?? []).map((i) => [i.id, i])), this.runs = new Map(((t == null ? void 0 : t.runs) ?? []).map((i) => [i.id, i])), this.rootId = (t == null ? void 0 : t.rootId) ?? null, this.fieldTypes = new Map(((t == null ? void 0 : t.fieldTypes) ?? []).map((i) => [i.id, i])), this._fieldTypesSnapshot = null, this.comments = new Map(((t == null ? void 0 : t.comments) ?? []).map((i) => [i.id, i])), this._commentsSnapshot = null, this.people = new Map(((t == null ? void 0 : t.people) ?? []).map((i) => [i.id, i])), this._peopleSnapshot = null, this._listeners = /* @__PURE__ */ new Map(), this._globalListeners = /* @__PURE__ */ new Set(), this._peerId = xt(), this._clock = new Pt(this._peerId), this._fieldClocks = new At(), this._orders = /* @__PURE__ */ new Map(), this._runOrders = /* @__PURE__ */ new Map(), this._runChars = /* @__PURE__ */ new Map(), this._runValueSnapshots = /* @__PURE__ */ new Map();
|
|
23
|
+
}
|
|
24
|
+
/** Lazily seeds a block's ordering CRDT state from its current (plain-array) contentIds the first time it's touched. */
|
|
25
|
+
_getOrder(t) {
|
|
26
|
+
let i = this._orders.get(t);
|
|
27
|
+
if (!i) {
|
|
28
|
+
const e = this.blocks.get(t);
|
|
29
|
+
i = L.fromArray((e == null ? void 0 : e.contentIds) ?? [], { peerId: "legacy" }), this._orders.set(t, i);
|
|
30
|
+
}
|
|
31
|
+
return i;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Lazily seeds a run's character-ordering CRDT state from its current
|
|
35
|
+
* (plain-string) value the first time it's touched. Unlike blocks,
|
|
36
|
+
* individual characters have no pre-existing stable id of their own —
|
|
37
|
+
* BUT a fresh *random* one (genId()) can't be minted here the way
|
|
38
|
+
* `_getRunOrder`'s block equivalent doesn't need to, because a block's
|
|
39
|
+
* seed ids (its real `.id`s) are already identical on every peer (the
|
|
40
|
+
* document itself was already synced); a run's *characters* aren't
|
|
41
|
+
* separately synced at all, only its plain `value` string is. Two peers
|
|
42
|
+
* independently lazy-seeding the same pre-existing text with random ids
|
|
43
|
+
* would each invent a DIFFERENT id for "the same" character, so a
|
|
44
|
+
* remote edit's `afterId` (anchored to the sender's random id) would
|
|
45
|
+
* never resolve on the receiving side. Fix: ids for this initial seed
|
|
46
|
+
* are DETERMINISTIC — `` `${runId}:legacyChar:${index}` `` — so any peer
|
|
47
|
+
* seeding from the same starting string (guaranteed, since a peer only
|
|
48
|
+
* ever needs to seed a run it has already received the full document
|
|
49
|
+
* for) always mints the identical ids independently, with no
|
|
50
|
+
* coordination required. Only ever used for this one-time seed; every
|
|
51
|
+
* character inserted after that (by typing, on any peer) gets a real
|
|
52
|
+
* `genId()`.
|
|
53
|
+
*/
|
|
54
|
+
_getRunOrder(t) {
|
|
55
|
+
let i = this._runOrders.get(t);
|
|
56
|
+
if (!i) {
|
|
57
|
+
const e = this.runs.get(t), s = /* @__PURE__ */ new Map();
|
|
58
|
+
i = new L();
|
|
59
|
+
let n = null, r = 0;
|
|
60
|
+
for (const o of Array.from((e == null ? void 0 : e.value) ?? "")) {
|
|
61
|
+
const c = `${t}:legacyChar:${r}`;
|
|
62
|
+
s.set(c, o), i.insert(c, n, { wallTime: 0, counter: 0, peerId: "legacy" }, "legacy"), n = c, r += 1;
|
|
63
|
+
}
|
|
64
|
+
this._runOrders.set(t, i), this._runChars.set(t, s);
|
|
65
|
+
}
|
|
66
|
+
return i;
|
|
67
|
+
}
|
|
68
|
+
/** Materializes a run's current text from its character CRDT, memoized until that run's char order next changes. */
|
|
69
|
+
_materializeRunValue(t) {
|
|
70
|
+
let i = this._runValueSnapshots.get(t);
|
|
71
|
+
if (i === void 0) {
|
|
72
|
+
const e = this._getRunOrder(t), s = this._runChars.get(t);
|
|
73
|
+
i = e.toArray().map((n) => s.get(n)).join(""), this._runValueSnapshots.set(t, i);
|
|
74
|
+
}
|
|
75
|
+
return i;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Diffs `newValue` against the run's current materialized value (common
|
|
79
|
+
* prefix/suffix, code-point aware — see diffCodePoints) and translates
|
|
80
|
+
* only the changed region into character-CRDT insert/delete ops. One
|
|
81
|
+
* shared clock covers the whole edit — matching how a single
|
|
82
|
+
* INSERT_BLOCK/REMOVE_BLOCK op already uses one tick() each — since
|
|
83
|
+
* these characters are never independently reordered relative to each
|
|
84
|
+
* other regardless of how many ticks they'd otherwise get.
|
|
85
|
+
*/
|
|
86
|
+
_editRunText(t, i) {
|
|
87
|
+
const e = this._materializeRunValue(t), { start: s, oldEnd: n, newEnd: r, newChars: o } = Nt(e, i), c = this._getRunOrder(t), d = this._runChars.get(t), l = c.toArray(), h = this._clock.tick(), u = [];
|
|
88
|
+
for (let w = s; w < n; w += 1) {
|
|
89
|
+
const g = l[w];
|
|
90
|
+
c.delete(g, h), u.push({ id: g, clock: h });
|
|
91
|
+
}
|
|
92
|
+
const f = [];
|
|
93
|
+
let T = s > 0 ? l[s - 1] : null;
|
|
94
|
+
for (let w = s; w < r; w += 1) {
|
|
95
|
+
const g = et(), _ = o[w];
|
|
96
|
+
d.set(g, _), c.insert(g, T, h, this._peerId), f.push({ id: g, char: _, afterId: T, clock: h }), T = g;
|
|
97
|
+
}
|
|
98
|
+
return this._runValueSnapshots.delete(t), { oldValue: e, insertedChars: f, deletedCharIds: u, clock: h };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Replays a previously-resolved text edit's EXACT character ids (from
|
|
102
|
+
* `op._charEdit`, cached on first application — see the UPDATE_RUN case)
|
|
103
|
+
* instead of re-diffing and minting fresh ones. Needed for `History`'s
|
|
104
|
+
* redo, which replays the same op OBJECT a second time: re-diffing would
|
|
105
|
+
* mint a brand-new set of ids rather than restoring the ones the
|
|
106
|
+
* matching undo had only tombstoned, breaking a subsequent undo/redo
|
|
107
|
+
* cycle (mirrors how INSERT_BLOCK's own handler restores an existing
|
|
108
|
+
* slot via `order.restore()` on redo instead of creating a new one,
|
|
109
|
+
* since a block's id is already stable across replay — characters have
|
|
110
|
+
* no such built-in stable id, so this cache is what gives them one).
|
|
111
|
+
*/
|
|
112
|
+
_replayRunTextEdit(t, i) {
|
|
113
|
+
const e = this._getRunOrder(t), s = this._runChars.get(t);
|
|
114
|
+
for (const n of i.insertedChars)
|
|
115
|
+
e.has(n.id) ? e.restore(n.id) : (s.set(n.id, n.char), e.insert(n.id, n.afterId, n.clock, this._peerId));
|
|
116
|
+
for (const n of i.deletedCharIds) e.delete(n.id, n.clock);
|
|
117
|
+
this._runValueSnapshots.delete(t);
|
|
118
|
+
}
|
|
119
|
+
getBlock(t) {
|
|
120
|
+
return this.blocks.get(t);
|
|
121
|
+
}
|
|
122
|
+
getRun(t) {
|
|
123
|
+
return this.runs.get(t);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Persisted, user-created custom select field types (static only) —
|
|
127
|
+
* insertion order. Returns the SAME array reference across calls unless
|
|
128
|
+
* a fieldTypes op ran in between — required for useSyncExternalStore
|
|
129
|
+
* (see useFieldTypes), same reference-stability contract as
|
|
130
|
+
* getBlock/getRun.
|
|
131
|
+
*/
|
|
132
|
+
getFieldTypes() {
|
|
133
|
+
return this._fieldTypesSnapshot || (this._fieldTypesSnapshot = [...this.fieldTypes.values()]), this._fieldTypesSnapshot;
|
|
134
|
+
}
|
|
135
|
+
getFieldType(t) {
|
|
136
|
+
return this.fieldTypes.get(t);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Every comment thread — insertion order. Returns the SAME array
|
|
140
|
+
* reference across calls unless a comments op ran in between — required
|
|
141
|
+
* for useSyncExternalStore (see useComments), same contract as
|
|
142
|
+
* getFieldTypes.
|
|
143
|
+
*/
|
|
144
|
+
getComments() {
|
|
145
|
+
return this._commentsSnapshot || (this._commentsSnapshot = [...this.comments.values()]), this._commentsSnapshot;
|
|
146
|
+
}
|
|
147
|
+
getComment(t) {
|
|
148
|
+
return this.comments.get(t);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The document's own people list (see src/people/people.js's addPerson —
|
|
152
|
+
* the documented entry point, and createSelectFieldType's "Assignee"
|
|
153
|
+
* usage, which sources its options from this instead of any device-local
|
|
154
|
+
* list, so every collaborator on the same document sees the same
|
|
155
|
+
* assignable people). Same reference-stability contract as getFieldTypes/
|
|
156
|
+
* getComments — required for useSyncExternalStore (see usePeople).
|
|
157
|
+
*/
|
|
158
|
+
getPeople() {
|
|
159
|
+
return this._peopleSnapshot || (this._peopleSnapshot = [...this.people.values()]), this._peopleSnapshot;
|
|
160
|
+
}
|
|
161
|
+
getPerson(t) {
|
|
162
|
+
return this.people.get(t);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Every run id in the whole document, regardless of reachability from
|
|
166
|
+
* the root — used by removeCommentMarkEverywhere (see
|
|
167
|
+
* src/comments/commentMarks.js) to strip a deleted comment's id from
|
|
168
|
+
* any run that still carries it, without needing a separate index.
|
|
169
|
+
*/
|
|
170
|
+
getAllRunIds() {
|
|
171
|
+
return [...this.runs.keys()];
|
|
172
|
+
}
|
|
173
|
+
getRootId() {
|
|
174
|
+
return this.rootId;
|
|
175
|
+
}
|
|
176
|
+
subscribe(t, i) {
|
|
177
|
+
let e = this._listeners.get(t);
|
|
178
|
+
return e || (e = /* @__PURE__ */ new Set(), this._listeners.set(t, e)), e.add(i), () => {
|
|
179
|
+
e.delete(i), e.size === 0 && this._listeners.delete(t);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Fires on every mutation, local or remote (applyOperation and
|
|
184
|
+
* applyRemoteOperation both funnel through _notify), regardless of
|
|
185
|
+
* which specific id(s) changed — unlike subscribe(id, ...), which is
|
|
186
|
+
* scoped to one id for render-isolation purposes. Meant for
|
|
187
|
+
* whole-document concerns like auto-persistence, not for UI rendering
|
|
188
|
+
* (a component re-rendering on every edit anywhere defeats the point of
|
|
189
|
+
* per-id subscriptions).
|
|
190
|
+
*/
|
|
191
|
+
subscribeAll(t) {
|
|
192
|
+
return this._globalListeners.add(t), () => this._globalListeners.delete(t);
|
|
193
|
+
}
|
|
194
|
+
_notify(t) {
|
|
195
|
+
for (const i of t) {
|
|
196
|
+
const e = this._listeners.get(i);
|
|
197
|
+
if (e)
|
|
198
|
+
for (const s of [...e]) s();
|
|
199
|
+
}
|
|
200
|
+
if (this._globalListeners.size > 0)
|
|
201
|
+
for (const i of [...this._globalListeners]) i();
|
|
202
|
+
}
|
|
203
|
+
/** Recursively collects a block (or run) and everything reachable under it. */
|
|
204
|
+
_captureSubtree(t) {
|
|
205
|
+
const i = [], e = [], s = (n) => {
|
|
206
|
+
var o;
|
|
207
|
+
if (this.runs.has(n)) {
|
|
208
|
+
e.push(this.runs.get(n));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const r = this.blocks.get(n);
|
|
212
|
+
if (r) {
|
|
213
|
+
i.push(r);
|
|
214
|
+
for (const c of r.contentIds) s(c);
|
|
215
|
+
for (const c of ((o = r.props) == null ? void 0 : o.titleRunIds) ?? []) s(c);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
return s(t), { blocks: i, runs: e };
|
|
219
|
+
}
|
|
220
|
+
_deleteSubtree(t) {
|
|
221
|
+
const i = (e) => {
|
|
222
|
+
var n;
|
|
223
|
+
if (this.runs.has(e)) {
|
|
224
|
+
this.runs.delete(e);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const s = this.blocks.get(e);
|
|
228
|
+
if (s) {
|
|
229
|
+
for (const r of s.contentIds) i(r);
|
|
230
|
+
for (const r of ((n = s.props) == null ? void 0 : n.titleRunIds) ?? []) i(r);
|
|
231
|
+
this.blocks.delete(e);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
i(t);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Applies one operation, mutating only the affected map entries, and
|
|
238
|
+
* returns the inverse operation (computed from state captured *before* the
|
|
239
|
+
* mutation, per-op — never reconstructed afterward).
|
|
240
|
+
*/
|
|
241
|
+
applyOperation(t) {
|
|
242
|
+
var i;
|
|
243
|
+
switch (t.type) {
|
|
244
|
+
case m.UPDATE_RUN: {
|
|
245
|
+
const e = this.runs.get(t.id), s = Object.keys(t.patch);
|
|
246
|
+
if (s.length === 1 && s[0] === "value") {
|
|
247
|
+
let o = t._charEdit;
|
|
248
|
+
if (o)
|
|
249
|
+
this._replayRunTextEdit(t.id, o);
|
|
250
|
+
else {
|
|
251
|
+
const { oldValue: d, insertedChars: l, deletedCharIds: h, clock: u } = this._editRunText(
|
|
252
|
+
t.id,
|
|
253
|
+
t.patch.value
|
|
254
|
+
);
|
|
255
|
+
o = { oldValue: d, insertedChars: l, deletedCharIds: h, clock: u }, t._charEdit = o;
|
|
256
|
+
}
|
|
257
|
+
const c = this._materializeRunValue(t.id);
|
|
258
|
+
return this.runs.set(t.id, { ...e, value: c }), this._lastEnvelope = {
|
|
259
|
+
kind: "runTextEdit",
|
|
260
|
+
runId: t.id,
|
|
261
|
+
insertedChars: o.insertedChars,
|
|
262
|
+
deletedCharIds: o.deletedCharIds,
|
|
263
|
+
clock: o.clock
|
|
264
|
+
}, this._notify([t.id]), z(
|
|
265
|
+
t.id,
|
|
266
|
+
o.insertedChars.map((d) => d.id),
|
|
267
|
+
// undo: tombstone what was just inserted...
|
|
268
|
+
o.deletedCharIds.map((d) => d.id),
|
|
269
|
+
// ...and restore what was just deleted
|
|
270
|
+
o.oldValue
|
|
271
|
+
// cosmetic-only, for History's caret restoration/change log -- see editRunChars's own doc comment
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
const n = {}, r = {};
|
|
275
|
+
for (const o of s)
|
|
276
|
+
n[o] = e[o], r[o] = this._clock.tick(), this._fieldClocks.recordLocal(t.id, o, r[o]);
|
|
277
|
+
return this.runs.set(t.id, { ...e, ...t.patch }), this._lastEnvelope = {
|
|
278
|
+
kind: "fieldWrite",
|
|
279
|
+
target: "run",
|
|
280
|
+
id: t.id,
|
|
281
|
+
patch: t.patch,
|
|
282
|
+
clocks: r
|
|
283
|
+
}, this._notify([t.id]), K(t.id, n);
|
|
284
|
+
}
|
|
285
|
+
case m.EDIT_RUN_CHARS: {
|
|
286
|
+
const e = this._getRunOrder(t.id), s = this._clock.tick();
|
|
287
|
+
for (const o of t.tombstoneIds) e.delete(o, s);
|
|
288
|
+
for (const o of t.restoreIds) e.restore(o);
|
|
289
|
+
this._runValueSnapshots.delete(t.id);
|
|
290
|
+
const n = this.runs.get(t.id), r = this._materializeRunValue(t.id);
|
|
291
|
+
return this.runs.set(t.id, { ...n, value: r }), this._lastEnvelope = {
|
|
292
|
+
kind: "runCharEdit",
|
|
293
|
+
runId: t.id,
|
|
294
|
+
tombstoneIds: t.tombstoneIds,
|
|
295
|
+
restoreIds: t.restoreIds,
|
|
296
|
+
clock: s
|
|
297
|
+
}, this._notify([t.id]), z(t.id, t.restoreIds, t.tombstoneIds);
|
|
298
|
+
}
|
|
299
|
+
case m.UPDATE_BLOCK_PROPS: {
|
|
300
|
+
const e = this.blocks.get(t.id), s = {}, n = {};
|
|
301
|
+
for (const r of Object.keys(t.patch))
|
|
302
|
+
s[r] = e.props[r], n[r] = this._clock.tick(), this._fieldClocks.recordLocal(t.id, `props.${r}`, n[r]);
|
|
303
|
+
return this.blocks.set(t.id, { ...e, props: { ...e.props, ...t.patch } }), this._lastEnvelope = {
|
|
304
|
+
kind: "fieldWrite",
|
|
305
|
+
target: "blockProps",
|
|
306
|
+
id: t.id,
|
|
307
|
+
patch: t.patch,
|
|
308
|
+
clocks: n
|
|
309
|
+
}, this._notify([t.id]), It(t.id, s);
|
|
310
|
+
}
|
|
311
|
+
case m.CHANGE_BLOCK_TYPE: {
|
|
312
|
+
const e = this.blocks.get(t.id), s = e.type, n = e.props, r = this._clock.tick();
|
|
313
|
+
return this._fieldClocks.recordLocal(t.id, "type", r), this.blocks.set(t.id, { ...e, type: t.blockType, props: t.props }), this._lastEnvelope = {
|
|
314
|
+
kind: "fieldWrite",
|
|
315
|
+
target: "blockType",
|
|
316
|
+
id: t.id,
|
|
317
|
+
blockType: t.blockType,
|
|
318
|
+
props: t.props,
|
|
319
|
+
clock: r
|
|
320
|
+
}, this._notify([t.id]), yt(t.id, s, n);
|
|
321
|
+
}
|
|
322
|
+
case m.INSERT_BLOCK: {
|
|
323
|
+
if (t.subtree) {
|
|
324
|
+
for (const n of t.subtree.blocks) this.blocks.set(n.id, n);
|
|
325
|
+
for (const n of t.subtree.runs) this.runs.set(n.id, n);
|
|
326
|
+
} else
|
|
327
|
+
this.blocks.set(t.block.id, t.block);
|
|
328
|
+
const e = this.blocks.get(t.parentId), s = this._getOrder(t.parentId);
|
|
329
|
+
if (s.has(t.block.id))
|
|
330
|
+
s.restore(t.block.id);
|
|
331
|
+
else {
|
|
332
|
+
const n = s.toArray(), r = t.index ?? n.length, o = r > 0 ? n[r - 1] : null;
|
|
333
|
+
s.insert(t.block.id, o, this._clock.tick(), this._peerId);
|
|
334
|
+
}
|
|
335
|
+
return this.blocks.set(t.parentId, { ...e, contentIds: s.toArray() }), this._lastEnvelope = {
|
|
336
|
+
kind: "insertSlot",
|
|
337
|
+
parentId: t.parentId,
|
|
338
|
+
blockId: t.block.id,
|
|
339
|
+
slot: s.getSlot(t.block.id),
|
|
340
|
+
subtree: t.subtree ?? { blocks: [t.block], runs: [] }
|
|
341
|
+
}, this._notify([t.block.id, t.parentId]), pt(t.block.id);
|
|
342
|
+
}
|
|
343
|
+
case m.REMOVE_BLOCK: {
|
|
344
|
+
const e = this.blocks.get(t.id), s = e.parentId, n = this.blocks.get(s), r = this._getOrder(s), o = r.toArray().indexOf(t.id), c = this._captureSubtree(t.id);
|
|
345
|
+
this._deleteSubtree(t.id);
|
|
346
|
+
const d = this._clock.tick();
|
|
347
|
+
return r.delete(t.id, d), this.blocks.set(s, { ...n, contentIds: r.toArray() }), this._lastEnvelope = { kind: "deleteSlot", parentId: s, blockId: t.id, clock: d }, this._notify([s]), { type: m.INSERT_BLOCK, block: e, parentId: s, index: o, subtree: c };
|
|
348
|
+
}
|
|
349
|
+
case m.MOVE_BLOCK: {
|
|
350
|
+
const s = this.blocks.get(t.id).parentId, n = this._getOrder(s), r = n.toArray().indexOf(t.id);
|
|
351
|
+
n.delete(t.id, this._clock.tick()), this.blocks.set(s, {
|
|
352
|
+
...this.blocks.get(s),
|
|
353
|
+
contentIds: n.toArray()
|
|
354
|
+
});
|
|
355
|
+
const o = this._getOrder(t.toParentId), c = o.toArray(), d = t.toIndex ?? c.length, l = d > 0 ? c[d - 1] : null, h = this._clock.tick();
|
|
356
|
+
o.has(t.id) ? o.move(t.id, l, h, this._peerId) : o.insert(t.id, l, h, this._peerId), this.blocks.set(t.toParentId, {
|
|
357
|
+
...this.blocks.get(t.toParentId),
|
|
358
|
+
contentIds: o.toArray()
|
|
359
|
+
}), this.blocks.set(t.id, { ...this.blocks.get(t.id), parentId: t.toParentId }), this._lastEnvelope = {
|
|
360
|
+
kind: "moveSlot",
|
|
361
|
+
fromParentId: s,
|
|
362
|
+
toParentId: t.toParentId,
|
|
363
|
+
blockId: t.id,
|
|
364
|
+
slot: o.getSlot(t.id)
|
|
365
|
+
};
|
|
366
|
+
const u = /* @__PURE__ */ new Set([t.id, s, t.toParentId]);
|
|
367
|
+
return this._notify(u), bt(t.id, s, r);
|
|
368
|
+
}
|
|
369
|
+
case m.SET_BLOCK_CONTENT_IDS: {
|
|
370
|
+
const e = this.blocks.get(t.blockId), s = e.contentIds;
|
|
371
|
+
return this.blocks.set(t.blockId, { ...e, contentIds: t.contentIds }), this._orders.set(t.blockId, L.fromArray(t.contentIds, { peerId: "legacy" })), this._lastEnvelope = null, this._notify([t.blockId]), kt(t.blockId, s);
|
|
372
|
+
}
|
|
373
|
+
case m.REPLACE_RUN_SPAN: {
|
|
374
|
+
const e = this.blocks.get(t.blockId), s = e.contentIds.indexOf(t.oldRunIds[0]) !== -1, n = s ? e.contentIds : ((i = e.props) == null ? void 0 : i.titleRunIds) ?? [], r = n.indexOf(t.oldRunIds[0]), o = t.oldRunIds.map((d) => this.runs.get(d));
|
|
375
|
+
for (const d of t.oldRunIds) this.runs.delete(d);
|
|
376
|
+
for (const d of t.newRuns) this.runs.set(d.id, d);
|
|
377
|
+
const c = [...n];
|
|
378
|
+
return c.splice(r, t.oldRunIds.length, ...t.newRuns.map((d) => d.id)), s ? this.blocks.set(t.blockId, { ...e, contentIds: c }) : this.blocks.set(t.blockId, {
|
|
379
|
+
...e,
|
|
380
|
+
props: { ...e.props, titleRunIds: c }
|
|
381
|
+
}), this._lastEnvelope = null, this._notify([t.blockId, ...t.oldRunIds, ...t.newRuns.map((d) => d.id)]), ft(
|
|
382
|
+
t.blockId,
|
|
383
|
+
t.newRuns.map((d) => d.id),
|
|
384
|
+
o
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
case m.SET_BLOCK_RUNS: {
|
|
388
|
+
const e = this.blocks.get(t.blockId), s = e.props && "titleRunIds" in e.props, n = s ? e.props.titleRunIds : e.contentIds, r = n.map((c) => this.runs.get(c)).filter(Boolean);
|
|
389
|
+
for (const c of n) this.runs.delete(c);
|
|
390
|
+
for (const c of t.runs) this.runs.set(c.id, c);
|
|
391
|
+
const o = t.runs.map((c) => c.id);
|
|
392
|
+
return s ? this.blocks.set(t.blockId, {
|
|
393
|
+
...e,
|
|
394
|
+
props: { ...e.props, titleRunIds: o }
|
|
395
|
+
}) : this.blocks.set(t.blockId, { ...e, contentIds: o }), this._lastEnvelope = null, this._notify([t.blockId, ...n, ...o]), mt(t.blockId, r);
|
|
396
|
+
}
|
|
397
|
+
case m.ADD_FIELD_TYPE:
|
|
398
|
+
return this.fieldTypes.set(t.fieldType.id, t.fieldType), this._fieldTypesSnapshot = null, this._notify([B]), ut(t.fieldType.id);
|
|
399
|
+
case m.UPDATE_FIELD_TYPE: {
|
|
400
|
+
const e = this.fieldTypes.get(t.id), s = {};
|
|
401
|
+
for (const n of Object.keys(t.patch)) s[n] = e[n];
|
|
402
|
+
return this.fieldTypes.set(t.id, { ...e, ...t.patch }), this._fieldTypesSnapshot = null, this._notify([B]), ht(t.id, s);
|
|
403
|
+
}
|
|
404
|
+
case m.REMOVE_FIELD_TYPE: {
|
|
405
|
+
const e = this.fieldTypes.get(t.id);
|
|
406
|
+
return this.fieldTypes.delete(t.id), this._fieldTypesSnapshot = null, this._notify([B]), lt(e);
|
|
407
|
+
}
|
|
408
|
+
case m.ADD_COMMENT_THREAD:
|
|
409
|
+
return this.comments.set(t.thread.id, t.thread), this._commentsSnapshot = null, this._lastEnvelope = { kind: "commentWrite", op: "addThread", thread: t.thread }, this._notify([R]), at(t.thread.id);
|
|
410
|
+
case m.REMOVE_COMMENT_THREAD: {
|
|
411
|
+
const e = this.comments.get(t.commentId);
|
|
412
|
+
return this.comments.delete(t.commentId), this._commentsSnapshot = null, this._lastEnvelope = { kind: "commentWrite", op: "removeThread", commentId: t.commentId }, this._notify([R]), dt(e);
|
|
413
|
+
}
|
|
414
|
+
case m.ADD_COMMENT_REPLY: {
|
|
415
|
+
const e = this.comments.get(t.commentId);
|
|
416
|
+
return this.comments.set(t.commentId, { ...e, messages: [...e.messages, t.message] }), this._commentsSnapshot = null, this._lastEnvelope = {
|
|
417
|
+
kind: "commentWrite",
|
|
418
|
+
op: "addReply",
|
|
419
|
+
commentId: t.commentId,
|
|
420
|
+
message: t.message
|
|
421
|
+
}, this._notify([R]), ct(t.commentId, t.message.id);
|
|
422
|
+
}
|
|
423
|
+
case m.REMOVE_COMMENT_REPLY: {
|
|
424
|
+
const e = this.comments.get(t.commentId), s = e.messages.find((n) => n.id === t.messageId);
|
|
425
|
+
return this.comments.set(t.commentId, {
|
|
426
|
+
...e,
|
|
427
|
+
messages: e.messages.filter((n) => n.id !== t.messageId)
|
|
428
|
+
}), this._commentsSnapshot = null, this._lastEnvelope = {
|
|
429
|
+
kind: "commentWrite",
|
|
430
|
+
op: "removeReply",
|
|
431
|
+
commentId: t.commentId,
|
|
432
|
+
messageId: t.messageId
|
|
433
|
+
}, this._notify([R]), ot(t.commentId, s);
|
|
434
|
+
}
|
|
435
|
+
case m.RESOLVE_COMMENT: {
|
|
436
|
+
const e = this.comments.get(t.commentId), s = e.resolved;
|
|
437
|
+
return this.comments.set(t.commentId, { ...e, resolved: t.resolved }), this._commentsSnapshot = null, this._lastEnvelope = {
|
|
438
|
+
kind: "commentWrite",
|
|
439
|
+
op: "resolve",
|
|
440
|
+
commentId: t.commentId,
|
|
441
|
+
resolved: t.resolved
|
|
442
|
+
}, this._notify([R]), rt(t.commentId, s);
|
|
443
|
+
}
|
|
444
|
+
case m.ADD_PERSON:
|
|
445
|
+
return this.people.set(t.person.id, t.person), this._peopleSnapshot = null, this._lastEnvelope = { kind: "peopleWrite", op: "add", person: t.person }, this._notify([O]), it(t.person.id);
|
|
446
|
+
case m.UPDATE_PERSON: {
|
|
447
|
+
const e = this.people.get(t.id), s = {};
|
|
448
|
+
for (const n of Object.keys(t.patch)) s[n] = e[n];
|
|
449
|
+
return this.people.set(t.id, { ...e, ...t.patch }), this._peopleSnapshot = null, this._lastEnvelope = { kind: "peopleWrite", op: "update", id: t.id, patch: t.patch }, this._notify([O]), nt(t.id, s);
|
|
450
|
+
}
|
|
451
|
+
case m.REMOVE_PERSON: {
|
|
452
|
+
const e = this.people.get(t.id);
|
|
453
|
+
return this.people.delete(t.id), this._peopleSnapshot = null, this._lastEnvelope = { kind: "peopleWrite", op: "remove", id: t.id }, this._notify([O]), st(e);
|
|
454
|
+
}
|
|
455
|
+
default:
|
|
456
|
+
throw new Error(`Unknown operation type: ${t.type}`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/** Applies a batch as one transaction: all mutations first, one notify pass, one combined inverse list. */
|
|
460
|
+
applyOperations(t) {
|
|
461
|
+
return t.map((i) => this.applyOperation(i));
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* The CRDT-native wire envelope for whatever `applyOperation` just did —
|
|
465
|
+
* read this immediately after each call to broadcast the change to
|
|
466
|
+
* other peers. `null` for op types with no CRDT-safe representation yet
|
|
467
|
+
* (the coarse resync ops: setBlockContentIds/replaceRunSpan/
|
|
468
|
+
* setBlockRuns/fieldType ops) — those remain local-only in this phase.
|
|
469
|
+
*/
|
|
470
|
+
getLastEnvelope() {
|
|
471
|
+
return this._lastEnvelope;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Applies a change received from another peer. Deliberately separate
|
|
475
|
+
* from `applyOperation`/`History.perform` — a remote change must never
|
|
476
|
+
* enter the local undo stack (only `perform`/`performBatch` push onto
|
|
477
|
+
* it), so this method is the only way for a remote envelope to reach
|
|
478
|
+
* the store. Idempotent and order-independent per envelope kind: safe
|
|
479
|
+
* to call more than once, or with envelopes arriving out of order.
|
|
480
|
+
*/
|
|
481
|
+
applyRemoteOperation(t) {
|
|
482
|
+
var i, e;
|
|
483
|
+
if (t)
|
|
484
|
+
switch (t.kind) {
|
|
485
|
+
case "insertSlot": {
|
|
486
|
+
for (const r of t.subtree.blocks)
|
|
487
|
+
this.blocks.has(r.id) || this.blocks.set(r.id, r);
|
|
488
|
+
for (const r of t.subtree.runs) this.runs.has(r.id) || this.runs.set(r.id, r);
|
|
489
|
+
const s = this._getOrder(t.parentId);
|
|
490
|
+
s.merge([t.slot]);
|
|
491
|
+
const n = this.blocks.get(t.parentId);
|
|
492
|
+
this.blocks.set(t.parentId, { ...n, contentIds: s.toArray() }), this._clock.receive(t.slot.clock), this._notify([t.blockId, t.parentId]);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
case "deleteSlot": {
|
|
496
|
+
const s = this._getOrder(t.parentId);
|
|
497
|
+
this.blocks.has(t.blockId) && this._deleteSubtree(t.blockId), s.delete(t.blockId, t.clock), t.clock && this._clock.receive(t.clock);
|
|
498
|
+
const n = this.blocks.get(t.parentId);
|
|
499
|
+
this.blocks.set(t.parentId, { ...n, contentIds: s.toArray() }), this._notify([t.parentId]);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
case "moveSlot": {
|
|
503
|
+
const s = this._getOrder(t.fromParentId);
|
|
504
|
+
s.delete(t.blockId, (i = t.slot) == null ? void 0 : i.clock), this.blocks.set(t.fromParentId, {
|
|
505
|
+
...this.blocks.get(t.fromParentId),
|
|
506
|
+
contentIds: s.toArray()
|
|
507
|
+
});
|
|
508
|
+
const n = this._getOrder(t.toParentId), r = n.has(t.blockId);
|
|
509
|
+
n.merge([t.slot]), r && n.move(
|
|
510
|
+
t.blockId,
|
|
511
|
+
t.slot.originId,
|
|
512
|
+
t.slot.clock,
|
|
513
|
+
t.slot.peerId
|
|
514
|
+
), this.blocks.set(t.toParentId, {
|
|
515
|
+
...this.blocks.get(t.toParentId),
|
|
516
|
+
contentIds: n.toArray()
|
|
517
|
+
}), this.blocks.has(t.blockId) && this.blocks.set(t.blockId, {
|
|
518
|
+
...this.blocks.get(t.blockId),
|
|
519
|
+
parentId: t.toParentId
|
|
520
|
+
}), this._clock.receive(t.slot.clock), this._notify([t.blockId, t.fromParentId, t.toParentId]);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
case "fieldWrite": {
|
|
524
|
+
if (t.target === "run") {
|
|
525
|
+
const s = this.runs.get(t.id);
|
|
526
|
+
if (s) {
|
|
527
|
+
const n = {};
|
|
528
|
+
for (const r of Object.keys(t.patch)) {
|
|
529
|
+
const o = t.clocks[r];
|
|
530
|
+
this._fieldClocks.shouldApplyRemote(t.id, r, o) && (n[r] = t.patch[r], this._fieldClocks.recordRemote(t.id, r, o));
|
|
531
|
+
}
|
|
532
|
+
Object.keys(n).length > 0 && (this.runs.set(t.id, { ...s, ...n }), this._notify([t.id]));
|
|
533
|
+
}
|
|
534
|
+
} else if (t.target === "blockProps") {
|
|
535
|
+
const s = this.blocks.get(t.id);
|
|
536
|
+
if (s) {
|
|
537
|
+
const n = {};
|
|
538
|
+
for (const r of Object.keys(t.patch)) {
|
|
539
|
+
const o = t.clocks[r];
|
|
540
|
+
this._fieldClocks.shouldApplyRemote(t.id, `props.${r}`, o) && (n[r] = t.patch[r], this._fieldClocks.recordRemote(t.id, `props.${r}`, o));
|
|
541
|
+
}
|
|
542
|
+
Object.keys(n).length > 0 && (this.blocks.set(t.id, { ...s, props: { ...s.props, ...n } }), this._notify([t.id]));
|
|
543
|
+
}
|
|
544
|
+
} else if (t.target === "blockType") {
|
|
545
|
+
const s = this.blocks.get(t.id);
|
|
546
|
+
s && this._fieldClocks.shouldApplyRemote(t.id, "type", t.clock) && (this._fieldClocks.recordRemote(t.id, "type", t.clock), this.blocks.set(t.id, {
|
|
547
|
+
...s,
|
|
548
|
+
type: t.blockType,
|
|
549
|
+
props: t.props
|
|
550
|
+
}), this._notify([t.id]));
|
|
551
|
+
}
|
|
552
|
+
for (const s of Object.values(t.clocks ?? {})) this._clock.receive(s);
|
|
553
|
+
t.clock && this._clock.receive(t.clock);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
case "runTextEdit": {
|
|
557
|
+
const s = this._getRunOrder(t.runId), n = this._runChars.get(t.runId);
|
|
558
|
+
for (const o of t.insertedChars)
|
|
559
|
+
s.has(o.id) || n.set(o.id, o.char), s.insert(o.id, o.afterId, o.clock, (e = o.clock) == null ? void 0 : e.peerId);
|
|
560
|
+
for (const o of t.deletedCharIds) s.delete(o.id, o.clock);
|
|
561
|
+
this._runValueSnapshots.delete(t.runId);
|
|
562
|
+
const r = this.runs.get(t.runId);
|
|
563
|
+
r && (this.runs.set(t.runId, {
|
|
564
|
+
...r,
|
|
565
|
+
value: this._materializeRunValue(t.runId)
|
|
566
|
+
}), this._notify([t.runId])), this._clock.receive(t.clock);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
case "runCharEdit": {
|
|
570
|
+
const s = this._getRunOrder(t.runId);
|
|
571
|
+
for (const r of t.tombstoneIds) s.delete(r, t.clock);
|
|
572
|
+
for (const r of t.restoreIds) s.restore(r);
|
|
573
|
+
this._runValueSnapshots.delete(t.runId);
|
|
574
|
+
const n = this.runs.get(t.runId);
|
|
575
|
+
n && (this.runs.set(t.runId, {
|
|
576
|
+
...n,
|
|
577
|
+
value: this._materializeRunValue(t.runId)
|
|
578
|
+
}), this._notify([t.runId])), this._clock.receive(t.clock);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
case "commentWrite": {
|
|
582
|
+
if (t.op === "addThread")
|
|
583
|
+
this.comments.has(t.thread.id) || (this.comments.set(t.thread.id, t.thread), this._commentsSnapshot = null, this._notify([R]));
|
|
584
|
+
else if (t.op === "removeThread")
|
|
585
|
+
this.comments.delete(t.commentId) && (this._commentsSnapshot = null, this._notify([R]));
|
|
586
|
+
else if (t.op === "addReply") {
|
|
587
|
+
const s = this.comments.get(t.commentId);
|
|
588
|
+
s && !s.messages.some((n) => n.id === t.message.id) && (this.comments.set(t.commentId, {
|
|
589
|
+
...s,
|
|
590
|
+
messages: [...s.messages, t.message]
|
|
591
|
+
}), this._commentsSnapshot = null, this._notify([R]));
|
|
592
|
+
} else if (t.op === "removeReply") {
|
|
593
|
+
const s = this.comments.get(t.commentId);
|
|
594
|
+
s && s.messages.some((n) => n.id === t.messageId) && (this.comments.set(t.commentId, {
|
|
595
|
+
...s,
|
|
596
|
+
messages: s.messages.filter((n) => n.id !== t.messageId)
|
|
597
|
+
}), this._commentsSnapshot = null, this._notify([R]));
|
|
598
|
+
} else if (t.op === "resolve") {
|
|
599
|
+
const s = this.comments.get(t.commentId);
|
|
600
|
+
s && (this.comments.set(t.commentId, { ...s, resolved: t.resolved }), this._commentsSnapshot = null, this._notify([R]));
|
|
601
|
+
}
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
case "peopleWrite": {
|
|
605
|
+
if (t.op === "add")
|
|
606
|
+
this.people.has(t.person.id) || (this.people.set(t.person.id, t.person), this._peopleSnapshot = null, this._notify([O]));
|
|
607
|
+
else if (t.op === "update") {
|
|
608
|
+
const s = this.people.get(t.id);
|
|
609
|
+
s && (this.people.set(t.id, { ...s, ...t.patch }), this._peopleSnapshot = null, this._notify([O]));
|
|
610
|
+
} else t.op === "remove" && this.people.delete(t.id) && (this._peopleSnapshot = null, this._notify([O]));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
default:
|
|
614
|
+
throw new Error(`Unknown remote envelope kind: ${t.kind}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/** Total tombstoned (deleted but still retained) slots across every parent's ordering CRDT and every run's character CRDT — a cheap signal for whether pruneTombstones() is worth calling, or just for observing growth over a long session. */
|
|
618
|
+
getTombstoneCount() {
|
|
619
|
+
let t = 0;
|
|
620
|
+
for (const i of this._orders.values()) t += i.tombstoneCount();
|
|
621
|
+
for (const i of this._runOrders.values()) t += i.tombstoneCount();
|
|
622
|
+
return t;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Permanently removes tombstones older than `maxAgeMs` (default 24h)
|
|
626
|
+
* across every parent's ordering CRDT — see ListCrdtState.pruneTombstones
|
|
627
|
+
* for the safety argument this relies on (this package's reconnect model
|
|
628
|
+
* always resyncs via a full snapshot, never a replayed op log, which is
|
|
629
|
+
* what makes a time-based threshold safe here specifically). Opt-in:
|
|
630
|
+
* never called automatically by anything in this store — call it
|
|
631
|
+
* yourself on whatever schedule fits (see createPeriodicTombstoneGC for
|
|
632
|
+
* a ready-made "just handle it" helper). Returns the number of slots
|
|
633
|
+
* removed.
|
|
634
|
+
*/
|
|
635
|
+
pruneTombstones({ maxAgeMs: t = 24 * 60 * 60 * 1e3, now: i = Date.now() } = {}) {
|
|
636
|
+
const e = { wallTime: i - t, counter: 0, peerId: "" };
|
|
637
|
+
let s = 0;
|
|
638
|
+
for (const n of this._orders.values()) s += n.pruneTombstones(e);
|
|
639
|
+
for (const n of this._runOrders.values()) s += n.pruneTombstones(e);
|
|
640
|
+
return s;
|
|
641
|
+
}
|
|
642
|
+
toJSON() {
|
|
643
|
+
return {
|
|
644
|
+
blocks: [...this.blocks.values()],
|
|
645
|
+
runs: [...this.runs.values()],
|
|
646
|
+
rootId: this.rootId,
|
|
647
|
+
fieldTypes: [...this.fieldTypes.values()],
|
|
648
|
+
comments: [...this.comments.values()],
|
|
649
|
+
people: [...this.people.values()]
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
static fromJSON(t) {
|
|
653
|
+
return new F(t);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function Dt(a, t) {
|
|
657
|
+
if (typeof a.performBatch == "function") a.performBatch(t);
|
|
658
|
+
else for (const i of t) a.applyOperation(i);
|
|
659
|
+
}
|
|
660
|
+
function ne(a, t) {
|
|
661
|
+
return { roots: t.map((i) => q(a, i)) };
|
|
662
|
+
}
|
|
663
|
+
function Lt(a, t, { parentId: i, index: e }) {
|
|
664
|
+
const s = [];
|
|
665
|
+
let n = e;
|
|
666
|
+
for (const r of t.roots) {
|
|
667
|
+
const { block: o, runs: c, subtreeBlocks: d } = vt(r);
|
|
668
|
+
o.parentId = i, s.push(_t(o, i, n, { blocks: [o, ...d], runs: c })), n += 1;
|
|
669
|
+
}
|
|
670
|
+
Dt(a, s);
|
|
671
|
+
}
|
|
672
|
+
function Bt(a, t) {
|
|
673
|
+
const i = a.store ?? a;
|
|
674
|
+
i.blocks = new Map((t.blocks ?? []).map((e) => [e.id, e])), i.runs = new Map((t.runs ?? []).map((e) => [e.id, e])), i.rootId = t.rootId ?? null, i.fieldTypes = new Map((t.fieldTypes ?? []).map((e) => [e.id, e])), i._fieldTypesSnapshot = null, i.comments = new Map((t.comments ?? []).map((e) => [e.id, e])), i._commentsSnapshot = null, i._orders = /* @__PURE__ */ new Map(), i._notify([
|
|
675
|
+
...i.blocks.keys(),
|
|
676
|
+
...i.runs.keys(),
|
|
677
|
+
"$fieldTypes",
|
|
678
|
+
"$comments"
|
|
679
|
+
]);
|
|
680
|
+
}
|
|
681
|
+
function ie(a, t) {
|
|
682
|
+
for (const i of t)
|
|
683
|
+
a.register(`__blockTemplate:${i.id}`, {
|
|
684
|
+
component: () => null,
|
|
685
|
+
isLeaf: !0,
|
|
686
|
+
slashCommand: {
|
|
687
|
+
label: i.label,
|
|
688
|
+
icon: i.icon,
|
|
689
|
+
keywords: i.keywords ?? [i.label],
|
|
690
|
+
run(e, { blockId: s, runId: n, sliceStart: r, sliceEnd: o }) {
|
|
691
|
+
const c = e.getRun(n), d = (c == null ? void 0 : c.value) ?? "";
|
|
692
|
+
e.applyOperation(
|
|
693
|
+
K(n, { value: d.slice(0, r) + d.slice(o) })
|
|
694
|
+
);
|
|
695
|
+
const l = e.getBlock(s), u = e.getBlock(l.parentId).contentIds.indexOf(s) + 1;
|
|
696
|
+
Lt(e, i, { parentId: l.parentId, index: u });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function Vt(a, { pretty: t = !0 } = {}) {
|
|
702
|
+
const i = a.getRootId(), { blocks: e, runs: s } = q(a, i);
|
|
703
|
+
return JSON.stringify({ version: 1, rootId: i, blocks: e, runs: s }, null, t ? 2 : 0);
|
|
704
|
+
}
|
|
705
|
+
function X(a, t, i) {
|
|
706
|
+
const e = a.getBlock(a.getRootId());
|
|
707
|
+
return e ? J(a, t, e.contentIds, i).html : "";
|
|
708
|
+
}
|
|
709
|
+
function re(a, t, i) {
|
|
710
|
+
const e = a.getBlock(a.getRootId());
|
|
711
|
+
return e ? J(a, t, e.contentIds, i).text : "";
|
|
712
|
+
}
|
|
713
|
+
function oe(a, t, i) {
|
|
714
|
+
return `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40">
|
|
715
|
+
<head>
|
|
716
|
+
<meta charset="utf-8">
|
|
717
|
+
<title>Document</title>
|
|
718
|
+
<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:DoNotOptimizeForBrowser/></w:WordDocument></xml><![endif]-->
|
|
719
|
+
</head>
|
|
720
|
+
<body>${X(a, t, i)}</body>
|
|
721
|
+
</html>`;
|
|
722
|
+
}
|
|
723
|
+
function ce(a, t, i) {
|
|
724
|
+
const e = a.getBlock(a.getRootId());
|
|
725
|
+
if (!e) return "";
|
|
726
|
+
const s = { store: a, registry: t, inlineRegistry: i }, n = e.contentIds;
|
|
727
|
+
let r = "", o = null;
|
|
728
|
+
for (const c of n) {
|
|
729
|
+
const d = a.getBlock(c), l = t.get(d.type), h = (l.toMarkdown ?? l.toPlainText)(d, s);
|
|
730
|
+
if (o) {
|
|
731
|
+
const u = d.type === "listItem" && o.type === "listItem" && !!d.props.ordered == !!o.props.ordered, f = d.type === "blockquote" && o.type === "blockquote";
|
|
732
|
+
r += u || f ? `
|
|
733
|
+
` : `
|
|
734
|
+
|
|
735
|
+
`;
|
|
736
|
+
}
|
|
737
|
+
r += h, o = d;
|
|
738
|
+
}
|
|
739
|
+
return r;
|
|
740
|
+
}
|
|
741
|
+
const Ft = 5 * 60 * 1e3, Ht = 200;
|
|
742
|
+
function $t({
|
|
743
|
+
store: a,
|
|
744
|
+
docId: t,
|
|
745
|
+
idleMs: i = Ft,
|
|
746
|
+
maxVersions: e = Ht,
|
|
747
|
+
onSnapshot: s,
|
|
748
|
+
onError: n
|
|
749
|
+
}) {
|
|
750
|
+
let r = null, o = a.getHistoryLog().length, c = !1;
|
|
751
|
+
function d() {
|
|
752
|
+
r && (clearTimeout(r), r = null);
|
|
753
|
+
}
|
|
754
|
+
async function l() {
|
|
755
|
+
if (d(), !c) return;
|
|
756
|
+
c = !1;
|
|
757
|
+
const u = a.getHistoryLog(), f = u.slice(o);
|
|
758
|
+
if (o = u.length, f.length === 0) return;
|
|
759
|
+
const T = new Set(f.map((_) => _.id).filter(Boolean)), w = [...new Set(f.map((_) => _.actorId).filter(Boolean))], g = f[f.length - 1].actorId ?? null;
|
|
760
|
+
try {
|
|
761
|
+
const _ = JSON.parse(Vt(a)), S = {
|
|
762
|
+
id: crypto.randomUUID(),
|
|
763
|
+
docId: t,
|
|
764
|
+
timestamp: Date.now(),
|
|
765
|
+
authorId: g,
|
|
766
|
+
authorIds: w,
|
|
767
|
+
summary: `${T.size} block${T.size === 1 ? "" : "s"} changed`,
|
|
768
|
+
doc: _
|
|
769
|
+
};
|
|
770
|
+
await Ct(S), s == null || s(S);
|
|
771
|
+
const y = await V(t);
|
|
772
|
+
if (y.length > e) {
|
|
773
|
+
const C = y.slice(e);
|
|
774
|
+
await Promise.all(C.map((N) => Ot(N.id)));
|
|
775
|
+
}
|
|
776
|
+
} catch (_) {
|
|
777
|
+
n == null || n(_);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const h = a.subscribeToHistory(() => {
|
|
781
|
+
c = !0, d(), r = setTimeout(l, i);
|
|
782
|
+
});
|
|
783
|
+
return {
|
|
784
|
+
/** Stops listening. Does NOT flush a final in-progress window -- call `flush()` first if you want that (e.g. right before navigating away). */
|
|
785
|
+
stop() {
|
|
786
|
+
d(), h();
|
|
787
|
+
},
|
|
788
|
+
/** Closes and saves the current window immediately, without waiting for `idleMs` of inactivity. A no-op if nothing's happened since the last save. */
|
|
789
|
+
flush: l
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function zt(a, t) {
|
|
793
|
+
const i = W(a), e = W(t), s = i.length, n = e.length, r = Array.from({ length: s + 1 }, () => new Uint32Array(n + 1));
|
|
794
|
+
for (let h = s - 1; h >= 0; h -= 1)
|
|
795
|
+
for (let u = n - 1; u >= 0; u -= 1)
|
|
796
|
+
r[h][u] = i[h] === e[u] ? r[h + 1][u + 1] + 1 : Math.max(r[h + 1][u], r[h][u + 1]);
|
|
797
|
+
const o = [], c = (h, u) => {
|
|
798
|
+
const f = o[o.length - 1];
|
|
799
|
+
f && f.type === h ? f.text += u : o.push({ type: h, text: u });
|
|
800
|
+
};
|
|
801
|
+
let d = 0, l = 0;
|
|
802
|
+
for (; d < s && l < n; )
|
|
803
|
+
i[d] === e[l] ? (c("equal", i[d]), d += 1, l += 1) : r[d + 1][l] >= r[d][l + 1] ? (c("removed", i[d]), d += 1) : (c("added", e[l]), l += 1);
|
|
804
|
+
for (; d < s; )
|
|
805
|
+
c("removed", i[d]), d += 1;
|
|
806
|
+
for (; l < n; )
|
|
807
|
+
c("added", e[l]), l += 1;
|
|
808
|
+
return o;
|
|
809
|
+
}
|
|
810
|
+
function W(a) {
|
|
811
|
+
return (a ?? "").match(/\s+|[^\s]+/g) ?? [];
|
|
812
|
+
}
|
|
813
|
+
const jt = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
814
|
+
function A(a) {
|
|
815
|
+
return a.replace(/[&<>"']/g, (t) => jt[t]);
|
|
816
|
+
}
|
|
817
|
+
function U(a) {
|
|
818
|
+
const t = new Map(a.blocks.map((r) => [r.id, r])), i = new Map(a.runs.map((r) => [r.id, r])), e = [], s = (r) => r.map((o) => {
|
|
819
|
+
var c;
|
|
820
|
+
return ((c = i.get(o)) == null ? void 0 : c.value) ?? "";
|
|
821
|
+
}).join(""), n = (r) => {
|
|
822
|
+
var d;
|
|
823
|
+
const o = t.get(r);
|
|
824
|
+
if (!o) return;
|
|
825
|
+
const c = (d = o.props) == null ? void 0 : d.titleRunIds;
|
|
826
|
+
Array.isArray(c) && c.length > 0 ? e.push({ id: r, text: s(c) }) : o.contentIds.length > 0 && o.contentIds.every((l) => i.has(l)) && e.push({ id: r, text: s(o.contentIds) });
|
|
827
|
+
for (const l of o.contentIds)
|
|
828
|
+
i.has(l) || n(l);
|
|
829
|
+
};
|
|
830
|
+
return n(a.rootId), e;
|
|
831
|
+
}
|
|
832
|
+
const Y = {
|
|
833
|
+
rootId: "root",
|
|
834
|
+
blocks: [{ id: "root", type: "page", parentId: null, contentIds: [], props: {} }],
|
|
835
|
+
runs: []
|
|
836
|
+
};
|
|
837
|
+
function Wt(a, t) {
|
|
838
|
+
const i = U(a ?? Y), e = U(t ?? Y), s = new Map(i.map((o) => [o.id, o])), n = new Set(e.map((o) => o.id)), r = [];
|
|
839
|
+
for (const o of e) {
|
|
840
|
+
const c = s.get(o.id);
|
|
841
|
+
if (!c) {
|
|
842
|
+
const h = A(o.text) || " ";
|
|
843
|
+
r.push(
|
|
844
|
+
`<p class="be-version-diff-block"><span class="be-version-diff-added">${h}</span></p>`
|
|
845
|
+
);
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (c.text === o.text) {
|
|
849
|
+
r.push(`<p class="be-version-diff-block">${A(o.text) || " "}</p>`);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
const l = zt(c.text, o.text).map((h) => {
|
|
853
|
+
const u = A(h.text);
|
|
854
|
+
return h.type === "added" ? `<span class="be-version-diff-added">${u}</span>` : h.type === "removed" ? `<span class="be-version-diff-removed">${u}</span>` : u;
|
|
855
|
+
}).join("");
|
|
856
|
+
r.push(`<p class="be-version-diff-block">${l}</p>`);
|
|
857
|
+
}
|
|
858
|
+
for (const o of i)
|
|
859
|
+
if (!n.has(o.id)) {
|
|
860
|
+
const c = A(o.text) || " ";
|
|
861
|
+
r.push(
|
|
862
|
+
`<p class="be-version-diff-block"><span class="be-version-diff-removed">${c}</span></p>`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
return r.length === 0 ? '<p class="be-version-diff-empty">No text changes.</p>' : r.join("");
|
|
866
|
+
}
|
|
867
|
+
function Ut(a) {
|
|
868
|
+
const [t, i] = v([]), [e, s] = v(!1), n = Z(() => a ? (s(!1), V(a).then((r) => {
|
|
869
|
+
i(r), s(!0);
|
|
870
|
+
})) : Promise.resolve(), [a]);
|
|
871
|
+
return M(() => {
|
|
872
|
+
if (!a) {
|
|
873
|
+
i([]), s(!0);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
let r = !1;
|
|
877
|
+
return s(!1), V(a).then((o) => {
|
|
878
|
+
r || (i(o), s(!0));
|
|
879
|
+
}), () => {
|
|
880
|
+
r = !0;
|
|
881
|
+
};
|
|
882
|
+
}, [a]), { versions: t, isLoaded: e, refresh: n };
|
|
883
|
+
}
|
|
884
|
+
const Yt = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
885
|
+
function Kt(a) {
|
|
886
|
+
const t = new Date(a), i = /* @__PURE__ */ new Date(), e = (n) => new Date(n.getFullYear(), n.getMonth(), n.getDate()).getTime(), s = Math.round((e(i) - e(t)) / (24 * 60 * 60 * 1e3));
|
|
887
|
+
return s === 0 ? "Today" : s === 1 ? "Yesterday" : t.toLocaleDateString(void 0, {
|
|
888
|
+
month: "long",
|
|
889
|
+
day: "numeric",
|
|
890
|
+
year: t.getFullYear() !== i.getFullYear() ? "numeric" : void 0
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
function de({ docId: a, idleMs: t, maxVersions: i }) {
|
|
894
|
+
const e = gt(), s = wt(), n = Tt(), { versions: r, isLoaded: o, refresh: c } = Ut(a), [d, l] = v(!1), [h, u] = v(null), [f, T] = v("changes"), w = H(null), g = H(null);
|
|
895
|
+
M(() => {
|
|
896
|
+
if (typeof e.getHistoryLog != "function") return;
|
|
897
|
+
const p = $t({
|
|
898
|
+
store: e,
|
|
899
|
+
docId: a,
|
|
900
|
+
idleMs: t,
|
|
901
|
+
maxVersions: i,
|
|
902
|
+
onSnapshot: c
|
|
903
|
+
});
|
|
904
|
+
return () => p.stop();
|
|
905
|
+
}, [e, a]);
|
|
906
|
+
function _() {
|
|
907
|
+
l(!1), u(null), T("changes");
|
|
908
|
+
}
|
|
909
|
+
M(() => {
|
|
910
|
+
if (!d) return;
|
|
911
|
+
const p = (I) => {
|
|
912
|
+
I.key === "Escape" && _();
|
|
913
|
+
};
|
|
914
|
+
return document.addEventListener("keydown", p), () => document.removeEventListener("keydown", p);
|
|
915
|
+
}, [d]), M(() => {
|
|
916
|
+
var p, I, E;
|
|
917
|
+
if (d) {
|
|
918
|
+
g.current = document.activeElement;
|
|
919
|
+
const P = w.current;
|
|
920
|
+
(p = (P == null ? void 0 : P.querySelector(Yt)) ?? P) == null || p.focus();
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
(E = (I = g.current) == null ? void 0 : I.focus) == null || E.call(I), g.current = null;
|
|
924
|
+
}, [d]);
|
|
925
|
+
const S = r.findIndex((p) => p.id === h), y = S >= 0 ? r[S] : null, C = S >= 0 ? r[S + 1] ?? null : null, N = $(() => {
|
|
926
|
+
if (!y) return "";
|
|
927
|
+
const p = new F(y.doc);
|
|
928
|
+
return X(p, s, n);
|
|
929
|
+
}, [y, s, n]), G = $(() => y ? Wt((C == null ? void 0 : C.doc) ?? null, y.doc) : "", [y, C]);
|
|
930
|
+
function Q() {
|
|
931
|
+
y && window.confirm("Restore this version? This replaces the current document.") && (Bt(e, y.doc), _());
|
|
932
|
+
}
|
|
933
|
+
const x = [];
|
|
934
|
+
for (const p of r) {
|
|
935
|
+
const I = Kt(p.timestamp);
|
|
936
|
+
let E = x[x.length - 1];
|
|
937
|
+
(!E || E.label !== I) && (E = { label: I, versions: [] }, x.push(E)), E.versions.push(p);
|
|
938
|
+
}
|
|
939
|
+
return /* @__PURE__ */ b(D, { children: [
|
|
940
|
+
/* @__PURE__ */ b(
|
|
941
|
+
"button",
|
|
942
|
+
{
|
|
943
|
+
type: "button",
|
|
944
|
+
className: "be-version-history-trigger",
|
|
945
|
+
onClick: () => l(!0),
|
|
946
|
+
title: "Version history",
|
|
947
|
+
children: [
|
|
948
|
+
/* @__PURE__ */ k(Rt, { size: 15 }),
|
|
949
|
+
"Version history"
|
|
950
|
+
]
|
|
951
|
+
}
|
|
952
|
+
),
|
|
953
|
+
d && tt(
|
|
954
|
+
/* @__PURE__ */ b(D, { children: [
|
|
955
|
+
/* @__PURE__ */ k("div", { className: "be-version-history-backdrop", onClick: _ }),
|
|
956
|
+
/* @__PURE__ */ b(
|
|
957
|
+
"div",
|
|
958
|
+
{
|
|
959
|
+
ref: w,
|
|
960
|
+
className: "be-version-history-drawer",
|
|
961
|
+
role: "dialog",
|
|
962
|
+
"aria-modal": "true",
|
|
963
|
+
"aria-label": "Version history",
|
|
964
|
+
tabIndex: -1,
|
|
965
|
+
children: [
|
|
966
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-header", children: [
|
|
967
|
+
/* @__PURE__ */ k("span", { children: y ? "Preview" : "Version history" }),
|
|
968
|
+
/* @__PURE__ */ k(
|
|
969
|
+
"button",
|
|
970
|
+
{
|
|
971
|
+
type: "button",
|
|
972
|
+
className: "be-version-history-close",
|
|
973
|
+
onClick: _,
|
|
974
|
+
"aria-label": "Close",
|
|
975
|
+
children: /* @__PURE__ */ k(Et, { size: 14 })
|
|
976
|
+
}
|
|
977
|
+
)
|
|
978
|
+
] }),
|
|
979
|
+
y ? /* @__PURE__ */ b("div", { className: "be-version-history-preview", children: [
|
|
980
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-preview-meta", children: [
|
|
981
|
+
/* @__PURE__ */ k(j, { authorId: y.authorId, size: 24 }),
|
|
982
|
+
/* @__PURE__ */ b("div", { children: [
|
|
983
|
+
/* @__PURE__ */ k("div", { className: "be-version-history-preview-author", children: y.authorId ?? "Unknown" }),
|
|
984
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-preview-time", children: [
|
|
985
|
+
new Date(y.timestamp).toLocaleString(),
|
|
986
|
+
" · ",
|
|
987
|
+
y.summary
|
|
988
|
+
] })
|
|
989
|
+
] })
|
|
990
|
+
] }),
|
|
991
|
+
/* @__PURE__ */ b(
|
|
992
|
+
"div",
|
|
993
|
+
{
|
|
994
|
+
className: "be-version-history-view-toggle",
|
|
995
|
+
role: "tablist",
|
|
996
|
+
"aria-label": "View mode",
|
|
997
|
+
children: [
|
|
998
|
+
/* @__PURE__ */ k(
|
|
999
|
+
"button",
|
|
1000
|
+
{
|
|
1001
|
+
type: "button",
|
|
1002
|
+
role: "tab",
|
|
1003
|
+
"aria-selected": f === "changes",
|
|
1004
|
+
className: f === "changes" ? "is-active" : "",
|
|
1005
|
+
onClick: () => T("changes"),
|
|
1006
|
+
children: "Changes"
|
|
1007
|
+
}
|
|
1008
|
+
),
|
|
1009
|
+
/* @__PURE__ */ k(
|
|
1010
|
+
"button",
|
|
1011
|
+
{
|
|
1012
|
+
type: "button",
|
|
1013
|
+
role: "tab",
|
|
1014
|
+
"aria-selected": f === "preview",
|
|
1015
|
+
className: f === "preview" ? "is-active" : "",
|
|
1016
|
+
onClick: () => T("preview"),
|
|
1017
|
+
children: "Preview"
|
|
1018
|
+
}
|
|
1019
|
+
)
|
|
1020
|
+
]
|
|
1021
|
+
}
|
|
1022
|
+
),
|
|
1023
|
+
f === "changes" && /* @__PURE__ */ b(D, { children: [
|
|
1024
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-diff-legend", children: [
|
|
1025
|
+
/* @__PURE__ */ k("span", { className: "be-version-diff-added", children: "Added" }),
|
|
1026
|
+
/* @__PURE__ */ k("span", { className: "be-version-diff-removed", children: "Removed" }),
|
|
1027
|
+
!C && /* @__PURE__ */ k("span", { className: "be-version-history-diff-legend-note", children: "First saved version — everything shown is new." })
|
|
1028
|
+
] }),
|
|
1029
|
+
/* @__PURE__ */ k(
|
|
1030
|
+
"div",
|
|
1031
|
+
{
|
|
1032
|
+
className: "be-version-history-preview-body be-version-history-diff-body",
|
|
1033
|
+
dangerouslySetInnerHTML: { __html: G }
|
|
1034
|
+
}
|
|
1035
|
+
)
|
|
1036
|
+
] }),
|
|
1037
|
+
f === "preview" && // Static HTML from a throwaway store (see previewHtml above) -- inherently read-only, not a second live editor.
|
|
1038
|
+
/* @__PURE__ */ k(
|
|
1039
|
+
"div",
|
|
1040
|
+
{
|
|
1041
|
+
className: "be-version-history-preview-body",
|
|
1042
|
+
dangerouslySetInnerHTML: { __html: N }
|
|
1043
|
+
}
|
|
1044
|
+
),
|
|
1045
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-preview-actions", children: [
|
|
1046
|
+
/* @__PURE__ */ k(
|
|
1047
|
+
"button",
|
|
1048
|
+
{
|
|
1049
|
+
type: "button",
|
|
1050
|
+
onClick: () => {
|
|
1051
|
+
u(null), T("changes");
|
|
1052
|
+
},
|
|
1053
|
+
children: "Back"
|
|
1054
|
+
}
|
|
1055
|
+
),
|
|
1056
|
+
/* @__PURE__ */ b(
|
|
1057
|
+
"button",
|
|
1058
|
+
{
|
|
1059
|
+
type: "button",
|
|
1060
|
+
className: "be-version-history-restore",
|
|
1061
|
+
onClick: Q,
|
|
1062
|
+
children: [
|
|
1063
|
+
/* @__PURE__ */ k(St, { size: 13 }),
|
|
1064
|
+
"Restore this version"
|
|
1065
|
+
]
|
|
1066
|
+
}
|
|
1067
|
+
)
|
|
1068
|
+
] })
|
|
1069
|
+
] }) : /* @__PURE__ */ b("div", { className: "be-version-history-list", children: [
|
|
1070
|
+
!o && /* @__PURE__ */ k("p", { className: "be-version-history-empty", children: "Loading…" }),
|
|
1071
|
+
o && r.length === 0 && /* @__PURE__ */ k("p", { className: "be-version-history-empty", children: "No versions yet — one is saved automatically after you make some edits and pause." }),
|
|
1072
|
+
x.map((p) => /* @__PURE__ */ b("div", { className: "be-version-history-group", children: [
|
|
1073
|
+
/* @__PURE__ */ k("div", { className: "be-version-history-group-label", children: p.label }),
|
|
1074
|
+
p.versions.map((I) => /* @__PURE__ */ b(
|
|
1075
|
+
"button",
|
|
1076
|
+
{
|
|
1077
|
+
type: "button",
|
|
1078
|
+
className: "be-version-history-item",
|
|
1079
|
+
onClick: () => u(I.id),
|
|
1080
|
+
children: [
|
|
1081
|
+
/* @__PURE__ */ k(j, { authorId: I.authorId, size: 24 }),
|
|
1082
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-item-body", children: [
|
|
1083
|
+
/* @__PURE__ */ k("div", { className: "be-version-history-item-author", children: I.authorId ?? "Unknown" }),
|
|
1084
|
+
/* @__PURE__ */ b("div", { className: "be-version-history-item-meta", children: [
|
|
1085
|
+
Mt(I.timestamp),
|
|
1086
|
+
" · ",
|
|
1087
|
+
I.summary
|
|
1088
|
+
] })
|
|
1089
|
+
] })
|
|
1090
|
+
]
|
|
1091
|
+
},
|
|
1092
|
+
I.id
|
|
1093
|
+
))
|
|
1094
|
+
] }, p.label))
|
|
1095
|
+
] })
|
|
1096
|
+
]
|
|
1097
|
+
}
|
|
1098
|
+
)
|
|
1099
|
+
] }),
|
|
1100
|
+
document.body
|
|
1101
|
+
)
|
|
1102
|
+
] });
|
|
1103
|
+
}
|
|
1104
|
+
export {
|
|
1105
|
+
F as E,
|
|
1106
|
+
de as V,
|
|
1107
|
+
X as a,
|
|
1108
|
+
ce as b,
|
|
1109
|
+
$t as c,
|
|
1110
|
+
Wt as d,
|
|
1111
|
+
Vt as e,
|
|
1112
|
+
re as f,
|
|
1113
|
+
oe as g,
|
|
1114
|
+
Bt as h,
|
|
1115
|
+
ne as i,
|
|
1116
|
+
Lt as j,
|
|
1117
|
+
ie as r,
|
|
1118
|
+
Ut as u
|
|
1119
|
+
};
|
|
1120
|
+
//# sourceMappingURL=VersionHistory-Bg6fufP5.js.map
|