superdoc 2.0.0-next.27 → 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-Dvl3EZsv.es.js → create-super-doc-ui-BKVf2uCM.es.js} +660 -159
- package/dist/chunks/{create-super-doc-ui-pYMthdDn.cjs → create-super-doc-ui-BXehf3i3.cjs} +660 -159
- package/dist/collaboration-upgrade-engine.cjs +1 -1
- package/dist/collaboration-upgrade-engine.es.js +1 -1
- package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
- package/dist/document-api/src/track-changes/track-changes.d.ts +6 -2
- package/dist/document-api/src/types/clipboard.d.ts +5 -1
- package/dist/document-api/src/types/table-operations.types.d.ts +6 -4
- package/dist/document-api/src/types/track-changes.types.d.ts +15 -0
- package/dist/layout-engine/contracts/src/incremental-dependency.d.ts +10 -0
- package/dist/layout-engine/contracts/src/index.d.ts +38 -0
- package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +40 -1
- package/dist/layout-engine/layout-engine/src/index.d.ts +17 -0
- package/dist/layout-engine/layout-engine/src/paginator.d.ts +16 -0
- package/dist/layout-engine/layout-engine/src/paragraph-layout-eligibility.d.ts +1 -4
- 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 +130 -86
- package/dist/style.layered.css +130 -86
- package/dist/superdoc/src/components/CommentsLayer/floating-comment-positioning.d.ts +1 -2
- package/dist/superdoc/src/core/types/index.d.ts +7 -0
- 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/public/ui/reasons.d.ts +8 -0
- package/dist/superdoc/src/stores/comments-store.d.ts +17 -11
- package/dist/superdoc/src/stores/helpers/floating-comment-instances.d.ts +2 -10
- package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +14 -0
- package/dist/superdoc/src/stores/superdoc-store.d.ts +51 -33
- package/dist/superdoc.cjs +531 -135
- package/dist/superdoc.es.js +531 -135
- 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;
|
|
@@ -311,7 +365,8 @@ const SUPERDOC_UI_REASONS = {
|
|
|
311
365
|
historyEmpty: "history-empty",
|
|
312
366
|
searchUnavailable: "search-unavailable",
|
|
313
367
|
searchInvalidPattern: "search-invalid-pattern",
|
|
314
|
-
replaceUnsupported: "replace-unsupported"
|
|
368
|
+
replaceUnsupported: "replace-unsupported",
|
|
369
|
+
contentControlLocked: "content-control-locked"
|
|
315
370
|
};
|
|
316
371
|
const BUILT_IN_COMMAND_IDS = {
|
|
317
372
|
bold: "bold",
|
|
@@ -2275,7 +2330,13 @@ function buildTableCommandInput(action, context) {
|
|
|
2275
2330
|
function createSuperDocUI(options) {
|
|
2276
2331
|
const superdoc = options.superdoc;
|
|
2277
2332
|
const optimisticInlineValues = /* @__PURE__ */ new Map();
|
|
2333
|
+
const optimisticInlineToggles = /* @__PURE__ */ new Map();
|
|
2334
|
+
const pendingInlineToggleMutations = [];
|
|
2278
2335
|
let lastOptimisticInlineSelectionSignature = null;
|
|
2336
|
+
let optimisticInlineToggleGeneration = 0;
|
|
2337
|
+
let inlineToggleMutationActive = false;
|
|
2338
|
+
let inlineToggleMutationIdle = Promise.resolve();
|
|
2339
|
+
let resolveInlineToggleMutationIdle = null;
|
|
2279
2340
|
let painter = {
|
|
2280
2341
|
mode: "idle",
|
|
2281
2342
|
snapshot: null,
|
|
@@ -2451,6 +2512,31 @@ function createSuperDocUI(options) {
|
|
|
2451
2512
|
let explicitActiveChange = null;
|
|
2452
2513
|
let explicitActiveChangeRevision = 0;
|
|
2453
2514
|
let queuedTrackChangeNavigationInvalidation = 0;
|
|
2515
|
+
const mirrorTrackedChangeFocusToHost = (next) => {
|
|
2516
|
+
const host = getHost();
|
|
2517
|
+
if (typeof host?.getHandles !== "function") return;
|
|
2518
|
+
const handles = safeCall(() => host.getHandles(), null);
|
|
2519
|
+
const review = handles?.review;
|
|
2520
|
+
if (!review) return;
|
|
2521
|
+
if (next) {
|
|
2522
|
+
if (typeof review.setActiveReviewTarget !== "function") return;
|
|
2523
|
+
const layout = handles?.layout;
|
|
2524
|
+
safeCall(() => review.setActiveReviewTarget({
|
|
2525
|
+
entityType: "trackedChange",
|
|
2526
|
+
entityId: next.id,
|
|
2527
|
+
...next.paintedEntityId ? { paintedEntityId: next.paintedEntityId } : {},
|
|
2528
|
+
origin: "panel",
|
|
2529
|
+
layoutEpoch: typeof layout?.generation === "number" ? layout.generation : 0,
|
|
2530
|
+
story: next.story ?? {
|
|
2531
|
+
kind: "story",
|
|
2532
|
+
storyType: "body"
|
|
2533
|
+
}
|
|
2534
|
+
}), null);
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (typeof review.getActiveReviewTarget !== "function" || typeof review.clearActiveReviewTarget !== "function") return;
|
|
2538
|
+
if (safeCall(() => review.getActiveReviewTarget(), null)?.entityType === "trackedChange") safeCall(() => review.clearActiveReviewTarget(), void 0);
|
|
2539
|
+
};
|
|
2454
2540
|
const setExplicitActiveChange = (next, options$1) => {
|
|
2455
2541
|
explicitActiveChange = next;
|
|
2456
2542
|
explicitActiveChangeRevision += 1;
|
|
@@ -2464,6 +2550,8 @@ function createSuperDocUI(options) {
|
|
|
2464
2550
|
let lastEffectiveInlineReadKey = null;
|
|
2465
2551
|
let pendingEffectiveInlineRead = null;
|
|
2466
2552
|
let asyncRefreshScheduled = false;
|
|
2553
|
+
let asyncReadFailureRetryTimer = null;
|
|
2554
|
+
let asyncReadFailureRetryAtMs = 0;
|
|
2467
2555
|
let foregroundAsyncRetryTimer = null;
|
|
2468
2556
|
let pendingSelectionSeedValidationToken = null;
|
|
2469
2557
|
let typingContentInvalidationTimer = null;
|
|
@@ -2478,6 +2566,19 @@ function createSuperDocUI(options) {
|
|
|
2478
2566
|
if (typeof queueMicrotask === "function") queueMicrotask(run);
|
|
2479
2567
|
else Promise.resolve().then(run);
|
|
2480
2568
|
};
|
|
2569
|
+
const ASYNC_READ_FAILURE_RETRY_BASE_MS = 250;
|
|
2570
|
+
const ASYNC_READ_FAILURE_RETRY_MAX_MS = 5e3;
|
|
2571
|
+
const scheduleAsyncReadFailureRetry = (retryAtMs) => {
|
|
2572
|
+
if (disposed) return;
|
|
2573
|
+
if (asyncReadFailureRetryTimer && asyncReadFailureRetryAtMs <= retryAtMs) return;
|
|
2574
|
+
if (asyncReadFailureRetryTimer) clearTimeout(asyncReadFailureRetryTimer);
|
|
2575
|
+
asyncReadFailureRetryAtMs = retryAtMs;
|
|
2576
|
+
asyncReadFailureRetryTimer = setTimeout(() => {
|
|
2577
|
+
asyncReadFailureRetryTimer = null;
|
|
2578
|
+
asyncReadFailureRetryAtMs = 0;
|
|
2579
|
+
if (!disposed) recompute();
|
|
2580
|
+
}, Math.max(0, retryAtMs - Date.now()));
|
|
2581
|
+
};
|
|
2481
2582
|
const editorIdentityIds = /* @__PURE__ */ new WeakMap();
|
|
2482
2583
|
let editorIdentityCounter = 0;
|
|
2483
2584
|
const editorIdentityId = (editor) => {
|
|
@@ -2490,6 +2591,16 @@ function createSuperDocUI(options) {
|
|
|
2490
2591
|
return `e${id}`;
|
|
2491
2592
|
};
|
|
2492
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;
|
|
2493
2604
|
let postDecisionTrackChangesToken = null;
|
|
2494
2605
|
let postDecisionTrackChangesReconcileToken = null;
|
|
2495
2606
|
let postDecisionTrackChangeIds = /* @__PURE__ */ new Set();
|
|
@@ -2535,28 +2646,92 @@ function createSuperDocUI(options) {
|
|
|
2535
2646
|
run();
|
|
2536
2647
|
}, POST_DECISION_TRACKED_CHANGES_RECONCILE_DELAY_MS);
|
|
2537
2648
|
};
|
|
2538
|
-
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) => {
|
|
2539
2706
|
if (ids.size === 0) return;
|
|
2540
|
-
|
|
2541
|
-
|
|
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]);
|
|
2542
2713
|
applyPostDecisionTrackChangesToCache(postDecisionTrackChangeIds);
|
|
2543
2714
|
schedulePostDecisionTrackChangesReconcile();
|
|
2544
|
-
|
|
2715
|
+
publishPostDecisionTrackChanges(newlyRemoved, receipt);
|
|
2545
2716
|
};
|
|
2546
2717
|
const postDecisionTrackChangeIdsForToken = (token) => postDecisionTrackChangesToken === token && postDecisionTrackChangeIds.size > 0 ? postDecisionTrackChangeIds : null;
|
|
2547
|
-
const mergeTrackedChangeItemsIntoCache = (items) => {
|
|
2718
|
+
const mergeTrackedChangeItemsIntoCache = (items, orderedItems = []) => {
|
|
2548
2719
|
const projected = items.map(projectTrackChangesItem).filter((item) => item != null && readEntityId(item) != null);
|
|
2549
2720
|
if (projected.length === 0) return;
|
|
2721
|
+
const orderedIds = orderedItems.map(projectTrackChangesItem).map((item) => item == null ? null : readEntityId(item)).filter((id) => id != null);
|
|
2550
2722
|
for (const key of ["trackChanges", "trackChanges:all"]) {
|
|
2551
2723
|
const entry = asyncReads.get(key);
|
|
2552
2724
|
const nextValue = entry?.hasSettled && Array.isArray(entry.value) ? [...entry.value] : [];
|
|
2725
|
+
const insertedIds = /* @__PURE__ */ new Set();
|
|
2553
2726
|
let changed = false;
|
|
2554
2727
|
for (const fresh of projected) {
|
|
2555
2728
|
const id = readEntityId(fresh);
|
|
2556
2729
|
if (!id) continue;
|
|
2557
2730
|
const index = nextValue.findIndex((candidate) => readEntityId(candidate) === id);
|
|
2558
|
-
if (index < 0)
|
|
2559
|
-
|
|
2731
|
+
if (index < 0) {
|
|
2732
|
+
nextValue.push(fresh);
|
|
2733
|
+
insertedIds.add(id);
|
|
2734
|
+
} else {
|
|
2560
2735
|
const previous = nextValue[index];
|
|
2561
2736
|
nextValue[index] = isLooseObject(previous) ? {
|
|
2562
2737
|
...previous,
|
|
@@ -2565,6 +2740,24 @@ function createSuperDocUI(options) {
|
|
|
2565
2740
|
}
|
|
2566
2741
|
changed = true;
|
|
2567
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
|
+
}
|
|
2568
2761
|
if (!changed) continue;
|
|
2569
2762
|
asyncReads.set(key, {
|
|
2570
2763
|
token: entry?.token ?? null,
|
|
@@ -2574,6 +2767,57 @@ function createSuperDocUI(options) {
|
|
|
2574
2767
|
});
|
|
2575
2768
|
}
|
|
2576
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
|
+
};
|
|
2577
2821
|
const reviewMutationReconciler = createV2ReviewMutationReconciler({
|
|
2578
2822
|
getContext: () => {
|
|
2579
2823
|
const editor = getEditor();
|
|
@@ -2587,6 +2831,7 @@ function createSuperDocUI(options) {
|
|
|
2587
2831
|
editor,
|
|
2588
2832
|
adapter,
|
|
2589
2833
|
documentId: rawDocumentId == null ? null : String(rawDocumentId),
|
|
2834
|
+
reconcileToken: reviewMutationToken,
|
|
2590
2835
|
v2TrackedChanges,
|
|
2591
2836
|
docTrackChanges
|
|
2592
2837
|
};
|
|
@@ -2600,6 +2845,9 @@ function createSuperDocUI(options) {
|
|
|
2600
2845
|
resolvedIds: [],
|
|
2601
2846
|
unresolvedIds: []
|
|
2602
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)));
|
|
2603
2851
|
const getTrackedChange = context.v2TrackedChanges?.getTrackedChange;
|
|
2604
2852
|
const getRawTrackedChange = context.docTrackChanges?.get;
|
|
2605
2853
|
if (typeof getTrackedChange !== "function" && typeof getRawTrackedChange !== "function") return {
|
|
@@ -2648,55 +2896,59 @@ function createSuperDocUI(options) {
|
|
|
2648
2896
|
resolvedIds.push(read.id);
|
|
2649
2897
|
} else unresolvedIds.push(read.id);
|
|
2650
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
|
+
}
|
|
2651
2910
|
return {
|
|
2652
2911
|
ok: unresolvedIds.length === 0,
|
|
2653
2912
|
items,
|
|
2913
|
+
orderedItems,
|
|
2654
2914
|
resolvedIds,
|
|
2655
2915
|
unresolvedIds,
|
|
2656
2916
|
failures: reads.filter((read) => !read.ok).map((read) => read.result)
|
|
2657
2917
|
};
|
|
2658
2918
|
},
|
|
2659
|
-
hydrate:
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
if (
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
const list = context.docTrackChanges?.list;
|
|
2674
|
-
if (typeof list !== "function") return {
|
|
2675
|
-
ok: false,
|
|
2676
|
-
items: []
|
|
2677
|
-
};
|
|
2678
|
-
const result = await Promise.resolve(list.call(context.docTrackChanges));
|
|
2679
|
-
return {
|
|
2680
|
-
ok: true,
|
|
2681
|
-
items: Array.isArray(result?.items) ? result.items : []
|
|
2682
|
-
};
|
|
2683
|
-
} catch (error) {
|
|
2684
|
-
return {
|
|
2685
|
-
ok: false,
|
|
2686
|
-
items: [],
|
|
2687
|
-
failures: [error]
|
|
2688
|
-
};
|
|
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;
|
|
2689
2933
|
}
|
|
2690
|
-
},
|
|
2691
|
-
onReconciled: (_context, ids, result) => {
|
|
2692
2934
|
mergeTrackedChangeItemsIntoCache((result?.items ?? []).filter((item) => {
|
|
2693
2935
|
const projected = projectTrackChangesItem(item);
|
|
2694
2936
|
const id = projected == null ? null : readEntityId(projected);
|
|
2695
2937
|
return id != null && ids.has(id);
|
|
2696
|
-
}));
|
|
2938
|
+
}), result?.orderedItems ?? result?.items ?? []);
|
|
2697
2939
|
recompute();
|
|
2698
2940
|
}
|
|
2699
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
|
+
};
|
|
2700
2952
|
const selectionReadToken = () => `${contentToken()}|m${readDocumentMode()}|s${selectionEpoch}`;
|
|
2701
2953
|
const selectionSignature = (selection$1) => JSON.stringify({
|
|
2702
2954
|
t: selection$1.target ?? null,
|
|
@@ -2714,7 +2966,7 @@ function createSuperDocUI(options) {
|
|
|
2714
2966
|
const read = host?.getDocumentLoadingSnapshot;
|
|
2715
2967
|
if (typeof read !== "function") return "unknown";
|
|
2716
2968
|
const stage = safeCall(() => read.call(host), null)?.sourceStage;
|
|
2717
|
-
if (stage === "source-loading") return "loading";
|
|
2969
|
+
if (stage === "opening" || stage === "ready-window" || stage === "source-loading") return "loading";
|
|
2718
2970
|
if (stage === "source-complete" || stage === "source-failed" || stage === "source-cancelled") return "complete";
|
|
2719
2971
|
return "unknown";
|
|
2720
2972
|
};
|
|
@@ -2880,17 +3132,24 @@ function createSuperDocUI(options) {
|
|
|
2880
3132
|
token,
|
|
2881
3133
|
value: normalize(raw),
|
|
2882
3134
|
hasSettled: true,
|
|
2883
|
-
inflightToken: null
|
|
3135
|
+
inflightToken: null,
|
|
3136
|
+
failedToken: null,
|
|
3137
|
+
failureCount: 0,
|
|
3138
|
+
retryAtMs: 0
|
|
2884
3139
|
};
|
|
2885
3140
|
asyncReads.set(key, settled);
|
|
2886
3141
|
return settled;
|
|
2887
3142
|
}
|
|
2888
3143
|
const prev = asyncReads.get(key);
|
|
3144
|
+
const retryingSameToken = prev?.failedToken === token;
|
|
2889
3145
|
asyncReads.set(key, {
|
|
2890
3146
|
token: prev?.token ?? null,
|
|
2891
3147
|
value: prev?.value ?? null,
|
|
2892
3148
|
hasSettled: prev?.hasSettled ?? false,
|
|
2893
|
-
inflightToken: token
|
|
3149
|
+
inflightToken: token,
|
|
3150
|
+
failedToken: retryingSameToken ? token : null,
|
|
3151
|
+
failureCount: retryingSameToken ? prev.failureCount ?? 0 : 0,
|
|
3152
|
+
retryAtMs: 0
|
|
2894
3153
|
});
|
|
2895
3154
|
Promise.resolve(raw).then((resolved) => {
|
|
2896
3155
|
const entry = asyncReads.get(key);
|
|
@@ -2899,17 +3158,26 @@ function createSuperDocUI(options) {
|
|
|
2899
3158
|
token,
|
|
2900
3159
|
value: normalize(resolved),
|
|
2901
3160
|
hasSettled: true,
|
|
2902
|
-
inflightToken: null
|
|
3161
|
+
inflightToken: null,
|
|
3162
|
+
failedToken: null,
|
|
3163
|
+
failureCount: 0,
|
|
3164
|
+
retryAtMs: 0
|
|
2903
3165
|
});
|
|
2904
3166
|
scheduleAsyncRefresh();
|
|
2905
3167
|
}, () => {
|
|
2906
3168
|
const entry = asyncReads.get(key);
|
|
2907
3169
|
if (!entry || entry.inflightToken !== token) return;
|
|
3170
|
+
const failureCount = entry.failedToken === token ? (entry.failureCount ?? 0) + 1 : 1;
|
|
3171
|
+
const retryDelayMs = Math.min(ASYNC_READ_FAILURE_RETRY_MAX_MS, ASYNC_READ_FAILURE_RETRY_BASE_MS * 2 ** Math.min(failureCount - 1, 8));
|
|
3172
|
+
const retryAtMs = Date.now() + retryDelayMs;
|
|
2908
3173
|
asyncReads.set(key, {
|
|
2909
3174
|
...entry,
|
|
2910
|
-
inflightToken: null
|
|
3175
|
+
inflightToken: null,
|
|
3176
|
+
failedToken: token,
|
|
3177
|
+
failureCount,
|
|
3178
|
+
retryAtMs
|
|
2911
3179
|
});
|
|
2912
|
-
|
|
3180
|
+
scheduleAsyncReadFailureRetry(retryAtMs);
|
|
2913
3181
|
});
|
|
2914
3182
|
return null;
|
|
2915
3183
|
};
|
|
@@ -2970,6 +3238,17 @@ function createSuperDocUI(options) {
|
|
|
2970
3238
|
status: "pending"
|
|
2971
3239
|
};
|
|
2972
3240
|
}
|
|
3241
|
+
if (entry?.failedToken === token && entry.inflightToken == null && (entry.retryAtMs ?? 0) > Date.now()) {
|
|
3242
|
+
scheduleAsyncReadFailureRetry(entry.retryAtMs);
|
|
3243
|
+
if (entry.hasSettled) return {
|
|
3244
|
+
value: entry.value,
|
|
3245
|
+
status: "stale"
|
|
3246
|
+
};
|
|
3247
|
+
return {
|
|
3248
|
+
value: null,
|
|
3249
|
+
status: "pending"
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
2973
3252
|
if (!entry && key.startsWith("effInline:") && shouldDeferEffectiveInlineRead(key)) return {
|
|
2974
3253
|
value: null,
|
|
2975
3254
|
status: "pending"
|
|
@@ -3019,8 +3298,14 @@ function createSuperDocUI(options) {
|
|
|
3019
3298
|
const editor = getEditor();
|
|
3020
3299
|
if (editor === lastCoordinatorEditor) return;
|
|
3021
3300
|
reviewMutationReconciler.reset();
|
|
3301
|
+
reviewMutationToken = {};
|
|
3022
3302
|
lastCoordinatorEditor = editor;
|
|
3023
3303
|
asyncReads.clear();
|
|
3304
|
+
authoritativeTrackChangesPendingToken = null;
|
|
3305
|
+
clearAuthoritativeTrackChangesRetry();
|
|
3306
|
+
if (asyncReadFailureRetryTimer) clearTimeout(asyncReadFailureRetryTimer);
|
|
3307
|
+
asyncReadFailureRetryTimer = null;
|
|
3308
|
+
asyncReadFailureRetryAtMs = 0;
|
|
3024
3309
|
if (pendingEffectiveInlineRead?.timer) clearTimeout(pendingEffectiveInlineRead.timer);
|
|
3025
3310
|
pendingEffectiveInlineRead = null;
|
|
3026
3311
|
lastEffectiveInlineReadKey = null;
|
|
@@ -3036,7 +3321,9 @@ function createSuperDocUI(options) {
|
|
|
3036
3321
|
clearPostDecisionTrackChanges();
|
|
3037
3322
|
};
|
|
3038
3323
|
const invalidateDocumentContent = (options$1) => {
|
|
3324
|
+
const carryAuthoritativeTrackChangesHold = authoritativeTrackChangesPendingToken !== null;
|
|
3039
3325
|
documentMutationRevision += 1;
|
|
3326
|
+
if (carryAuthoritativeTrackChangesHold) authoritativeTrackChangesPendingToken = contentToken();
|
|
3040
3327
|
if (!options$1?.preservePostDecisionTrackChanges) clearPostDecisionTrackChanges();
|
|
3041
3328
|
};
|
|
3042
3329
|
const insertText = (text) => {
|
|
@@ -3227,7 +3514,12 @@ function createSuperDocUI(options) {
|
|
|
3227
3514
|
};
|
|
3228
3515
|
const readAllStoryTrackChanges = () => {
|
|
3229
3516
|
const tcApi = getDoc()?.trackChanges;
|
|
3230
|
-
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) => {
|
|
3231
3523
|
return (raw && Array.isArray(raw.items) ? raw.items : []).map(projectTrackChangesItem).filter((item) => item != null);
|
|
3232
3524
|
});
|
|
3233
3525
|
return status === "ready" ? value : null;
|
|
@@ -3238,7 +3530,11 @@ function createSuperDocUI(options) {
|
|
|
3238
3530
|
const listTrackedChanges = typeof v2TrackedChanges?.listTrackedChanges === "function" ? v2TrackedChanges.listTrackedChanges : null;
|
|
3239
3531
|
const token = contentToken();
|
|
3240
3532
|
const postDecisionIds = postDecisionTrackChangeIdsForToken(token);
|
|
3241
|
-
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, () => {
|
|
3242
3538
|
if (listTrackedChanges) return postDecisionTrackChangesToken === token ? listTrackedChanges.call(v2TrackedChanges, POST_DECISION_TRACKED_CHANGES_LIST_OPTIONS) : listTrackedChanges.call(v2TrackedChanges);
|
|
3243
3539
|
return tcApi?.list ? tcApi.list() : void 0;
|
|
3244
3540
|
}, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
|
|
@@ -3284,22 +3580,29 @@ function createSuperDocUI(options) {
|
|
|
3284
3580
|
activeIds
|
|
3285
3581
|
};
|
|
3286
3582
|
};
|
|
3287
|
-
const
|
|
3583
|
+
const computeActiveContentControlLockModes = (ccApi, selection$1) => {
|
|
3288
3584
|
if (!ccApi || typeof ccApi.listInRange !== "function") return {
|
|
3289
|
-
|
|
3585
|
+
lockModesById: /* @__PURE__ */ new Map(),
|
|
3290
3586
|
status: "ready"
|
|
3291
3587
|
};
|
|
3292
3588
|
const range = selectionBlockRange(selection$1);
|
|
3293
3589
|
if (!range) return {
|
|
3294
|
-
|
|
3590
|
+
lockModesById: /* @__PURE__ */ new Map(),
|
|
3295
3591
|
status: "ready"
|
|
3296
3592
|
};
|
|
3297
3593
|
const { value, status } = readAsync(`contentControls:inRange:${selectionSignature(selection$1)}`, contentToken(), () => ccApi.listInRange(range), (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
|
|
3298
3594
|
const rows = value ?? [];
|
|
3299
|
-
const
|
|
3300
|
-
for (const row of rows) if (typeof row?.id === "string")
|
|
3595
|
+
const lockModesById = /* @__PURE__ */ new Map();
|
|
3596
|
+
for (const row of rows) if (typeof row?.id === "string") lockModesById.set(row.id, typeof row.lockMode === "string" ? row.lockMode : "unlocked");
|
|
3597
|
+
return {
|
|
3598
|
+
lockModesById,
|
|
3599
|
+
status
|
|
3600
|
+
};
|
|
3601
|
+
};
|
|
3602
|
+
const computeActiveContentControlIds = (ccApi, selection$1) => {
|
|
3603
|
+
const { lockModesById, status } = computeActiveContentControlLockModes(ccApi, selection$1);
|
|
3301
3604
|
return {
|
|
3302
|
-
ids,
|
|
3605
|
+
ids: [...lockModesById.keys()],
|
|
3303
3606
|
status
|
|
3304
3607
|
};
|
|
3305
3608
|
};
|
|
@@ -3615,14 +3918,20 @@ function createSuperDocUI(options) {
|
|
|
3615
3918
|
frozenProjectedValues = projectedInlineValues;
|
|
3616
3919
|
frozenProjectedValuesKey = `${selectionKey(selection$1)}:${contentToken()}`;
|
|
3617
3920
|
}
|
|
3921
|
+
const ccApi = doc?.contentControls;
|
|
3922
|
+
const { lockModesById } = computeActiveContentControlLockModes(ccApi, selection$1);
|
|
3618
3923
|
const states = {};
|
|
3619
|
-
for (const id of allCommandIds()) states[id] = computeCommandState(id, doc, selection$1, projectedInlineValues);
|
|
3924
|
+
for (const id of allCommandIds()) states[id] = computeCommandState(id, doc, selection$1, projectedInlineValues, lockModesById);
|
|
3620
3925
|
return states;
|
|
3621
3926
|
};
|
|
3622
3927
|
const unavailableRouteReason = (doc) => {
|
|
3623
3928
|
if (doc) return SUPERDOC_UI_REASONS.operationUnavailable;
|
|
3624
3929
|
return getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady;
|
|
3625
3930
|
};
|
|
3931
|
+
const blocksContentStyling = (lockMode) => lockMode === "contentLocked" || lockMode === "sdtContentLocked";
|
|
3932
|
+
const contentControlLockReason = (lockModesById) => {
|
|
3933
|
+
for (const lockMode of lockModesById.values()) if (blocksContentStyling(lockMode)) return SUPERDOC_UI_REASONS.contentControlLocked;
|
|
3934
|
+
};
|
|
3626
3935
|
const trackDecisionReason = (command, supported, readonly, selection$1, doc) => {
|
|
3627
3936
|
if (!supported) return command.scope === "all" ? SUPERDOC_UI_REASONS.bulkDecisionsDisabled : unavailableRouteReason(doc);
|
|
3628
3937
|
if (readonly) return SUPERDOC_UI_REASONS.documentReadonly;
|
|
@@ -3645,7 +3954,7 @@ function createSuperDocUI(options) {
|
|
|
3645
3954
|
if (support && (support.status === "supported" || support.enabled === true)) return void 0;
|
|
3646
3955
|
return SUPERDOC_UI_REASONS.bulkDecisionsDisabled;
|
|
3647
3956
|
};
|
|
3648
|
-
const computeCommandState = (id, doc, selection$1, projectedInlineValues) => {
|
|
3957
|
+
const computeCommandState = (id, doc, selection$1, projectedInlineValues, lockModesById = /* @__PURE__ */ new Map()) => {
|
|
3649
3958
|
if (customCommands.has(id)) return normalizeCommandState({
|
|
3650
3959
|
supported: true,
|
|
3651
3960
|
enabled: true,
|
|
@@ -3720,6 +4029,13 @@ function createSuperDocUI(options) {
|
|
|
3720
4029
|
supported: true,
|
|
3721
4030
|
reason: SUPERDOC_UI_REASONS.documentReadonly
|
|
3722
4031
|
}, "builtin");
|
|
4032
|
+
const lockReason = contentControlLockReason(lockModesById);
|
|
4033
|
+
if (lockReason) return normalizeCommandState({
|
|
4034
|
+
enabled: false,
|
|
4035
|
+
active: false,
|
|
4036
|
+
supported: true,
|
|
4037
|
+
reason: lockReason
|
|
4038
|
+
}, "builtin");
|
|
3723
4039
|
const enabled = selectionBlockIds(selection$1).length > 0 || resolveInlineSelectionTarget(selection$1) != null;
|
|
3724
4040
|
return normalizeCommandState({
|
|
3725
4041
|
enabled,
|
|
@@ -3770,18 +4086,28 @@ function createSuperDocUI(options) {
|
|
|
3770
4086
|
value,
|
|
3771
4087
|
reason: SUPERDOC_UI_REASONS.documentReadonly
|
|
3772
4088
|
}, "builtin");
|
|
3773
|
-
if (descriptor.inline && !resolveInlineSelectionTarget(selection$1))
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
4089
|
+
if (descriptor.inline && !resolveInlineSelectionTarget(selection$1)) {
|
|
4090
|
+
const lockReason = contentControlLockReason(lockModesById);
|
|
4091
|
+
if (lockReason) return normalizeCommandState({
|
|
4092
|
+
enabled: false,
|
|
4093
|
+
active,
|
|
4094
|
+
supported: true,
|
|
4095
|
+
value,
|
|
4096
|
+
reason: lockReason
|
|
4097
|
+
}, "builtin");
|
|
4098
|
+
return normalizeCommandState(selectionBlockIds(selection$1).length > 0 && typeof getHost()?.setPendingInlineFormat === "function" ? {
|
|
4099
|
+
enabled: true,
|
|
4100
|
+
active,
|
|
4101
|
+
supported: true,
|
|
4102
|
+
value
|
|
4103
|
+
} : {
|
|
4104
|
+
enabled: false,
|
|
4105
|
+
active,
|
|
4106
|
+
supported: true,
|
|
4107
|
+
value,
|
|
4108
|
+
reason: SUPERDOC_UI_REASONS.rangeSelectionRequired
|
|
4109
|
+
}, "builtin");
|
|
4110
|
+
}
|
|
3785
4111
|
if ((descriptor.blockParagraph || descriptor.list) && selectionBlockIds(selection$1).length === 0) return normalizeCommandState({
|
|
3786
4112
|
enabled: false,
|
|
3787
4113
|
active,
|
|
@@ -3795,6 +4121,16 @@ function createSuperDocUI(options) {
|
|
|
3795
4121
|
supported: true,
|
|
3796
4122
|
reason: SUPERDOC_UI_REASONS.rangeSelectionRequired
|
|
3797
4123
|
}, "builtin");
|
|
4124
|
+
if (descriptor.inline) {
|
|
4125
|
+
const lockReason = contentControlLockReason(lockModesById);
|
|
4126
|
+
if (lockReason) return normalizeCommandState({
|
|
4127
|
+
enabled: false,
|
|
4128
|
+
active,
|
|
4129
|
+
supported: true,
|
|
4130
|
+
value,
|
|
4131
|
+
reason: lockReason
|
|
4132
|
+
}, "builtin");
|
|
4133
|
+
}
|
|
3798
4134
|
return normalizeCommandState({
|
|
3799
4135
|
enabled: true,
|
|
3800
4136
|
active,
|
|
@@ -3857,6 +4193,9 @@ function createSuperDocUI(options) {
|
|
|
3857
4193
|
if (descriptor.list?.mode === "toggle-seed" && descriptor.list.seed) return readListSeed(doc, selection$1) === descriptor.list.seed;
|
|
3858
4194
|
const pending = pendingInlineActive(descriptor);
|
|
3859
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;
|
|
3860
4199
|
return commandIsActive(descriptor, selection$1);
|
|
3861
4200
|
};
|
|
3862
4201
|
const routedCommandValue = (descriptor, doc, selection$1, projectedInlineValues) => {
|
|
@@ -4483,6 +4822,9 @@ function createSuperDocUI(options) {
|
|
|
4483
4822
|
const selection$1 = computeSelection();
|
|
4484
4823
|
if (selection$1.status === "ready") pruneSelectionScopedAsyncReads(selection$1);
|
|
4485
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);
|
|
4486
4828
|
return {
|
|
4487
4829
|
ready: getEditor() != null,
|
|
4488
4830
|
documentMode,
|
|
@@ -4503,7 +4845,10 @@ function createSuperDocUI(options) {
|
|
|
4503
4845
|
};
|
|
4504
4846
|
const syncOptimisticInlineSelection = (selection$1) => {
|
|
4505
4847
|
const nextSignature = selectionInlineValueSignature(selection$1);
|
|
4506
|
-
if (nextSignature && lastOptimisticInlineSelectionSignature && nextSignature !== lastOptimisticInlineSelectionSignature)
|
|
4848
|
+
if (nextSignature && lastOptimisticInlineSelectionSignature && nextSignature !== lastOptimisticInlineSelectionSignature) {
|
|
4849
|
+
optimisticInlineValues.clear();
|
|
4850
|
+
optimisticInlineToggles.clear();
|
|
4851
|
+
}
|
|
4507
4852
|
if (nextSignature) lastOptimisticInlineSelectionSignature = nextSignature;
|
|
4508
4853
|
};
|
|
4509
4854
|
let currentHostSelectionSource = null;
|
|
@@ -4581,24 +4926,48 @@ function createSuperDocUI(options) {
|
|
|
4581
4926
|
try {
|
|
4582
4927
|
const off = next.subscribe((event) => {
|
|
4583
4928
|
const type = event?.type;
|
|
4584
|
-
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
|
+
}
|
|
4585
4941
|
if (type === "mutation:committed" || type === "save:completed") {
|
|
4586
4942
|
const impact = type === "mutation:committed" ? getV2TrackedChangeMutationImpact(event) : null;
|
|
4587
|
-
|
|
4588
|
-
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) {
|
|
4589
4953
|
const receipt = event.origin === "history" ? null : event.receipt;
|
|
4590
4954
|
const readiness = getEditor()?.documentMutationReadiness;
|
|
4591
|
-
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";
|
|
4592
4958
|
const upsertImpact = {
|
|
4593
|
-
upsertIds: impact
|
|
4594
|
-
removedIds: /* @__PURE__ */ new Set()
|
|
4959
|
+
upsertIds: impact?.upsertIds ?? /* @__PURE__ */ new Set(),
|
|
4960
|
+
removedIds: /* @__PURE__ */ new Set(),
|
|
4961
|
+
reconcileMode: impact?.reconcileMode === "authoritative" ? "authoritative" : "targeted"
|
|
4595
4962
|
};
|
|
4596
|
-
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));
|
|
4597
4965
|
else {
|
|
4598
4966
|
reviewMutationReconciler.enqueue(upsertImpact);
|
|
4599
|
-
reviewMutationReconciler.onRender();
|
|
4967
|
+
reviewMutationReconciler.onRender(supersedesPendingReviewReconcile);
|
|
4600
4968
|
}
|
|
4601
4969
|
}
|
|
4970
|
+
if (authoritativeHistoryImpact) return;
|
|
4602
4971
|
if (type === "mutation:committed" && isEditableTextMutationEvent(event)) {
|
|
4603
4972
|
lastTypingMutationAtMs = Date.now();
|
|
4604
4973
|
scheduleTypingDocumentContentInvalidation();
|
|
@@ -4753,7 +5122,10 @@ function createSuperDocUI(options) {
|
|
|
4753
5122
|
}
|
|
4754
5123
|
function executeTrackDecisionCommand(command, payload) {
|
|
4755
5124
|
if (command.scope === "id" && payload == null) {
|
|
4756
|
-
if (state.selection.activeChangeIds.length > 1) return
|
|
5125
|
+
if (state.selection.activeChangeIds.length > 1) return executeTrackDecisionTarget(command.kind, {
|
|
5126
|
+
kind: "ids",
|
|
5127
|
+
ids: state.selection.activeChangeIds
|
|
5128
|
+
}, null);
|
|
4757
5129
|
if (state.selection.activeChangeIds.length === 1 && !state.selection.empty && state.selection.selectionTarget) {
|
|
4758
5130
|
const route = activeChangePartialDecisionRoute();
|
|
4759
5131
|
if (route === "unavailable") return false;
|
|
@@ -4789,6 +5161,7 @@ function createSuperDocUI(options) {
|
|
|
4789
5161
|
}
|
|
4790
5162
|
let lastCommandSettlement = Promise.resolve(false);
|
|
4791
5163
|
let pendingCommandSettlement = null;
|
|
5164
|
+
let lastInlineToggleCommandSettlement = Promise.resolve(false);
|
|
4792
5165
|
const awaitMutationReadiness = async (result) => {
|
|
4793
5166
|
if (!isSuccessfulReceipt(result)) return result;
|
|
4794
5167
|
const readiness = getEditor()?.documentMutationReadiness;
|
|
@@ -4816,6 +5189,69 @@ function createSuperDocUI(options) {
|
|
|
4816
5189
|
settled: awaitMutationReadiness(immediate)
|
|
4817
5190
|
};
|
|
4818
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
|
+
};
|
|
4819
5255
|
const UNSUPPORTED_TRACKED_UI_MUTATION_ROUTES = new Set(["create.tableOfContents"]);
|
|
4820
5256
|
const unsupportedTrackedMutationReceipt = (route) => failedReceipt(`Tracked authoring is not supported for ${route}.`);
|
|
4821
5257
|
const editorMutationOptionsForRoute = (route) => {
|
|
@@ -4859,33 +5295,62 @@ function createSuperDocUI(options) {
|
|
|
4859
5295
|
});
|
|
4860
5296
|
return result;
|
|
4861
5297
|
};
|
|
4862
|
-
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);
|
|
4863
5326
|
invalidateDocumentContent();
|
|
4864
5327
|
recompute();
|
|
4865
5328
|
return settled;
|
|
4866
5329
|
}, () => {
|
|
5330
|
+
onSettled?.(false);
|
|
4867
5331
|
invalidateDocumentContent();
|
|
4868
5332
|
recompute();
|
|
4869
5333
|
return false;
|
|
4870
5334
|
});
|
|
4871
|
-
const settleCommandExecution = (result) => {
|
|
5335
|
+
const settleCommandExecution = (result, onSettled) => {
|
|
4872
5336
|
const pending = pendingCommandSettlement;
|
|
4873
5337
|
pendingCommandSettlement = null;
|
|
4874
5338
|
if (pending) {
|
|
4875
|
-
lastCommandSettlement = finalizeCommandSettlement(pending);
|
|
5339
|
+
lastCommandSettlement = finalizeCommandSettlement(pending, onSettled);
|
|
4876
5340
|
return commandResultFromOperationResult(result);
|
|
4877
5341
|
}
|
|
4878
5342
|
if (isPromiseLike(result)) {
|
|
4879
|
-
lastCommandSettlement = finalizeCommandSettlement(settleOperationResult(result).settled);
|
|
5343
|
+
lastCommandSettlement = finalizeCommandSettlement(settleOperationResult(result).settled, onSettled);
|
|
4880
5344
|
return true;
|
|
4881
5345
|
}
|
|
4882
5346
|
const settled = commandResultFromOperationResult(result);
|
|
4883
5347
|
if (isSuccessfulReceipt(settled)) {
|
|
4884
5348
|
if (typeof (getEditor()?.documentMutationReadiness)?.whenPainted === "function") {
|
|
4885
|
-
lastCommandSettlement = finalizeCommandSettlement(awaitMutationReadiness(settled));
|
|
5349
|
+
lastCommandSettlement = finalizeCommandSettlement(awaitMutationReadiness(settled), onSettled);
|
|
4886
5350
|
return settled;
|
|
4887
5351
|
}
|
|
4888
5352
|
}
|
|
5353
|
+
onSettled?.(settled);
|
|
4889
5354
|
lastCommandSettlement = Promise.resolve(settled);
|
|
4890
5355
|
invalidateDocumentContent();
|
|
4891
5356
|
recompute();
|
|
@@ -5025,6 +5490,9 @@ function createSuperDocUI(options) {
|
|
|
5025
5490
|
return true;
|
|
5026
5491
|
};
|
|
5027
5492
|
const executeClearFormattingCommand = () => {
|
|
5493
|
+
const doc = getDoc();
|
|
5494
|
+
const { lockModesById } = computeActiveContentControlLockModes(doc?.contentControls, state.selection);
|
|
5495
|
+
if (contentControlLockReason(lockModesById)) return false;
|
|
5028
5496
|
const immediateResults = [];
|
|
5029
5497
|
const settledResults = [];
|
|
5030
5498
|
const record = (result) => {
|
|
@@ -5040,7 +5508,6 @@ function createSuperDocUI(options) {
|
|
|
5040
5508
|
settledResults.push(Promise.resolve(false));
|
|
5041
5509
|
}
|
|
5042
5510
|
};
|
|
5043
|
-
const doc = getDoc();
|
|
5044
5511
|
const defaultStyleId = getStyleCatalog().cache?.full.defaults.paragraphStyleId ?? DEFAULT_PARAGRAPH_STYLE_ID;
|
|
5045
5512
|
const rangeTarget = resolveInlineSelectionTarget(state.selection);
|
|
5046
5513
|
if (rangeTarget) {
|
|
@@ -5446,7 +5913,7 @@ function createSuperDocUI(options) {
|
|
|
5446
5913
|
const rows = Number(record.rows ?? record.rowCount);
|
|
5447
5914
|
const columns = Number(record.columns ?? record.cols ?? record.colCount);
|
|
5448
5915
|
if (!Number.isInteger(rows) || rows < 1 || !Number.isInteger(columns) || columns < 1) return false;
|
|
5449
|
-
const caret =
|
|
5916
|
+
const caret = collapsedTextAddressFromSelection(state.selection);
|
|
5450
5917
|
const at = caret && typeof caret.blockId === "string" ? {
|
|
5451
5918
|
kind: "inParagraph",
|
|
5452
5919
|
target: paragraphTarget(caret.blockId),
|
|
@@ -5595,20 +6062,42 @@ function createSuperDocUI(options) {
|
|
|
5595
6062
|
const route = descriptor.docRoute;
|
|
5596
6063
|
if (!route) return false;
|
|
5597
6064
|
if (descriptor.mutates && readDocumentMode() === "viewing") return false;
|
|
5598
|
-
const
|
|
6065
|
+
const doc = getDoc();
|
|
6066
|
+
const op = resolveDocOperation(doc, route);
|
|
5599
6067
|
if (!op) return false;
|
|
5600
6068
|
if (descriptor.inline) {
|
|
6069
|
+
const { lockModesById } = computeActiveContentControlLockModes(doc?.contentControls, state.selection);
|
|
6070
|
+
if (contentControlLockReason(lockModesById)) return false;
|
|
5601
6071
|
const target = resolveInlineSelectionTarget(state.selection);
|
|
5602
6072
|
if (!target) {
|
|
5603
6073
|
if (state.selection.empty === false) return settleCommandExecution(false);
|
|
5604
6074
|
return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
|
|
5605
6075
|
}
|
|
5606
|
-
const active =
|
|
6076
|
+
const active = commandActiveState(descriptor, doc, state.selection);
|
|
5607
6077
|
const input = buildInlineFormatInput(descriptor.inline, target, normalized, active);
|
|
5608
6078
|
if (!input) return false;
|
|
6079
|
+
let releaseScheduledMutation = null;
|
|
5609
6080
|
try {
|
|
5610
|
-
|
|
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;
|
|
5611
6099
|
} catch {
|
|
6100
|
+
releaseScheduledMutation?.();
|
|
5612
6101
|
return false;
|
|
5613
6102
|
}
|
|
5614
6103
|
}
|
|
@@ -5637,6 +6126,15 @@ function createSuperDocUI(options) {
|
|
|
5637
6126
|
recompute();
|
|
5638
6127
|
};
|
|
5639
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 : [];
|
|
5640
6138
|
const DOUBLE_CLICK_MS = 500;
|
|
5641
6139
|
const now = Date.now();
|
|
5642
6140
|
if (painter.mode !== "idle") {
|
|
@@ -5659,11 +6157,9 @@ function createSuperDocUI(options) {
|
|
|
5659
6157
|
...painter,
|
|
5660
6158
|
lastClickAt: now
|
|
5661
6159
|
};
|
|
5662
|
-
const captureSlice =
|
|
6160
|
+
const captureSlice = await readSelectionInfoFresh();
|
|
5663
6161
|
const sourceKey = `${selectionKey(captureSlice)}:${contentToken()}`;
|
|
5664
|
-
const
|
|
5665
|
-
const sel = readSelectionInfoLive().value;
|
|
5666
|
-
const painterSnapshot = await captureFormatPainter(sel, captureSlice, captureProjected);
|
|
6162
|
+
const painterSnapshot = await captureFormatPainter(null, captureSlice, sourceKey && sourceKey === frozenProjectedValuesKey ? frozenProjectedValues : void 0, visibleActiveMarks);
|
|
5667
6163
|
if (captureEpoch !== painterCaptureEpoch) return true;
|
|
5668
6164
|
painter = {
|
|
5669
6165
|
...painter,
|
|
@@ -5676,7 +6172,7 @@ function createSuperDocUI(options) {
|
|
|
5676
6172
|
recompute();
|
|
5677
6173
|
return true;
|
|
5678
6174
|
};
|
|
5679
|
-
const captureFormatPainter = async (selection$1, captureSlice, captureProjected) => {
|
|
6175
|
+
const captureFormatPainter = async (selection$1, captureSlice, captureProjected, visibleActiveMarks = []) => {
|
|
5680
6176
|
const selTarget = captureSlice?.target ?? selection$1?.target;
|
|
5681
6177
|
if (!selTarget || selTarget["kind"] !== "text") return null;
|
|
5682
6178
|
const segments = Array.isArray(selTarget["segments"]) ? selTarget["segments"] : [];
|
|
@@ -5700,8 +6196,8 @@ function createSuperDocUI(options) {
|
|
|
5700
6196
|
underline: "underline",
|
|
5701
6197
|
strikethrough: "strike"
|
|
5702
6198
|
};
|
|
5703
|
-
const activeMarks = captureSlice?.activeMarks ?? (Array.isArray(selection$1?.["activeMarks"]) ? selection$1["activeMarks"] : []);
|
|
5704
|
-
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;
|
|
5705
6201
|
const projectedInlineValues = captureProjected ?? await readFormatPainterInlineValues(captureSlice ?? selectionSliceFromInfo(selection$1, "ready"));
|
|
5706
6202
|
if (projectedInlineValues.fontFamily) inlinePatch["fontFamily"] = projectedInlineValues.fontFamily;
|
|
5707
6203
|
if (projectedInlineValues.color) inlinePatch["color"] = projectedInlineValues.color;
|
|
@@ -5855,6 +6351,12 @@ function createSuperDocUI(options) {
|
|
|
5855
6351
|
}
|
|
5856
6352
|
const doc = getDoc();
|
|
5857
6353
|
if (!doc) return;
|
|
6354
|
+
const targetSelection = selectionSliceFromInfo(selection$1, "ready");
|
|
6355
|
+
const { lockModesById } = computeActiveContentControlLockModes(doc.contentControls, targetSelection);
|
|
6356
|
+
if (contentControlLockReason(lockModesById)) {
|
|
6357
|
+
recompute();
|
|
6358
|
+
return;
|
|
6359
|
+
}
|
|
5858
6360
|
const selTarget = selection$1["target"];
|
|
5859
6361
|
const selectionTarget = selection$1["selectionTarget"];
|
|
5860
6362
|
if (!(selection$1["empty"] === true) && Object.keys(snap.inline).length > 0) {
|
|
@@ -5954,12 +6456,15 @@ function createSuperDocUI(options) {
|
|
|
5954
6456
|
};
|
|
5955
6457
|
const executeCommandAsync = (id, payload, context) => {
|
|
5956
6458
|
const prepared = prepareCommandSelectionAsync(id, payload);
|
|
5957
|
-
|
|
6459
|
+
const settlement = prepared ? prepared.then(() => {
|
|
5958
6460
|
executeCommand(id, payload, context);
|
|
5959
6461
|
return lastCommandSettlement;
|
|
5960
|
-
})
|
|
5961
|
-
|
|
5962
|
-
|
|
6462
|
+
}) : (() => {
|
|
6463
|
+
executeCommand(id, payload, context);
|
|
6464
|
+
return lastCommandSettlement;
|
|
6465
|
+
})();
|
|
6466
|
+
if (getCommandDescriptor(id)?.inline?.kind === "toggle") lastInlineToggleCommandSettlement = settlement;
|
|
6467
|
+
return settlement;
|
|
5963
6468
|
};
|
|
5964
6469
|
const makeCommandHandle = (id) => ({
|
|
5965
6470
|
id,
|
|
@@ -6426,6 +6931,7 @@ function createSuperDocUI(options) {
|
|
|
6426
6931
|
setActive: (input) => {
|
|
6427
6932
|
if (input == null) {
|
|
6428
6933
|
setExplicitActiveChange(null);
|
|
6934
|
+
mirrorTrackedChangeFocusToHost(null);
|
|
6429
6935
|
recompute();
|
|
6430
6936
|
return true;
|
|
6431
6937
|
}
|
|
@@ -6435,6 +6941,7 @@ function createSuperDocUI(options) {
|
|
|
6435
6941
|
story: input.story
|
|
6436
6942
|
};
|
|
6437
6943
|
if (typeof requested.id !== "string" || requested.id.length === 0) return false;
|
|
6944
|
+
const paintedEntityId = requested.id;
|
|
6438
6945
|
if (requested.story) {
|
|
6439
6946
|
const allStoryItems = readAllStoryTrackChanges();
|
|
6440
6947
|
if (!allStoryItems) return false;
|
|
@@ -6447,7 +6954,9 @@ function createSuperDocUI(options) {
|
|
|
6447
6954
|
if (!bodyItems.some((item) => readEntityId(item) === publicId)) return false;
|
|
6448
6955
|
requested.id = publicId;
|
|
6449
6956
|
}
|
|
6957
|
+
if (paintedEntityId !== requested.id) requested.paintedEntityId = paintedEntityId;
|
|
6450
6958
|
setExplicitActiveChange(requested);
|
|
6959
|
+
mirrorTrackedChangeFocusToHost(requested);
|
|
6451
6960
|
recompute();
|
|
6452
6961
|
return true;
|
|
6453
6962
|
},
|
|
@@ -6466,10 +6975,11 @@ function createSuperDocUI(options) {
|
|
|
6466
6975
|
reason: SUPERDOC_UI_REASONS.targetUnresolved
|
|
6467
6976
|
};
|
|
6468
6977
|
const previousActiveChange = explicitActiveChange;
|
|
6469
|
-
|
|
6978
|
+
const requestedActiveChange = story ? {
|
|
6470
6979
|
id: changeId,
|
|
6471
6980
|
story
|
|
6472
|
-
} : { id: changeId }
|
|
6981
|
+
} : { id: changeId };
|
|
6982
|
+
setExplicitActiveChange(requestedActiveChange);
|
|
6473
6983
|
recompute();
|
|
6474
6984
|
const optimisticActiveChange = explicitActiveChange;
|
|
6475
6985
|
const optimisticRevision = explicitActiveChangeRevision;
|
|
@@ -6485,6 +6995,7 @@ function createSuperDocUI(options) {
|
|
|
6485
6995
|
ok: false
|
|
6486
6996
|
};
|
|
6487
6997
|
if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) rollback();
|
|
6998
|
+
else mirrorTrackedChangeFocusToHost(requestedActiveChange);
|
|
6488
6999
|
return result;
|
|
6489
7000
|
}
|
|
6490
7001
|
};
|
|
@@ -6511,12 +7022,13 @@ function createSuperDocUI(options) {
|
|
|
6511
7022
|
const tcApi = getDoc()?.trackChanges;
|
|
6512
7023
|
const isAllTarget = target.scope === "all";
|
|
6513
7024
|
const isRangeTarget = target.kind === "range";
|
|
7025
|
+
const isMultiIdTarget = target.kind === "ids";
|
|
6514
7026
|
if (bulkTrackDecisionBlockedReason({
|
|
6515
7027
|
kind,
|
|
6516
7028
|
scope: isAllTarget ? "all" : "id"
|
|
6517
7029
|
}, tcApi)) return false;
|
|
6518
7030
|
const legacyName = isAllTarget ? `${kind}All` : kind;
|
|
6519
|
-
const op = !isRangeTarget && !story && tcApi && typeof tcApi[legacyName] === "function" ? tcApi[legacyName] : null;
|
|
7031
|
+
const op = !isRangeTarget && !isMultiIdTarget && !story && tcApi && typeof tcApi[legacyName] === "function" ? tcApi[legacyName] : null;
|
|
6520
7032
|
const decide = tcApi && typeof tcApi.decide === "function" ? tcApi.decide : null;
|
|
6521
7033
|
if (!op && !decide) return false;
|
|
6522
7034
|
return op ? op.call(tcApi, changeId ?? {}) : decide.call(tcApi, {
|
|
@@ -6524,23 +7036,6 @@ function createSuperDocUI(options) {
|
|
|
6524
7036
|
target
|
|
6525
7037
|
});
|
|
6526
7038
|
};
|
|
6527
|
-
const executeTrackDecisionTargets = (kind, changeIds) => {
|
|
6528
|
-
const immediateResults = [];
|
|
6529
|
-
const settledResults = [];
|
|
6530
|
-
for (const changeId of changeIds) try {
|
|
6531
|
-
const result = settleOperationResult(callTrackDecisionTarget(kind, {
|
|
6532
|
-
kind: "id",
|
|
6533
|
-
id: changeId
|
|
6534
|
-
}, changeId));
|
|
6535
|
-
immediateResults.push(result.immediate);
|
|
6536
|
-
settledResults.push(result.settled);
|
|
6537
|
-
} catch {
|
|
6538
|
-
immediateResults.push(false);
|
|
6539
|
-
settledResults.push(Promise.resolve(false));
|
|
6540
|
-
}
|
|
6541
|
-
pendingCommandSettlement = Promise.all(settledResults).then((results) => combineCommandResults(results));
|
|
6542
|
-
return settleCommandExecution(combineCommandResults(immediateResults));
|
|
6543
|
-
};
|
|
6544
7039
|
const contentControlsSub = sliceHandle((s) => s.contentControls);
|
|
6545
7040
|
const findContentControl = (id) => {
|
|
6546
7041
|
return contentControlsSub.get().items.find((item) => item?.id === id) ?? null;
|
|
@@ -7464,10 +7959,16 @@ function createSuperDocUI(options) {
|
|
|
7464
7959
|
if (disposed) return;
|
|
7465
7960
|
disposed = true;
|
|
7466
7961
|
reviewMutationReconciler.reset();
|
|
7962
|
+
clearAuthoritativeTrackChangesRetry();
|
|
7467
7963
|
if (foregroundAsyncRetryTimer) {
|
|
7468
7964
|
clearTimeout(foregroundAsyncRetryTimer);
|
|
7469
7965
|
foregroundAsyncRetryTimer = null;
|
|
7470
7966
|
}
|
|
7967
|
+
if (asyncReadFailureRetryTimer) {
|
|
7968
|
+
clearTimeout(asyncReadFailureRetryTimer);
|
|
7969
|
+
asyncReadFailureRetryTimer = null;
|
|
7970
|
+
asyncReadFailureRetryAtMs = 0;
|
|
7971
|
+
}
|
|
7471
7972
|
if (pendingEffectiveInlineRead?.timer) {
|
|
7472
7973
|
clearTimeout(pendingEffectiveInlineRead.timer);
|
|
7473
7974
|
pendingEffectiveInlineRead.timer = null;
|