superdoc 2.0.0-next.28 → 2.0.0-next.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{PdfViewer-XZg0nr8y.es.js → PdfViewer-CNOtyS-I.es.js} +1 -1
- package/dist/chunks/{PdfViewer-BtuYf2yM.cjs → PdfViewer-CoFiGAhm.cjs} +1 -1
- package/dist/chunks/{create-super-doc-ui-BlnUCtrx.es.js → create-super-doc-ui-BKVf2uCM.es.js} +489 -108
- package/dist/chunks/{create-super-doc-ui-CZBUMdjl.cjs → create-super-doc-ui-BXehf3i3.cjs} +489 -108
- package/dist/collaboration-upgrade-engine.cjs +1 -1
- package/dist/collaboration-upgrade-engine.es.js +1 -1
- package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +18 -1
- package/dist/public/ui-react.cjs +1 -1
- package/dist/public/ui-react.es.js +1 -1
- package/dist/public/ui.cjs +1 -1
- package/dist/public/ui.es.js +1 -1
- package/dist/style.css +113 -88
- package/dist/style.layered.css +113 -88
- package/dist/superdoc/src/components/CommentsLayer/floating-comment-positioning.d.ts +1 -2
- package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +4 -1
- package/dist/superdoc/src/helpers/v2-review-mutation-reconciler.d.ts +6 -5
- package/dist/superdoc/src/stores/comments-store.d.ts +8 -2
- package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +14 -0
- package/dist/superdoc/src/stores/superdoc-store.d.ts +24 -6
- package/dist/superdoc.cjs +422 -134
- package/dist/superdoc.es.js +422 -134
- package/dist-cdn/style.layered.css +1 -1
- package/dist-cdn/superdoc.min.css +1 -1
- package/dist-cdn/superdoc.min.js +37 -37
- package/package.json +2 -2
|
@@ -21,7 +21,8 @@ function getV2TrackedChangeMutationImpact(event) {
|
|
|
21
21
|
if (upsertIds.size === 0 && removedIds.size === 0) return null;
|
|
22
22
|
return {
|
|
23
23
|
upsertIds,
|
|
24
|
-
removedIds
|
|
24
|
+
removedIds,
|
|
25
|
+
reconcileMode: event.origin === "history" ? "authoritative" : "targeted"
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
var normalizeIds = (values) => new Set(Array.from(values ?? []).filter((value) => typeof value === "string" && value.length > 0));
|
|
@@ -31,7 +32,7 @@ var itemId = (item) => {
|
|
|
31
32
|
if (typeof item?.address?.entityId === "string" && item.address.entityId.length > 0) return item.address.entityId;
|
|
32
33
|
return null;
|
|
33
34
|
};
|
|
34
|
-
var contextMatches = (left, right) => left?.adapter === right?.adapter && left?.documentId === right?.documentId && left?.editor === right?.editor;
|
|
35
|
+
var contextMatches = (left, right) => left?.adapter === right?.adapter && left?.documentId === right?.documentId && left?.editor === right?.editor && left?.reconcileToken === right?.reconcileToken;
|
|
35
36
|
function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onReconciled }) {
|
|
36
37
|
const pendingUpserts = /* @__PURE__ */ new Map();
|
|
37
38
|
let sequence = 0;
|
|
@@ -48,43 +49,78 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
|
|
|
48
49
|
}
|
|
49
50
|
return clearedIds;
|
|
50
51
|
};
|
|
51
|
-
const readySnapshot = () => new Map([...pendingUpserts].filter(([, entry]) => entry.ready === true));
|
|
52
|
+
const readySnapshot = (reconcileMode) => new Map([...pendingUpserts].filter(([, entry]) => entry.ready === true && entry.reconcileMode === reconcileMode));
|
|
52
53
|
const markRenderGatedReady = () => {
|
|
53
|
-
|
|
54
|
+
let changed = false;
|
|
55
|
+
for (const entry of pendingUpserts.values()) {
|
|
56
|
+
if (entry.gate !== "render" || entry.ready) continue;
|
|
57
|
+
entry.ready = true;
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
60
|
+
return changed;
|
|
61
|
+
};
|
|
62
|
+
const markEntriesReadyThrough = (maxVersion) => {
|
|
63
|
+
let changed = false;
|
|
64
|
+
for (const entry of pendingUpserts.values()) {
|
|
65
|
+
if (entry.version > maxVersion || entry.ready) continue;
|
|
66
|
+
entry.ready = true;
|
|
67
|
+
changed = true;
|
|
68
|
+
}
|
|
69
|
+
return changed;
|
|
54
70
|
};
|
|
55
|
-
const notifyReconciled = async (context, ids, result) => {
|
|
56
|
-
if (ids.size === 0 || typeof onReconciled !== "function") return;
|
|
71
|
+
const notifyReconciled = async (context, ids, result, allowEmpty = false) => {
|
|
72
|
+
if (!allowEmpty && ids.size === 0 || typeof onReconciled !== "function") return;
|
|
57
73
|
try {
|
|
58
74
|
await onReconciled(context, ids, result);
|
|
59
75
|
} catch {}
|
|
60
76
|
};
|
|
61
77
|
const idsFromResult = (result) => normalizeIds(result?.resolvedIds ?? (result?.items ?? []).map(itemId).filter(Boolean));
|
|
78
|
+
const reconcileTargetedSnapshot = async (context, snapshot, capturedGeneration) => {
|
|
79
|
+
const result = await reconcile(context, {
|
|
80
|
+
upsertIds: new Set(snapshot.keys()),
|
|
81
|
+
removedIds: /* @__PURE__ */ new Set(),
|
|
82
|
+
reconcileMode: "targeted"
|
|
83
|
+
});
|
|
84
|
+
if (!isCurrent(capturedGeneration, context)) return;
|
|
85
|
+
await notifyReconciled(context, clearResolved(idsFromResult(result), snapshot), result);
|
|
86
|
+
if (new Set([...snapshot].filter(([id, version]) => pendingUpserts.get(id) === version).map(([id]) => id)).size === 0) return;
|
|
87
|
+
const fallback = await hydrate(context, {
|
|
88
|
+
trackedChangesListMode: "interaction-prime",
|
|
89
|
+
refreshReason: "mutation-render-reconcile",
|
|
90
|
+
blocking: false
|
|
91
|
+
});
|
|
92
|
+
if (!isCurrent(capturedGeneration, context)) return;
|
|
93
|
+
await notifyReconciled(context, clearResolved(idsFromResult(fallback), snapshot), fallback);
|
|
94
|
+
};
|
|
95
|
+
const reconcileAuthoritativeSnapshot = async (context, snapshot, capturedGeneration) => {
|
|
96
|
+
const result = await hydrate(context, {
|
|
97
|
+
trackedChangesListMode: "all",
|
|
98
|
+
refreshReason: "mutation-history-render-reconcile",
|
|
99
|
+
blocking: true
|
|
100
|
+
});
|
|
101
|
+
if (!isCurrent(capturedGeneration, context)) return;
|
|
102
|
+
if (![...snapshot].some(([id, entry]) => pendingUpserts.get(id) === entry)) return;
|
|
103
|
+
const transportSucceeded = result?.ok === true;
|
|
104
|
+
const catalogIsAuthoritative = transportSucceeded && result?.complete === true && result?.sourceCoverageComplete === true;
|
|
105
|
+
const returnedIds = transportSucceeded ? idsFromResult(result) : /* @__PURE__ */ new Set();
|
|
106
|
+
const clearedIds = catalogIsAuthoritative ? clearResolved(new Set(snapshot.keys()), snapshot) : /* @__PURE__ */ new Set();
|
|
107
|
+
await notifyReconciled(context, catalogIsAuthoritative ? clearedIds : returnedIds, {
|
|
108
|
+
...result,
|
|
109
|
+
reconcileMode: "authoritative"
|
|
110
|
+
}, true);
|
|
111
|
+
};
|
|
62
112
|
const flush = () => {
|
|
63
|
-
if (flushInFlight)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const snapshot = readySnapshot();
|
|
68
|
-
if (snapshot.size === 0) return null;
|
|
113
|
+
if (flushInFlight) return flushInFlight;
|
|
114
|
+
const targetedSnapshot = readySnapshot("targeted");
|
|
115
|
+
const authoritativeSnapshot = readySnapshot("authoritative");
|
|
116
|
+
if (targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
|
|
69
117
|
const context = getContext();
|
|
70
118
|
if (!context?.adapter) return null;
|
|
71
119
|
flushRequested = false;
|
|
72
120
|
const capturedGeneration = generation;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
removedIds: /* @__PURE__ */ new Set()
|
|
77
|
-
})).then(async (result) => {
|
|
78
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
79
|
-
await notifyReconciled(context, clearResolved(idsFromResult(result), snapshot), result);
|
|
80
|
-
if (new Set([...snapshot].filter(([id, version]) => pendingUpserts.get(id) === version).map(([id]) => id)).size === 0) return;
|
|
81
|
-
const fallback = await hydrate(context, {
|
|
82
|
-
trackedChangesListMode: "interaction-prime",
|
|
83
|
-
refreshReason: "mutation-render-reconcile",
|
|
84
|
-
blocking: false
|
|
85
|
-
});
|
|
86
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
87
|
-
await notifyReconciled(context, clearResolved(idsFromResult(fallback), snapshot), fallback);
|
|
121
|
+
flushInFlight = Promise.resolve().then(async () => {
|
|
122
|
+
if (authoritativeSnapshot.size > 0) await reconcileAuthoritativeSnapshot(context, authoritativeSnapshot, capturedGeneration).catch(() => void 0);
|
|
123
|
+
if (targetedSnapshot.size > 0) await reconcileTargetedSnapshot(context, targetedSnapshot, capturedGeneration).catch(() => void 0);
|
|
88
124
|
}).catch(() => {}).finally(() => {
|
|
89
125
|
flushInFlight = null;
|
|
90
126
|
if (flushRequested) flush();
|
|
@@ -93,8 +129,18 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
|
|
|
93
129
|
};
|
|
94
130
|
const enqueueWithGate = (impact, gate) => {
|
|
95
131
|
if (!impact) return;
|
|
132
|
+
const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
|
|
96
133
|
const removedIds = normalizeIds(impact.removedIds);
|
|
97
|
-
for (const id of removedIds)
|
|
134
|
+
for (const id of removedIds) {
|
|
135
|
+
const existing = pendingUpserts.get(id);
|
|
136
|
+
if (existing?.reconcileMode === "authoritative") pendingUpserts.set(id, {
|
|
137
|
+
...existing,
|
|
138
|
+
version: ++sequence,
|
|
139
|
+
gate,
|
|
140
|
+
ready: false
|
|
141
|
+
});
|
|
142
|
+
else pendingUpserts.delete(id);
|
|
143
|
+
}
|
|
98
144
|
const context = getContext();
|
|
99
145
|
if (removedIds.size > 0 && context?.adapter) try {
|
|
100
146
|
Promise.resolve(reconcile(context, {
|
|
@@ -102,16 +148,20 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
|
|
|
102
148
|
removedIds
|
|
103
149
|
})).catch(() => void 0);
|
|
104
150
|
} catch {}
|
|
105
|
-
for (const id of normalizeIds(impact.upsertIds)) if (!removedIds.has(id))
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
for (const id of normalizeIds(impact.upsertIds)) if (!removedIds.has(id)) {
|
|
152
|
+
const existing = pendingUpserts.get(id);
|
|
153
|
+
pendingUpserts.set(id, {
|
|
154
|
+
version: ++sequence,
|
|
155
|
+
gate,
|
|
156
|
+
ready: false,
|
|
157
|
+
reconcileMode: reconcileMode === "authoritative" || existing?.reconcileMode === "authoritative" ? "authoritative" : "targeted"
|
|
158
|
+
});
|
|
159
|
+
}
|
|
110
160
|
};
|
|
111
161
|
const enqueue = (impact) => enqueueWithGate(impact, "render");
|
|
112
|
-
const onRender = () => {
|
|
113
|
-
markRenderGatedReady();
|
|
114
|
-
flushRequested = true;
|
|
162
|
+
const onRender = (supersedesInFlight = false) => {
|
|
163
|
+
const madeReady = markRenderGatedReady();
|
|
164
|
+
if (flushInFlight && (madeReady || supersedesInFlight)) flushRequested = true;
|
|
115
165
|
flush();
|
|
116
166
|
};
|
|
117
167
|
return {
|
|
@@ -125,18 +175,22 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
|
|
|
125
175
|
const context = getContext();
|
|
126
176
|
if (!context?.adapter) return;
|
|
127
177
|
const capturedGeneration = generation;
|
|
128
|
-
const
|
|
178
|
+
const capturedSequence = sequence;
|
|
179
|
+
const impactedIds = new Set([...normalizeIds(impact.upsertIds), ...normalizeIds(impact.removedIds)]);
|
|
180
|
+
const capturedEntries = new Map([...impactedIds].flatMap((id) => {
|
|
129
181
|
const entry = pendingUpserts.get(id);
|
|
130
182
|
return entry ? [[id, entry]] : [];
|
|
131
183
|
}));
|
|
132
184
|
Promise.resolve().then(() => waitForPaint()).then(() => {
|
|
133
185
|
if (!isCurrent(capturedGeneration, context)) return;
|
|
134
|
-
markRenderGatedReady();
|
|
186
|
+
let madeReady = markRenderGatedReady();
|
|
187
|
+
if (markEntriesReadyThrough(capturedSequence)) madeReady = true;
|
|
135
188
|
for (const [id, entry] of capturedEntries) {
|
|
136
189
|
if (pendingUpserts.get(id) !== entry) continue;
|
|
190
|
+
if (!entry.ready) madeReady = true;
|
|
137
191
|
entry.ready = true;
|
|
138
192
|
}
|
|
139
|
-
flushRequested = true;
|
|
193
|
+
if (flushInFlight && (madeReady || capturedSequence > 0)) flushRequested = true;
|
|
140
194
|
flush();
|
|
141
195
|
}).catch(() => {
|
|
142
196
|
if (!isCurrent(capturedGeneration, context)) return;
|
|
@@ -2276,7 +2330,13 @@ function buildTableCommandInput(action, context) {
|
|
|
2276
2330
|
function createSuperDocUI(options) {
|
|
2277
2331
|
const superdoc = options.superdoc;
|
|
2278
2332
|
const optimisticInlineValues = /* @__PURE__ */ new Map();
|
|
2333
|
+
const optimisticInlineToggles = /* @__PURE__ */ new Map();
|
|
2334
|
+
const pendingInlineToggleMutations = [];
|
|
2279
2335
|
let lastOptimisticInlineSelectionSignature = null;
|
|
2336
|
+
let optimisticInlineToggleGeneration = 0;
|
|
2337
|
+
let inlineToggleMutationActive = false;
|
|
2338
|
+
let inlineToggleMutationIdle = Promise.resolve();
|
|
2339
|
+
let resolveInlineToggleMutationIdle = null;
|
|
2280
2340
|
let painter = {
|
|
2281
2341
|
mode: "idle",
|
|
2282
2342
|
snapshot: null,
|
|
@@ -2531,6 +2591,16 @@ function createSuperDocUI(options) {
|
|
|
2531
2591
|
return `e${id}`;
|
|
2532
2592
|
};
|
|
2533
2593
|
let documentMutationRevision = 0;
|
|
2594
|
+
let reviewMutationToken = {};
|
|
2595
|
+
let authoritativeTrackChangesPendingToken = null;
|
|
2596
|
+
let authoritativeTrackChangesRetryTimer = null;
|
|
2597
|
+
let authoritativeTrackChangesFailureCount = 0;
|
|
2598
|
+
const clearAuthoritativeTrackChangesRetry = () => {
|
|
2599
|
+
if (authoritativeTrackChangesRetryTimer) clearTimeout(authoritativeTrackChangesRetryTimer);
|
|
2600
|
+
authoritativeTrackChangesRetryTimer = null;
|
|
2601
|
+
authoritativeTrackChangesFailureCount = 0;
|
|
2602
|
+
};
|
|
2603
|
+
let scheduleAuthoritativeTrackChangesRetry = (_reconcileToken) => void 0;
|
|
2534
2604
|
let postDecisionTrackChangesToken = null;
|
|
2535
2605
|
let postDecisionTrackChangesReconcileToken = null;
|
|
2536
2606
|
let postDecisionTrackChangeIds = /* @__PURE__ */ new Set();
|
|
@@ -2576,28 +2646,92 @@ function createSuperDocUI(options) {
|
|
|
2576
2646
|
run();
|
|
2577
2647
|
}, POST_DECISION_TRACKED_CHANGES_RECONCILE_DELAY_MS);
|
|
2578
2648
|
};
|
|
2579
|
-
const
|
|
2649
|
+
const notifyPostDecisionTrackChanges = (receipt) => {
|
|
2650
|
+
const notify = () => {
|
|
2651
|
+
if (disposed) return;
|
|
2652
|
+
for (const listener of [...listeners]) listener(state);
|
|
2653
|
+
};
|
|
2654
|
+
const record = isLooseObject(receipt) ? receipt : null;
|
|
2655
|
+
const readiness = getEditor()?.documentMutationReadiness;
|
|
2656
|
+
const waitForPostPaintTask = () => {
|
|
2657
|
+
const requestFrame = globalThis.requestAnimationFrame;
|
|
2658
|
+
if (typeof requestFrame !== "function") return Promise.resolve();
|
|
2659
|
+
return new Promise((resolve) => {
|
|
2660
|
+
let settled = false;
|
|
2661
|
+
const finish = () => {
|
|
2662
|
+
if (settled) return;
|
|
2663
|
+
settled = true;
|
|
2664
|
+
clearTimeout(fallback);
|
|
2665
|
+
resolve();
|
|
2666
|
+
};
|
|
2667
|
+
const fallback = setTimeout(finish, 250);
|
|
2668
|
+
requestFrame(() => requestFrame(() => setTimeout(finish, 0)));
|
|
2669
|
+
});
|
|
2670
|
+
};
|
|
2671
|
+
if (record?.success === true && typeof record.txId === "string" && record.txId.length > 0 && typeof readiness?.whenPainted === "function") try {
|
|
2672
|
+
Promise.resolve(readiness.whenPainted.call(readiness, record)).then(waitForPostPaintTask, () => void 0).then(notify, notify);
|
|
2673
|
+
return;
|
|
2674
|
+
} catch {}
|
|
2675
|
+
notify();
|
|
2676
|
+
};
|
|
2677
|
+
const publishPostDecisionTrackChanges = (ids, receipt) => {
|
|
2678
|
+
const previous = state.trackChanges;
|
|
2679
|
+
const items = previous.items.filter((item) => {
|
|
2680
|
+
const id = readEntityId(item);
|
|
2681
|
+
return !id || !ids.has(id);
|
|
2682
|
+
});
|
|
2683
|
+
if (items.length === previous.items.length) return;
|
|
2684
|
+
if (explicitActiveChange && ids.has(explicitActiveChange.id)) setExplicitActiveChange(null);
|
|
2685
|
+
const authors = /* @__PURE__ */ new Set();
|
|
2686
|
+
for (const item of items) {
|
|
2687
|
+
const change = trackChangesItemPayload(item);
|
|
2688
|
+
const row = item;
|
|
2689
|
+
const author = change.author ?? change.authorEmail ?? row.author ?? row.authorEmail;
|
|
2690
|
+
if (typeof author === "string") authors.add(author);
|
|
2691
|
+
}
|
|
2692
|
+
const removedCount = previous.items.length - items.length;
|
|
2693
|
+
state = {
|
|
2694
|
+
...state,
|
|
2695
|
+
trackChanges: {
|
|
2696
|
+
...previous,
|
|
2697
|
+
items,
|
|
2698
|
+
total: Math.max(items.length, previous.total - removedCount),
|
|
2699
|
+
activeId: previous.activeId && ids.has(previous.activeId) ? null : previous.activeId,
|
|
2700
|
+
authors: [...authors]
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
notifyPostDecisionTrackChanges(receipt);
|
|
2704
|
+
};
|
|
2705
|
+
const markPostDecisionTrackChanges = (ids, receipt) => {
|
|
2580
2706
|
if (ids.size === 0) return;
|
|
2581
|
-
|
|
2582
|
-
|
|
2707
|
+
const token = contentToken();
|
|
2708
|
+
const alreadyRemoved = postDecisionTrackChangesToken === token ? postDecisionTrackChangeIds : /* @__PURE__ */ new Set();
|
|
2709
|
+
const newlyRemoved = new Set([...ids].filter((id) => !alreadyRemoved.has(id)));
|
|
2710
|
+
if (newlyRemoved.size === 0) return;
|
|
2711
|
+
postDecisionTrackChangesToken = token;
|
|
2712
|
+
postDecisionTrackChangeIds = new Set([...alreadyRemoved, ...newlyRemoved]);
|
|
2583
2713
|
applyPostDecisionTrackChangesToCache(postDecisionTrackChangeIds);
|
|
2584
2714
|
schedulePostDecisionTrackChangesReconcile();
|
|
2585
|
-
|
|
2715
|
+
publishPostDecisionTrackChanges(newlyRemoved, receipt);
|
|
2586
2716
|
};
|
|
2587
2717
|
const postDecisionTrackChangeIdsForToken = (token) => postDecisionTrackChangesToken === token && postDecisionTrackChangeIds.size > 0 ? postDecisionTrackChangeIds : null;
|
|
2588
|
-
const mergeTrackedChangeItemsIntoCache = (items) => {
|
|
2718
|
+
const mergeTrackedChangeItemsIntoCache = (items, orderedItems = []) => {
|
|
2589
2719
|
const projected = items.map(projectTrackChangesItem).filter((item) => item != null && readEntityId(item) != null);
|
|
2590
2720
|
if (projected.length === 0) return;
|
|
2721
|
+
const orderedIds = orderedItems.map(projectTrackChangesItem).map((item) => item == null ? null : readEntityId(item)).filter((id) => id != null);
|
|
2591
2722
|
for (const key of ["trackChanges", "trackChanges:all"]) {
|
|
2592
2723
|
const entry = asyncReads.get(key);
|
|
2593
2724
|
const nextValue = entry?.hasSettled && Array.isArray(entry.value) ? [...entry.value] : [];
|
|
2725
|
+
const insertedIds = /* @__PURE__ */ new Set();
|
|
2594
2726
|
let changed = false;
|
|
2595
2727
|
for (const fresh of projected) {
|
|
2596
2728
|
const id = readEntityId(fresh);
|
|
2597
2729
|
if (!id) continue;
|
|
2598
2730
|
const index = nextValue.findIndex((candidate) => readEntityId(candidate) === id);
|
|
2599
|
-
if (index < 0)
|
|
2600
|
-
|
|
2731
|
+
if (index < 0) {
|
|
2732
|
+
nextValue.push(fresh);
|
|
2733
|
+
insertedIds.add(id);
|
|
2734
|
+
} else {
|
|
2601
2735
|
const previous = nextValue[index];
|
|
2602
2736
|
nextValue[index] = isLooseObject(previous) ? {
|
|
2603
2737
|
...previous,
|
|
@@ -2606,6 +2740,24 @@ function createSuperDocUI(options) {
|
|
|
2606
2740
|
}
|
|
2607
2741
|
changed = true;
|
|
2608
2742
|
}
|
|
2743
|
+
if (key === "trackChanges" && insertedIds.size > 0 && orderedIds.length > 0) {
|
|
2744
|
+
const placeableIds = new Set(orderedIds.filter((id) => insertedIds.has(id)));
|
|
2745
|
+
const insertedRows = new Map(nextValue.filter((item) => placeableIds.has(readEntityId(item) ?? "")).map((item) => [readEntityId(item), item]));
|
|
2746
|
+
const placedValue = nextValue.filter((item) => !placeableIds.has(readEntityId(item) ?? ""));
|
|
2747
|
+
for (const [orderIndex, id] of orderedIds.entries()) {
|
|
2748
|
+
if (!placeableIds.has(id)) continue;
|
|
2749
|
+
const item = insertedRows.get(id);
|
|
2750
|
+
if (!item) continue;
|
|
2751
|
+
const followingAnchor = orderedIds.slice(orderIndex + 1).map((candidateId) => placedValue.findIndex((candidate) => readEntityId(candidate) === candidateId)).find((index) => index >= 0);
|
|
2752
|
+
if (followingAnchor != null) {
|
|
2753
|
+
placedValue.splice(followingAnchor, 0, item);
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
const precedingAnchor = orderedIds.slice(0, orderIndex).reverse().map((candidateId) => placedValue.findIndex((candidate) => readEntityId(candidate) === candidateId)).find((index) => index >= 0);
|
|
2757
|
+
placedValue.splice(precedingAnchor == null ? placedValue.length : precedingAnchor + 1, 0, item);
|
|
2758
|
+
}
|
|
2759
|
+
nextValue.splice(0, nextValue.length, ...placedValue);
|
|
2760
|
+
}
|
|
2609
2761
|
if (!changed) continue;
|
|
2610
2762
|
asyncReads.set(key, {
|
|
2611
2763
|
token: entry?.token ?? null,
|
|
@@ -2615,6 +2767,57 @@ function createSuperDocUI(options) {
|
|
|
2615
2767
|
});
|
|
2616
2768
|
}
|
|
2617
2769
|
};
|
|
2770
|
+
const replaceTrackedChangeItemsInCache = (items) => {
|
|
2771
|
+
const token = contentToken();
|
|
2772
|
+
const suppressedIds = postDecisionTrackChangeIdsForToken(token);
|
|
2773
|
+
const projected = items.map(projectTrackChangesItem).filter((item) => {
|
|
2774
|
+
if (item == null) return false;
|
|
2775
|
+
const id = readEntityId(item);
|
|
2776
|
+
return id != null && !suppressedIds?.has(id);
|
|
2777
|
+
});
|
|
2778
|
+
for (const key of ["trackChanges", "trackChanges:all"]) asyncReads.set(key, {
|
|
2779
|
+
token,
|
|
2780
|
+
value: [...projected],
|
|
2781
|
+
hasSettled: true,
|
|
2782
|
+
inflightToken: null
|
|
2783
|
+
});
|
|
2784
|
+
};
|
|
2785
|
+
const hydrateTrackedChanges = async (context, options$1) => {
|
|
2786
|
+
try {
|
|
2787
|
+
const listTrackedChanges = context.v2TrackedChanges?.listTrackedChanges;
|
|
2788
|
+
if (typeof listTrackedChanges === "function") {
|
|
2789
|
+
const result$1 = await Promise.resolve(listTrackedChanges.call(context.v2TrackedChanges, {
|
|
2790
|
+
mode: options$1.trackedChangesListMode,
|
|
2791
|
+
refreshReason: options$1.refreshReason,
|
|
2792
|
+
blocking: options$1.blocking,
|
|
2793
|
+
...options$1.targetIds?.length ? { targetIds: options$1.targetIds } : {}
|
|
2794
|
+
}));
|
|
2795
|
+
const ok = options$1.trackedChangesListMode === "all" && options$1.refreshReason === "mutation-history-render-reconcile" ? result$1?.ok === true : result$1?.ok !== false;
|
|
2796
|
+
return {
|
|
2797
|
+
ok,
|
|
2798
|
+
items: ok && Array.isArray(result$1?.items) ? result$1.items : [],
|
|
2799
|
+
...typeof result$1?.complete === "boolean" ? { complete: result$1.complete } : {},
|
|
2800
|
+
...typeof result$1?.sourceCoverageComplete === "boolean" ? { sourceCoverageComplete: result$1.sourceCoverageComplete } : {}
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
const list = context.docTrackChanges?.list;
|
|
2804
|
+
if (typeof list !== "function") return {
|
|
2805
|
+
ok: false,
|
|
2806
|
+
items: []
|
|
2807
|
+
};
|
|
2808
|
+
const result = await Promise.resolve(list.call(context.docTrackChanges));
|
|
2809
|
+
return {
|
|
2810
|
+
ok: true,
|
|
2811
|
+
items: Array.isArray(result?.items) ? result.items : []
|
|
2812
|
+
};
|
|
2813
|
+
} catch (error) {
|
|
2814
|
+
return {
|
|
2815
|
+
ok: false,
|
|
2816
|
+
items: [],
|
|
2817
|
+
failures: [error]
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2618
2821
|
const reviewMutationReconciler = createV2ReviewMutationReconciler({
|
|
2619
2822
|
getContext: () => {
|
|
2620
2823
|
const editor = getEditor();
|
|
@@ -2628,6 +2831,7 @@ function createSuperDocUI(options) {
|
|
|
2628
2831
|
editor,
|
|
2629
2832
|
adapter,
|
|
2630
2833
|
documentId: rawDocumentId == null ? null : String(rawDocumentId),
|
|
2834
|
+
reconcileToken: reviewMutationToken,
|
|
2631
2835
|
v2TrackedChanges,
|
|
2632
2836
|
docTrackChanges
|
|
2633
2837
|
};
|
|
@@ -2641,6 +2845,9 @@ function createSuperDocUI(options) {
|
|
|
2641
2845
|
resolvedIds: [],
|
|
2642
2846
|
unresolvedIds: []
|
|
2643
2847
|
};
|
|
2848
|
+
const settledTrackChanges = asyncReads.get("trackChanges");
|
|
2849
|
+
const cachedIds = new Set(settledTrackChanges?.hasSettled && Array.isArray(settledTrackChanges.value) ? settledTrackChanges.value.map(readEntityId).filter((id) => id != null) : []);
|
|
2850
|
+
const idsNeedingPlacement = new Set(requestedIds.filter((id) => !cachedIds.has(id)));
|
|
2644
2851
|
const getTrackedChange = context.v2TrackedChanges?.getTrackedChange;
|
|
2645
2852
|
const getRawTrackedChange = context.docTrackChanges?.get;
|
|
2646
2853
|
if (typeof getTrackedChange !== "function" && typeof getRawTrackedChange !== "function") return {
|
|
@@ -2689,55 +2896,59 @@ function createSuperDocUI(options) {
|
|
|
2689
2896
|
resolvedIds.push(read.id);
|
|
2690
2897
|
} else unresolvedIds.push(read.id);
|
|
2691
2898
|
}
|
|
2899
|
+
let orderedItems;
|
|
2900
|
+
const placementIds = resolvedIds.filter((id) => idsNeedingPlacement.has(id));
|
|
2901
|
+
if (placementIds.length > 0) {
|
|
2902
|
+
const orderResult = await hydrateTrackedChanges(context, {
|
|
2903
|
+
trackedChangesListMode: "interaction-prime",
|
|
2904
|
+
refreshReason: "mutation-render-reconcile",
|
|
2905
|
+
blocking: false,
|
|
2906
|
+
targetIds: placementIds
|
|
2907
|
+
});
|
|
2908
|
+
if (orderResult.ok) orderedItems = orderResult.items;
|
|
2909
|
+
}
|
|
2692
2910
|
return {
|
|
2693
2911
|
ok: unresolvedIds.length === 0,
|
|
2694
2912
|
items,
|
|
2913
|
+
orderedItems,
|
|
2695
2914
|
resolvedIds,
|
|
2696
2915
|
unresolvedIds,
|
|
2697
2916
|
failures: reads.filter((read) => !read.ok).map((read) => read.result)
|
|
2698
2917
|
};
|
|
2699
2918
|
},
|
|
2700
|
-
hydrate:
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
if (
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
const list = context.docTrackChanges?.list;
|
|
2715
|
-
if (typeof list !== "function") return {
|
|
2716
|
-
ok: false,
|
|
2717
|
-
items: []
|
|
2718
|
-
};
|
|
2719
|
-
const result = await Promise.resolve(list.call(context.docTrackChanges));
|
|
2720
|
-
return {
|
|
2721
|
-
ok: true,
|
|
2722
|
-
items: Array.isArray(result?.items) ? result.items : []
|
|
2723
|
-
};
|
|
2724
|
-
} catch (error) {
|
|
2725
|
-
return {
|
|
2726
|
-
ok: false,
|
|
2727
|
-
items: [],
|
|
2728
|
-
failures: [error]
|
|
2729
|
-
};
|
|
2919
|
+
hydrate: hydrateTrackedChanges,
|
|
2920
|
+
onReconciled: (context, ids, result) => {
|
|
2921
|
+
if (result?.reconcileMode === "authoritative") {
|
|
2922
|
+
if (context.reconcileToken !== reviewMutationToken) return;
|
|
2923
|
+
if (result.ok === true && result.complete === true && result.sourceCoverageComplete === true) {
|
|
2924
|
+
clearAuthoritativeTrackChangesRetry();
|
|
2925
|
+
replaceTrackedChangeItemsInCache(result.items ?? []);
|
|
2926
|
+
authoritativeTrackChangesPendingToken = null;
|
|
2927
|
+
} else if (result.ok === true) {
|
|
2928
|
+
clearAuthoritativeTrackChangesRetry();
|
|
2929
|
+
mergeTrackedChangeItemsIntoCache(result.items ?? []);
|
|
2930
|
+
} else scheduleAuthoritativeTrackChangesRetry(context.reconcileToken);
|
|
2931
|
+
recompute();
|
|
2932
|
+
return;
|
|
2730
2933
|
}
|
|
2731
|
-
},
|
|
2732
|
-
onReconciled: (_context, ids, result) => {
|
|
2733
2934
|
mergeTrackedChangeItemsIntoCache((result?.items ?? []).filter((item) => {
|
|
2734
2935
|
const projected = projectTrackChangesItem(item);
|
|
2735
2936
|
const id = projected == null ? null : readEntityId(projected);
|
|
2736
2937
|
return id != null && ids.has(id);
|
|
2737
|
-
}));
|
|
2938
|
+
}), result?.orderedItems ?? result?.items ?? []);
|
|
2738
2939
|
recompute();
|
|
2739
2940
|
}
|
|
2740
2941
|
});
|
|
2942
|
+
scheduleAuthoritativeTrackChangesRetry = (reconcileToken) => {
|
|
2943
|
+
if (disposed || authoritativeTrackChangesRetryTimer) return;
|
|
2944
|
+
const delayMs = Math.min(ASYNC_READ_FAILURE_RETRY_BASE_MS * 2 ** authoritativeTrackChangesFailureCount, ASYNC_READ_FAILURE_RETRY_MAX_MS);
|
|
2945
|
+
authoritativeTrackChangesFailureCount += 1;
|
|
2946
|
+
authoritativeTrackChangesRetryTimer = setTimeout(() => {
|
|
2947
|
+
authoritativeTrackChangesRetryTimer = null;
|
|
2948
|
+
if (disposed || authoritativeTrackChangesPendingToken === null || reconcileToken !== reviewMutationToken) return;
|
|
2949
|
+
reviewMutationReconciler.onRender(true);
|
|
2950
|
+
}, delayMs);
|
|
2951
|
+
};
|
|
2741
2952
|
const selectionReadToken = () => `${contentToken()}|m${readDocumentMode()}|s${selectionEpoch}`;
|
|
2742
2953
|
const selectionSignature = (selection$1) => JSON.stringify({
|
|
2743
2954
|
t: selection$1.target ?? null,
|
|
@@ -3087,8 +3298,11 @@ function createSuperDocUI(options) {
|
|
|
3087
3298
|
const editor = getEditor();
|
|
3088
3299
|
if (editor === lastCoordinatorEditor) return;
|
|
3089
3300
|
reviewMutationReconciler.reset();
|
|
3301
|
+
reviewMutationToken = {};
|
|
3090
3302
|
lastCoordinatorEditor = editor;
|
|
3091
3303
|
asyncReads.clear();
|
|
3304
|
+
authoritativeTrackChangesPendingToken = null;
|
|
3305
|
+
clearAuthoritativeTrackChangesRetry();
|
|
3092
3306
|
if (asyncReadFailureRetryTimer) clearTimeout(asyncReadFailureRetryTimer);
|
|
3093
3307
|
asyncReadFailureRetryTimer = null;
|
|
3094
3308
|
asyncReadFailureRetryAtMs = 0;
|
|
@@ -3107,7 +3321,9 @@ function createSuperDocUI(options) {
|
|
|
3107
3321
|
clearPostDecisionTrackChanges();
|
|
3108
3322
|
};
|
|
3109
3323
|
const invalidateDocumentContent = (options$1) => {
|
|
3324
|
+
const carryAuthoritativeTrackChangesHold = authoritativeTrackChangesPendingToken !== null;
|
|
3110
3325
|
documentMutationRevision += 1;
|
|
3326
|
+
if (carryAuthoritativeTrackChangesHold) authoritativeTrackChangesPendingToken = contentToken();
|
|
3111
3327
|
if (!options$1?.preservePostDecisionTrackChanges) clearPostDecisionTrackChanges();
|
|
3112
3328
|
};
|
|
3113
3329
|
const insertText = (text) => {
|
|
@@ -3298,7 +3514,12 @@ function createSuperDocUI(options) {
|
|
|
3298
3514
|
};
|
|
3299
3515
|
const readAllStoryTrackChanges = () => {
|
|
3300
3516
|
const tcApi = getDoc()?.trackChanges;
|
|
3301
|
-
const
|
|
3517
|
+
const token = contentToken();
|
|
3518
|
+
const heldEntry = asyncReads.get("trackChanges:all");
|
|
3519
|
+
const { value, status } = authoritativeTrackChangesPendingToken === token ? {
|
|
3520
|
+
value: heldEntry?.hasSettled ? heldEntry.value : null,
|
|
3521
|
+
status: heldEntry?.hasSettled ? "stale" : "pending"
|
|
3522
|
+
} : readAsync("trackChanges:all", token, () => tcApi?.list ? tcApi.list({ in: "all" }) : void 0, (raw) => {
|
|
3302
3523
|
return (raw && Array.isArray(raw.items) ? raw.items : []).map(projectTrackChangesItem).filter((item) => item != null);
|
|
3303
3524
|
});
|
|
3304
3525
|
return status === "ready" ? value : null;
|
|
@@ -3309,7 +3530,11 @@ function createSuperDocUI(options) {
|
|
|
3309
3530
|
const listTrackedChanges = typeof v2TrackedChanges?.listTrackedChanges === "function" ? v2TrackedChanges.listTrackedChanges : null;
|
|
3310
3531
|
const token = contentToken();
|
|
3311
3532
|
const postDecisionIds = postDecisionTrackChangeIdsForToken(token);
|
|
3312
|
-
const
|
|
3533
|
+
const heldEntry = asyncReads.get("trackChanges");
|
|
3534
|
+
const { value, status: listStatus } = authoritativeTrackChangesPendingToken === token ? {
|
|
3535
|
+
value: heldEntry?.hasSettled ? heldEntry.value : null,
|
|
3536
|
+
status: heldEntry?.hasSettled ? "stale" : "pending"
|
|
3537
|
+
} : readAsync("trackChanges", token, () => {
|
|
3313
3538
|
if (listTrackedChanges) return postDecisionTrackChangesToken === token ? listTrackedChanges.call(v2TrackedChanges, POST_DECISION_TRACKED_CHANGES_LIST_OPTIONS) : listTrackedChanges.call(v2TrackedChanges);
|
|
3314
3539
|
return tcApi?.list ? tcApi.list() : void 0;
|
|
3315
3540
|
}, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
|
|
@@ -3968,6 +4193,9 @@ function createSuperDocUI(options) {
|
|
|
3968
4193
|
if (descriptor.list?.mode === "toggle-seed" && descriptor.list.seed) return readListSeed(doc, selection$1) === descriptor.list.seed;
|
|
3969
4194
|
const pending = pendingInlineActive(descriptor);
|
|
3970
4195
|
if (pending !== null) return pending;
|
|
4196
|
+
const optimistic = optimisticInlineToggles.get(descriptor.id);
|
|
4197
|
+
const selectionSignature$1 = selectionInlineValueSignature(selection$1);
|
|
4198
|
+
if (selectionSignature$1 && optimistic?.selectionSignature === selectionSignature$1) return optimistic.active;
|
|
3971
4199
|
return commandIsActive(descriptor, selection$1);
|
|
3972
4200
|
};
|
|
3973
4201
|
const routedCommandValue = (descriptor, doc, selection$1, projectedInlineValues) => {
|
|
@@ -4594,6 +4822,9 @@ function createSuperDocUI(options) {
|
|
|
4594
4822
|
const selection$1 = computeSelection();
|
|
4595
4823
|
if (selection$1.status === "ready") pruneSelectionScopedAsyncReads(selection$1);
|
|
4596
4824
|
reconcilePendingInlineFormat(selection$1);
|
|
4825
|
+
const selectionSignature$1 = selectionInlineValueSignature(selection$1);
|
|
4826
|
+
for (const [commandId, optimistic] of optimisticInlineToggles) if (!selectionSignature$1 || optimistic.selectionSignature !== selectionSignature$1) optimisticInlineToggles.delete(commandId);
|
|
4827
|
+
else if (optimistic.settled && selection$1.status === "ready") optimisticInlineToggles.delete(commandId);
|
|
4597
4828
|
return {
|
|
4598
4829
|
ready: getEditor() != null,
|
|
4599
4830
|
documentMode,
|
|
@@ -4614,7 +4845,10 @@ function createSuperDocUI(options) {
|
|
|
4614
4845
|
};
|
|
4615
4846
|
const syncOptimisticInlineSelection = (selection$1) => {
|
|
4616
4847
|
const nextSignature = selectionInlineValueSignature(selection$1);
|
|
4617
|
-
if (nextSignature && lastOptimisticInlineSelectionSignature && nextSignature !== lastOptimisticInlineSelectionSignature)
|
|
4848
|
+
if (nextSignature && lastOptimisticInlineSelectionSignature && nextSignature !== lastOptimisticInlineSelectionSignature) {
|
|
4849
|
+
optimisticInlineValues.clear();
|
|
4850
|
+
optimisticInlineToggles.clear();
|
|
4851
|
+
}
|
|
4618
4852
|
if (nextSignature) lastOptimisticInlineSelectionSignature = nextSignature;
|
|
4619
4853
|
};
|
|
4620
4854
|
let currentHostSelectionSource = null;
|
|
@@ -4692,24 +4926,48 @@ function createSuperDocUI(options) {
|
|
|
4692
4926
|
try {
|
|
4693
4927
|
const off = next.subscribe((event) => {
|
|
4694
4928
|
const type = event?.type;
|
|
4695
|
-
if (type === "
|
|
4929
|
+
if (type === "source:complete" || type === "source:signals-complete") {
|
|
4930
|
+
if (authoritativeTrackChangesPendingToken !== null) {
|
|
4931
|
+
clearAuthoritativeTrackChangesRetry();
|
|
4932
|
+
reviewMutationReconciler.onRender(true);
|
|
4933
|
+
}
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
if (type === "mutation:committed") {
|
|
4937
|
+
lastEditableMutationAtMs = Date.now();
|
|
4938
|
+
clearAuthoritativeTrackChangesRetry();
|
|
4939
|
+
reviewMutationToken = {};
|
|
4940
|
+
}
|
|
4696
4941
|
if (type === "mutation:committed" || type === "save:completed") {
|
|
4697
4942
|
const impact = type === "mutation:committed" ? getV2TrackedChangeMutationImpact(event) : null;
|
|
4698
|
-
|
|
4699
|
-
if (impact?.
|
|
4943
|
+
const supersedesPendingReviewReconcile = type === "mutation:committed" && reviewMutationReconciler.getPendingIds().size > 0;
|
|
4944
|
+
if (impact?.removedIds.size) markPostDecisionTrackChanges(new Set(impact.removedIds), event.receipt);
|
|
4945
|
+
const authoritativeHistoryImpact = impact?.reconcileMode === "authoritative" && impact.upsertIds.size > 0;
|
|
4946
|
+
if (authoritativeHistoryImpact) {
|
|
4947
|
+
invalidateDocumentContent();
|
|
4948
|
+
clearAuthoritativeTrackChangesRetry();
|
|
4949
|
+
authoritativeTrackChangesPendingToken = contentToken();
|
|
4950
|
+
recompute();
|
|
4951
|
+
}
|
|
4952
|
+
if (impact?.upsertIds.size || supersedesPendingReviewReconcile) {
|
|
4700
4953
|
const receipt = event.origin === "history" ? null : event.receipt;
|
|
4701
4954
|
const readiness = getEditor()?.documentMutationReadiness;
|
|
4702
|
-
const
|
|
4955
|
+
const canWaitForReceiptPaint = typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
|
|
4956
|
+
const historyRenderEpoch = event.origin === "history" && typeof readiness?.getRenderEpoch === "function" ? safeCall(() => readiness.getRenderEpoch.call(readiness), null) : null;
|
|
4957
|
+
const canWaitForHistoryPaint = event.origin === "history" && typeof readiness?.whenPainted === "function";
|
|
4703
4958
|
const upsertImpact = {
|
|
4704
|
-
upsertIds: impact
|
|
4705
|
-
removedIds: /* @__PURE__ */ new Set()
|
|
4959
|
+
upsertIds: impact?.upsertIds ?? /* @__PURE__ */ new Set(),
|
|
4960
|
+
removedIds: /* @__PURE__ */ new Set(),
|
|
4961
|
+
reconcileMode: impact?.reconcileMode === "authoritative" ? "authoritative" : "targeted"
|
|
4706
4962
|
};
|
|
4707
|
-
if (
|
|
4963
|
+
if (canWaitForReceiptPaint) reviewMutationReconciler.enqueueAfterPaint(upsertImpact, () => readiness.whenPainted.call(readiness, receipt));
|
|
4964
|
+
else if (canWaitForHistoryPaint) reviewMutationReconciler.enqueueAfterPaint(upsertImpact, () => readiness.whenPainted.call(readiness, typeof historyRenderEpoch === "number" ? { afterEpoch: historyRenderEpoch } : void 0));
|
|
4708
4965
|
else {
|
|
4709
4966
|
reviewMutationReconciler.enqueue(upsertImpact);
|
|
4710
|
-
reviewMutationReconciler.onRender();
|
|
4967
|
+
reviewMutationReconciler.onRender(supersedesPendingReviewReconcile);
|
|
4711
4968
|
}
|
|
4712
4969
|
}
|
|
4970
|
+
if (authoritativeHistoryImpact) return;
|
|
4713
4971
|
if (type === "mutation:committed" && isEditableTextMutationEvent(event)) {
|
|
4714
4972
|
lastTypingMutationAtMs = Date.now();
|
|
4715
4973
|
scheduleTypingDocumentContentInvalidation();
|
|
@@ -4903,6 +5161,7 @@ function createSuperDocUI(options) {
|
|
|
4903
5161
|
}
|
|
4904
5162
|
let lastCommandSettlement = Promise.resolve(false);
|
|
4905
5163
|
let pendingCommandSettlement = null;
|
|
5164
|
+
let lastInlineToggleCommandSettlement = Promise.resolve(false);
|
|
4906
5165
|
const awaitMutationReadiness = async (result) => {
|
|
4907
5166
|
if (!isSuccessfulReceipt(result)) return result;
|
|
4908
5167
|
const readiness = getEditor()?.documentMutationReadiness;
|
|
@@ -4930,6 +5189,69 @@ function createSuperDocUI(options) {
|
|
|
4930
5189
|
settled: awaitMutationReadiness(immediate)
|
|
4931
5190
|
};
|
|
4932
5191
|
};
|
|
5192
|
+
const releaseInlineToggleMutation = () => {
|
|
5193
|
+
const next = pendingInlineToggleMutations.shift();
|
|
5194
|
+
if (!next) {
|
|
5195
|
+
inlineToggleMutationActive = false;
|
|
5196
|
+
resolveInlineToggleMutationIdle?.();
|
|
5197
|
+
resolveInlineToggleMutationIdle = null;
|
|
5198
|
+
return;
|
|
5199
|
+
}
|
|
5200
|
+
try {
|
|
5201
|
+
next.resolve(disposed ? false : next.mutate());
|
|
5202
|
+
} catch {
|
|
5203
|
+
next.resolve(false);
|
|
5204
|
+
}
|
|
5205
|
+
};
|
|
5206
|
+
const releaseInlineToggleMutationOnce = () => {
|
|
5207
|
+
let released = false;
|
|
5208
|
+
return () => {
|
|
5209
|
+
if (released) return;
|
|
5210
|
+
released = true;
|
|
5211
|
+
releaseInlineToggleMutation();
|
|
5212
|
+
};
|
|
5213
|
+
};
|
|
5214
|
+
const scheduleInlineToggleMutation = (key, mutate) => {
|
|
5215
|
+
if (!inlineToggleMutationActive) {
|
|
5216
|
+
inlineToggleMutationActive = true;
|
|
5217
|
+
inlineToggleMutationIdle = new Promise((resolve$1) => {
|
|
5218
|
+
resolveInlineToggleMutationIdle = resolve$1;
|
|
5219
|
+
});
|
|
5220
|
+
try {
|
|
5221
|
+
return {
|
|
5222
|
+
result: mutate(),
|
|
5223
|
+
release: releaseInlineToggleMutationOnce()
|
|
5224
|
+
};
|
|
5225
|
+
} catch (error) {
|
|
5226
|
+
inlineToggleMutationActive = false;
|
|
5227
|
+
resolveInlineToggleMutationIdle?.();
|
|
5228
|
+
resolveInlineToggleMutationIdle = null;
|
|
5229
|
+
throw error;
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5232
|
+
const pending = pendingInlineToggleMutations.find((mutation) => mutation.key === key);
|
|
5233
|
+
if (pending) {
|
|
5234
|
+
pending.mutate = mutate;
|
|
5235
|
+
return {
|
|
5236
|
+
result: pending.result,
|
|
5237
|
+
release: () => void 0
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5240
|
+
let resolve;
|
|
5241
|
+
const result = new Promise((settle) => {
|
|
5242
|
+
resolve = settle;
|
|
5243
|
+
});
|
|
5244
|
+
pendingInlineToggleMutations.push({
|
|
5245
|
+
key,
|
|
5246
|
+
mutate,
|
|
5247
|
+
result,
|
|
5248
|
+
resolve
|
|
5249
|
+
});
|
|
5250
|
+
return {
|
|
5251
|
+
result,
|
|
5252
|
+
release: releaseInlineToggleMutationOnce()
|
|
5253
|
+
};
|
|
5254
|
+
};
|
|
4933
5255
|
const UNSUPPORTED_TRACKED_UI_MUTATION_ROUTES = new Set(["create.tableOfContents"]);
|
|
4934
5256
|
const unsupportedTrackedMutationReceipt = (route) => failedReceipt(`Tracked authoring is not supported for ${route}.`);
|
|
4935
5257
|
const editorMutationOptionsForRoute = (route) => {
|
|
@@ -4973,33 +5295,62 @@ function createSuperDocUI(options) {
|
|
|
4973
5295
|
});
|
|
4974
5296
|
return result;
|
|
4975
5297
|
};
|
|
4976
|
-
const
|
|
5298
|
+
const captureOptimisticInlineToggle = (descriptor, selection$1, input, result) => {
|
|
5299
|
+
if (descriptor.inline?.kind !== "toggle") return null;
|
|
5300
|
+
if (!commandResultSucceeded(commandResultFromOperationResult(result))) return null;
|
|
5301
|
+
if (typeof input.value !== "boolean" && input.value !== null) return null;
|
|
5302
|
+
const selectionSignature$1 = selectionInlineValueSignature(selection$1);
|
|
5303
|
+
if (!selectionSignature$1) return null;
|
|
5304
|
+
const generation = ++optimisticInlineToggleGeneration;
|
|
5305
|
+
optimisticInlineToggles.set(descriptor.id, {
|
|
5306
|
+
selectionSignature: selectionSignature$1,
|
|
5307
|
+
active: input.value === true,
|
|
5308
|
+
generation,
|
|
5309
|
+
settled: false
|
|
5310
|
+
});
|
|
5311
|
+
recompute();
|
|
5312
|
+
return generation;
|
|
5313
|
+
};
|
|
5314
|
+
const settleOptimisticInlineToggle = (descriptor, generation, result) => {
|
|
5315
|
+
if (generation == null) return;
|
|
5316
|
+
const optimistic = optimisticInlineToggles.get(descriptor.id);
|
|
5317
|
+
if (!optimistic || optimistic.generation !== generation) return;
|
|
5318
|
+
if (!commandResultSucceeded(result)) {
|
|
5319
|
+
optimisticInlineToggles.delete(descriptor.id);
|
|
5320
|
+
return;
|
|
5321
|
+
}
|
|
5322
|
+
optimistic.settled = true;
|
|
5323
|
+
};
|
|
5324
|
+
const finalizeCommandSettlement = (promise, onSettled) => promise.then((settled) => {
|
|
5325
|
+
onSettled?.(settled);
|
|
4977
5326
|
invalidateDocumentContent();
|
|
4978
5327
|
recompute();
|
|
4979
5328
|
return settled;
|
|
4980
5329
|
}, () => {
|
|
5330
|
+
onSettled?.(false);
|
|
4981
5331
|
invalidateDocumentContent();
|
|
4982
5332
|
recompute();
|
|
4983
5333
|
return false;
|
|
4984
5334
|
});
|
|
4985
|
-
const settleCommandExecution = (result) => {
|
|
5335
|
+
const settleCommandExecution = (result, onSettled) => {
|
|
4986
5336
|
const pending = pendingCommandSettlement;
|
|
4987
5337
|
pendingCommandSettlement = null;
|
|
4988
5338
|
if (pending) {
|
|
4989
|
-
lastCommandSettlement = finalizeCommandSettlement(pending);
|
|
5339
|
+
lastCommandSettlement = finalizeCommandSettlement(pending, onSettled);
|
|
4990
5340
|
return commandResultFromOperationResult(result);
|
|
4991
5341
|
}
|
|
4992
5342
|
if (isPromiseLike(result)) {
|
|
4993
|
-
lastCommandSettlement = finalizeCommandSettlement(settleOperationResult(result).settled);
|
|
5343
|
+
lastCommandSettlement = finalizeCommandSettlement(settleOperationResult(result).settled, onSettled);
|
|
4994
5344
|
return true;
|
|
4995
5345
|
}
|
|
4996
5346
|
const settled = commandResultFromOperationResult(result);
|
|
4997
5347
|
if (isSuccessfulReceipt(settled)) {
|
|
4998
5348
|
if (typeof (getEditor()?.documentMutationReadiness)?.whenPainted === "function") {
|
|
4999
|
-
lastCommandSettlement = finalizeCommandSettlement(awaitMutationReadiness(settled));
|
|
5349
|
+
lastCommandSettlement = finalizeCommandSettlement(awaitMutationReadiness(settled), onSettled);
|
|
5000
5350
|
return settled;
|
|
5001
5351
|
}
|
|
5002
5352
|
}
|
|
5353
|
+
onSettled?.(settled);
|
|
5003
5354
|
lastCommandSettlement = Promise.resolve(settled);
|
|
5004
5355
|
invalidateDocumentContent();
|
|
5005
5356
|
recompute();
|
|
@@ -5722,12 +6073,31 @@ function createSuperDocUI(options) {
|
|
|
5722
6073
|
if (state.selection.empty === false) return settleCommandExecution(false);
|
|
5723
6074
|
return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
|
|
5724
6075
|
}
|
|
5725
|
-
const active =
|
|
6076
|
+
const active = commandActiveState(descriptor, doc, state.selection);
|
|
5726
6077
|
const input = buildInlineFormatInput(descriptor.inline, target, normalized, active);
|
|
5727
6078
|
if (!input) return false;
|
|
6079
|
+
let releaseScheduledMutation = null;
|
|
5728
6080
|
try {
|
|
5729
|
-
|
|
6081
|
+
const commandEditor = getEditor();
|
|
6082
|
+
const commandMode = readDocumentMode();
|
|
6083
|
+
const mutate = () => {
|
|
6084
|
+
if (getEditor() !== commandEditor || getDoc() !== doc || readDocumentMode() !== commandMode) return false;
|
|
6085
|
+
return callInlineFormatMutation(route, op, input);
|
|
6086
|
+
};
|
|
6087
|
+
const selectionSignature$1 = selectionInlineValueSignature(state.selection) ?? selectionKey(state.selection);
|
|
6088
|
+
const scheduledMutation = descriptor.inline.kind === "toggle" ? scheduleInlineToggleMutation(`${descriptor.id}:${selectionSignature$1}`, mutate) : null;
|
|
6089
|
+
releaseScheduledMutation = scheduledMutation?.release ?? null;
|
|
6090
|
+
const mutationResult = scheduledMutation ? scheduledMutation.result : mutate();
|
|
6091
|
+
const optimisticGeneration = captureOptimisticInlineToggle(descriptor, state.selection, input, mutationResult);
|
|
6092
|
+
const immediate = settleCommandExecution(decorateInlineCommandResult(descriptor, state.selection, input, mutationResult), (settled) => {
|
|
6093
|
+
settleOptimisticInlineToggle(descriptor, optimisticGeneration, settled);
|
|
6094
|
+
releaseScheduledMutation?.();
|
|
6095
|
+
releaseScheduledMutation = null;
|
|
6096
|
+
});
|
|
6097
|
+
if (descriptor.inline.kind === "toggle") lastInlineToggleCommandSettlement = lastCommandSettlement;
|
|
6098
|
+
return immediate;
|
|
5730
6099
|
} catch {
|
|
6100
|
+
releaseScheduledMutation?.();
|
|
5731
6101
|
return false;
|
|
5732
6102
|
}
|
|
5733
6103
|
}
|
|
@@ -5756,6 +6126,15 @@ function createSuperDocUI(options) {
|
|
|
5756
6126
|
recompute();
|
|
5757
6127
|
};
|
|
5758
6128
|
const executeCopyFormat = async () => {
|
|
6129
|
+
const visibleActiveMarksAtClick = [
|
|
6130
|
+
"bold",
|
|
6131
|
+
"italic",
|
|
6132
|
+
"underline",
|
|
6133
|
+
"strikethrough"
|
|
6134
|
+
].filter((id) => state.toolbar.commands[id]?.active === true);
|
|
6135
|
+
const inlineToggleSettlement = await lastInlineToggleCommandSettlement;
|
|
6136
|
+
await inlineToggleMutationIdle;
|
|
6137
|
+
const visibleActiveMarks = commandResultSucceeded(inlineToggleSettlement) ? visibleActiveMarksAtClick : [];
|
|
5759
6138
|
const DOUBLE_CLICK_MS = 500;
|
|
5760
6139
|
const now = Date.now();
|
|
5761
6140
|
if (painter.mode !== "idle") {
|
|
@@ -5778,11 +6157,9 @@ function createSuperDocUI(options) {
|
|
|
5778
6157
|
...painter,
|
|
5779
6158
|
lastClickAt: now
|
|
5780
6159
|
};
|
|
5781
|
-
const captureSlice =
|
|
6160
|
+
const captureSlice = await readSelectionInfoFresh();
|
|
5782
6161
|
const sourceKey = `${selectionKey(captureSlice)}:${contentToken()}`;
|
|
5783
|
-
const
|
|
5784
|
-
const sel = readSelectionInfoLive().value;
|
|
5785
|
-
const painterSnapshot = await captureFormatPainter(sel, captureSlice, captureProjected);
|
|
6162
|
+
const painterSnapshot = await captureFormatPainter(null, captureSlice, sourceKey && sourceKey === frozenProjectedValuesKey ? frozenProjectedValues : void 0, visibleActiveMarks);
|
|
5786
6163
|
if (captureEpoch !== painterCaptureEpoch) return true;
|
|
5787
6164
|
painter = {
|
|
5788
6165
|
...painter,
|
|
@@ -5795,7 +6172,7 @@ function createSuperDocUI(options) {
|
|
|
5795
6172
|
recompute();
|
|
5796
6173
|
return true;
|
|
5797
6174
|
};
|
|
5798
|
-
const captureFormatPainter = async (selection$1, captureSlice, captureProjected) => {
|
|
6175
|
+
const captureFormatPainter = async (selection$1, captureSlice, captureProjected, visibleActiveMarks = []) => {
|
|
5799
6176
|
const selTarget = captureSlice?.target ?? selection$1?.target;
|
|
5800
6177
|
if (!selTarget || selTarget["kind"] !== "text") return null;
|
|
5801
6178
|
const segments = Array.isArray(selTarget["segments"]) ? selTarget["segments"] : [];
|
|
@@ -5819,8 +6196,8 @@ function createSuperDocUI(options) {
|
|
|
5819
6196
|
underline: "underline",
|
|
5820
6197
|
strikethrough: "strike"
|
|
5821
6198
|
};
|
|
5822
|
-
const activeMarks = captureSlice?.activeMarks ?? (Array.isArray(selection$1?.["activeMarks"]) ? selection$1["activeMarks"] : []);
|
|
5823
|
-
for (const [markName, patchKey] of Object.entries(MARK_TO_PATCH)) if (activeMarks.
|
|
6199
|
+
const activeMarks = new Set([...captureSlice?.activeMarks ?? (Array.isArray(selection$1?.["activeMarks"]) ? selection$1["activeMarks"] : []), ...visibleActiveMarks]);
|
|
6200
|
+
for (const [markName, patchKey] of Object.entries(MARK_TO_PATCH)) if (activeMarks.has(markName)) inlinePatch[patchKey] = true;
|
|
5824
6201
|
const projectedInlineValues = captureProjected ?? await readFormatPainterInlineValues(captureSlice ?? selectionSliceFromInfo(selection$1, "ready"));
|
|
5825
6202
|
if (projectedInlineValues.fontFamily) inlinePatch["fontFamily"] = projectedInlineValues.fontFamily;
|
|
5826
6203
|
if (projectedInlineValues.color) inlinePatch["color"] = projectedInlineValues.color;
|
|
@@ -6079,12 +6456,15 @@ function createSuperDocUI(options) {
|
|
|
6079
6456
|
};
|
|
6080
6457
|
const executeCommandAsync = (id, payload, context) => {
|
|
6081
6458
|
const prepared = prepareCommandSelectionAsync(id, payload);
|
|
6082
|
-
|
|
6459
|
+
const settlement = prepared ? prepared.then(() => {
|
|
6083
6460
|
executeCommand(id, payload, context);
|
|
6084
6461
|
return lastCommandSettlement;
|
|
6085
|
-
})
|
|
6086
|
-
|
|
6087
|
-
|
|
6462
|
+
}) : (() => {
|
|
6463
|
+
executeCommand(id, payload, context);
|
|
6464
|
+
return lastCommandSettlement;
|
|
6465
|
+
})();
|
|
6466
|
+
if (getCommandDescriptor(id)?.inline?.kind === "toggle") lastInlineToggleCommandSettlement = settlement;
|
|
6467
|
+
return settlement;
|
|
6088
6468
|
};
|
|
6089
6469
|
const makeCommandHandle = (id) => ({
|
|
6090
6470
|
id,
|
|
@@ -7579,6 +7959,7 @@ function createSuperDocUI(options) {
|
|
|
7579
7959
|
if (disposed) return;
|
|
7580
7960
|
disposed = true;
|
|
7581
7961
|
reviewMutationReconciler.reset();
|
|
7962
|
+
clearAuthoritativeTrackChangesRetry();
|
|
7582
7963
|
if (foregroundAsyncRetryTimer) {
|
|
7583
7964
|
clearTimeout(foregroundAsyncRetryTimer);
|
|
7584
7965
|
foregroundAsyncRetryTimer = null;
|