superdoc 2.4.0-next.20 → 2.4.0-next.22
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/{create-super-doc-ui-BbNbi0cj.es.js → create-super-doc-ui-CmOSA_lN.es.js} +253 -657
- package/dist/chunks/{create-super-doc-ui-CPuOlTWb.cjs → create-super-doc-ui-CqGmoMMA.cjs} +252 -662
- package/dist/collaboration-upgrade-engine.cjs +1 -1
- package/dist/collaboration-upgrade-engine.es.js +1 -1
- package/dist/document-api/src/citations/citations.types.d.ts +9 -0
- package/dist/document-api/src/types/receipt.d.ts +9 -5
- package/dist/layout-engine/contracts/src/incremental-dependency.d.ts +22 -2
- package/dist/layout-engine/contracts/src/index.d.ts +74 -2
- package/dist/layout-engine/contracts/src/inline-box.d.ts +19 -0
- package/dist/layout-engine/contracts/src/paragraph-visible-text.d.ts +19 -0
- package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +2 -1
- package/dist/layout-engine/layout-bridge/src/inline-box-key.d.ts +3 -0
- package/dist/layout-engine/layout-bridge/src/run-visual-marks.d.ts +2 -0
- package/dist/layout-engine/layout-bridge/src/text-measurement.d.ts +0 -12
- package/dist/layout-engine/painters/dom/src/runs/inline-box.d.ts +8 -0
- 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 +93 -92
- package/dist/style.layered.css +93 -92
- package/dist/superdoc/src/components/CommentsLayer/CommentDialog.vue.d.ts +2 -0
- package/dist/superdoc/src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue.d.ts +16 -0
- package/dist/superdoc/src/components/CommentsLayer/commentsList/commentsList.vue.d.ts +3 -1
- package/dist/superdoc/src/components/CommentsLayer/commentsList/super-comments-list.d.ts +12 -0
- package/dist/superdoc/src/core/SuperDoc.d.ts +6 -7
- package/dist/superdoc/src/core/extensions/index.d.ts +1 -1
- package/dist/superdoc/src/core/extensions/types.d.ts +63 -5
- package/dist/superdoc/src/core/types/index.d.ts +19 -0
- package/dist/superdoc/src/core/v2-integration/v2-integration.d.ts +3 -3
- package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +3 -5
- package/dist/superdoc/src/public/index.d.cts +6 -0
- package/dist/superdoc/src/public/index.d.ts +3 -0
- package/dist/superdoc/src/public/ui/types.d.ts +5 -5
- package/dist/superdoc/src/stores/comments-store.d.ts +81 -78
- package/dist/superdoc/src/stores/superdoc-store.d.ts +243 -234
- package/dist/superdoc.cjs +660 -857
- package/dist/superdoc.es.js +648 -846
- package/dist-cdn/style.layered.css +1 -1
- package/dist-cdn/superdoc.min.css +1 -1
- package/dist-cdn/superdoc.min.js +36 -36
- package/package.json +2 -2
- package/dist/superdoc/src/helpers/v2-remote-review-hydration.d.ts +0 -12
- package/dist/superdoc/src/helpers/v2-review-mutation-reconciler.d.ts +0 -19
- package/dist/superdoc/src/helpers/v2-typing-review-hydration.d.ts +0 -16
package/dist/chunks/{create-super-doc-ui-BbNbi0cj.es.js → create-super-doc-ui-CmOSA_lN.es.js}
RENAMED
|
@@ -423,6 +423,7 @@ Object.freeze([
|
|
|
423
423
|
"furniture-page-tokens",
|
|
424
424
|
"non-balanceable-multi-column-sections",
|
|
425
425
|
"body-anchored-objects",
|
|
426
|
+
"non-flowing-page-relative-body-anchors",
|
|
426
427
|
"footnotes",
|
|
427
428
|
"page-references",
|
|
428
429
|
"keep-constraints",
|
|
@@ -435,11 +436,9 @@ Object.freeze([
|
|
|
435
436
|
* Classify the tracked-change identities carried by a v2 mutation event.
|
|
436
437
|
*
|
|
437
438
|
* Receipt entities are the durable invalidation contract between the document
|
|
438
|
-
* kernel and derived review UI. Consumers
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
* different: undo/redo can restore many identities at once, so consumers use
|
|
442
|
-
* one authoritative catalog after render instead of issuing one read per id.
|
|
439
|
+
* kernel and derived review UI. Consumers apply removals/remaps locally; the
|
|
440
|
+
* next committed window supplies surviving or restored rows. History follows
|
|
441
|
+
* the same bounded path and never enumerates invisible review identities.
|
|
443
442
|
*/
|
|
444
443
|
function getV2TrackedChangeMutationImpact(event) {
|
|
445
444
|
if (event?.type !== "mutation:committed") return null;
|
|
@@ -479,7 +478,7 @@ function getV2TrackedChangeMutationImpact(event) {
|
|
|
479
478
|
upsertIds,
|
|
480
479
|
removedIds,
|
|
481
480
|
remappedPairs,
|
|
482
|
-
reconcileMode:
|
|
481
|
+
reconcileMode: "targeted",
|
|
483
482
|
...allResolved ? { allResolved } : {}
|
|
484
483
|
};
|
|
485
484
|
}
|
|
@@ -489,280 +488,6 @@ function readAllResolvedFact(event, receipt) {
|
|
|
489
488
|
return fact;
|
|
490
489
|
}
|
|
491
490
|
//#endregion
|
|
492
|
-
//#region src/helpers/v2-review-mutation-reconciler.js
|
|
493
|
-
var normalizeIds = (values) => new Set(Array.from(values ?? []).filter((value) => typeof value === "string" && value.length > 0));
|
|
494
|
-
var itemId = (item) => {
|
|
495
|
-
if (typeof item?.id === "string" && item.id.length > 0) return item.id;
|
|
496
|
-
if (typeof item?.changeId === "string" && item.changeId.length > 0) return item.changeId;
|
|
497
|
-
if (typeof item?.address?.entityId === "string" && item.address.entityId.length > 0) return item.address.entityId;
|
|
498
|
-
return null;
|
|
499
|
-
};
|
|
500
|
-
var contextMatches = (left, right) => left?.adapter === right?.adapter && left?.documentId === right?.documentId && left?.editor === right?.editor && left?.reconcileToken === right?.reconcileToken;
|
|
501
|
-
/**
|
|
502
|
-
* Reconcile tracked changes only after their canonical render has made the
|
|
503
|
-
* read model observable. Command receipts use narrow reads; history results
|
|
504
|
-
* use one authoritative catalog because undo/redo can restore hundreds of
|
|
505
|
-
* identities in one transaction. An absent history identity only converges
|
|
506
|
-
* when both catalog and source coverage are explicitly complete.
|
|
507
|
-
*/
|
|
508
|
-
function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onReconciled }) {
|
|
509
|
-
const pendingUpserts = /* @__PURE__ */ new Map();
|
|
510
|
-
let pendingAllResolved = null;
|
|
511
|
-
let sequence = 0;
|
|
512
|
-
let generation = 0;
|
|
513
|
-
let flushInFlight = null;
|
|
514
|
-
let flushRequested = false;
|
|
515
|
-
const settlementWaiters = [];
|
|
516
|
-
const resolveSettlementsThrough = (version, value) => {
|
|
517
|
-
for (let index = settlementWaiters.length - 1; index >= 0; index -= 1) {
|
|
518
|
-
const waiter = settlementWaiters[index];
|
|
519
|
-
if (waiter.generation !== generation || waiter.version > version) continue;
|
|
520
|
-
settlementWaiters.splice(index, 1);
|
|
521
|
-
waiter.resolve(value === true && waiter.requiresAllResolved ? pendingAllResolved == null : value);
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
const isCurrent = (capturedGeneration, context) => generation === capturedGeneration && contextMatches(context, getContext());
|
|
525
|
-
const clearResolved = (ids, snapshot) => {
|
|
526
|
-
const clearedIds = /* @__PURE__ */ new Set();
|
|
527
|
-
for (const id of ids) {
|
|
528
|
-
if (pendingUpserts.get(id) !== snapshot.get(id)) continue;
|
|
529
|
-
pendingUpserts.delete(id);
|
|
530
|
-
clearedIds.add(id);
|
|
531
|
-
}
|
|
532
|
-
return clearedIds;
|
|
533
|
-
};
|
|
534
|
-
const readySnapshot = (reconcileMode) => new Map([...pendingUpserts].filter(([, entry]) => entry.ready === true && entry.reconcileMode === reconcileMode));
|
|
535
|
-
const markRenderGatedReady = () => {
|
|
536
|
-
let changed = false;
|
|
537
|
-
if (pendingAllResolved?.gate === "render" && !pendingAllResolved.ready) {
|
|
538
|
-
pendingAllResolved.ready = true;
|
|
539
|
-
changed = true;
|
|
540
|
-
}
|
|
541
|
-
for (const entry of pendingUpserts.values()) {
|
|
542
|
-
if (entry.gate !== "render" || entry.ready) continue;
|
|
543
|
-
entry.ready = true;
|
|
544
|
-
changed = true;
|
|
545
|
-
}
|
|
546
|
-
return changed;
|
|
547
|
-
};
|
|
548
|
-
const markEntriesReadyThrough = (maxVersion) => {
|
|
549
|
-
let changed = false;
|
|
550
|
-
if (pendingAllResolved && pendingAllResolved.version <= maxVersion && !pendingAllResolved.ready) {
|
|
551
|
-
pendingAllResolved.ready = true;
|
|
552
|
-
changed = true;
|
|
553
|
-
}
|
|
554
|
-
for (const entry of pendingUpserts.values()) {
|
|
555
|
-
if (entry.version > maxVersion || entry.ready) continue;
|
|
556
|
-
entry.ready = true;
|
|
557
|
-
changed = true;
|
|
558
|
-
}
|
|
559
|
-
return changed;
|
|
560
|
-
};
|
|
561
|
-
const notifyReconciled = async (context, ids, result, allowEmpty = false) => {
|
|
562
|
-
if (!allowEmpty && ids.size === 0 || typeof onReconciled !== "function") return;
|
|
563
|
-
try {
|
|
564
|
-
await onReconciled(context, ids, result);
|
|
565
|
-
} catch {}
|
|
566
|
-
};
|
|
567
|
-
const idsFromResult = (result) => normalizeIds(result?.resolvedIds ?? (result?.items ?? []).map(itemId).filter(Boolean));
|
|
568
|
-
const reconcileTargetedSnapshot = async (context, snapshot, capturedGeneration) => {
|
|
569
|
-
const result = await reconcile(context, {
|
|
570
|
-
upsertIds: new Set(snapshot.keys()),
|
|
571
|
-
removedIds: /* @__PURE__ */ new Set(),
|
|
572
|
-
reconcileMode: "targeted"
|
|
573
|
-
});
|
|
574
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
575
|
-
const resolvedIds = idsFromResult(result);
|
|
576
|
-
const clearedIds = clearResolved(resolvedIds, snapshot);
|
|
577
|
-
await notifyReconciled(context, clearedIds, result);
|
|
578
|
-
if (new Set([...snapshot].filter(([id, version]) => pendingUpserts.get(id) === version).map(([id]) => id)).size === 0) return;
|
|
579
|
-
const fallback = await hydrate(context, {
|
|
580
|
-
trackedChangesListMode: "interaction-prime",
|
|
581
|
-
refreshReason: "mutation-render-reconcile",
|
|
582
|
-
blocking: false
|
|
583
|
-
});
|
|
584
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
585
|
-
const fallbackResolvedIds = idsFromResult(fallback);
|
|
586
|
-
const fallbackClearedIds = clearResolved(fallbackResolvedIds, snapshot);
|
|
587
|
-
await notifyReconciled(context, fallbackClearedIds, fallback);
|
|
588
|
-
};
|
|
589
|
-
const reconcileAuthoritativeSnapshot = async (context, snapshot, capturedGeneration) => {
|
|
590
|
-
const result = await hydrate(context, {
|
|
591
|
-
trackedChangesListMode: "all",
|
|
592
|
-
refreshReason: "mutation-history-render-reconcile",
|
|
593
|
-
blocking: true
|
|
594
|
-
});
|
|
595
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
596
|
-
if (![...snapshot].some(([id, entry]) => pendingUpserts.get(id) === entry)) return;
|
|
597
|
-
const transportSucceeded = result?.ok === true;
|
|
598
|
-
const catalogIsAuthoritative = transportSucceeded && result?.complete === true && result?.sourceCoverageComplete === true;
|
|
599
|
-
const returnedIds = transportSucceeded ? idsFromResult(result) : /* @__PURE__ */ new Set();
|
|
600
|
-
const clearedIds = catalogIsAuthoritative ? clearResolved(new Set(snapshot.keys()), snapshot) : /* @__PURE__ */ new Set();
|
|
601
|
-
await notifyReconciled(context, catalogIsAuthoritative ? clearedIds : returnedIds, {
|
|
602
|
-
...result,
|
|
603
|
-
reconcileMode: "authoritative"
|
|
604
|
-
}, true);
|
|
605
|
-
};
|
|
606
|
-
const flush = () => {
|
|
607
|
-
if (flushInFlight) return flushInFlight;
|
|
608
|
-
const allResolvedSnapshot = pendingAllResolved?.ready ? pendingAllResolved : null;
|
|
609
|
-
const targetedSnapshot = readySnapshot("targeted");
|
|
610
|
-
const authoritativeSnapshot = readySnapshot("authoritative");
|
|
611
|
-
if (!allResolvedSnapshot && targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
|
|
612
|
-
const context = getContext();
|
|
613
|
-
if (!context?.adapter) return null;
|
|
614
|
-
flushRequested = false;
|
|
615
|
-
const capturedGeneration = generation;
|
|
616
|
-
flushInFlight = Promise.resolve().then(async () => {
|
|
617
|
-
if (allResolvedSnapshot) {
|
|
618
|
-
let result = null;
|
|
619
|
-
try {
|
|
620
|
-
result = await reconcile(context, {
|
|
621
|
-
upsertIds: /* @__PURE__ */ new Set(),
|
|
622
|
-
removedIds: /* @__PURE__ */ new Set(),
|
|
623
|
-
allResolved: allResolvedSnapshot.fact,
|
|
624
|
-
reconcileMode: "targeted"
|
|
625
|
-
});
|
|
626
|
-
} catch {}
|
|
627
|
-
if (!isCurrent(capturedGeneration, context)) return;
|
|
628
|
-
if (result?.ok === true) {
|
|
629
|
-
if (pendingAllResolved === allResolvedSnapshot) pendingAllResolved = null;
|
|
630
|
-
await notifyReconciled(context, /* @__PURE__ */ new Set(), result, true);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
if (authoritativeSnapshot.size > 0) await reconcileAuthoritativeSnapshot(context, authoritativeSnapshot, capturedGeneration).catch(() => void 0);
|
|
634
|
-
if (targetedSnapshot.size > 0) await reconcileTargetedSnapshot(context, targetedSnapshot, capturedGeneration).catch(() => void 0);
|
|
635
|
-
}).catch(() => {}).finally(() => {
|
|
636
|
-
flushInFlight = null;
|
|
637
|
-
if (flushRequested) flush();
|
|
638
|
-
});
|
|
639
|
-
return flushInFlight;
|
|
640
|
-
};
|
|
641
|
-
const enqueueWithGate = (impact, gate) => {
|
|
642
|
-
if (!impact) return;
|
|
643
|
-
if (impact.allResolved) {
|
|
644
|
-
pendingUpserts.clear();
|
|
645
|
-
pendingAllResolved = {
|
|
646
|
-
fact: impact.allResolved,
|
|
647
|
-
version: ++sequence,
|
|
648
|
-
gate,
|
|
649
|
-
ready: false
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
|
|
653
|
-
const removedIds = normalizeIds(impact.removedIds);
|
|
654
|
-
const upsertIds = normalizeIds(impact.upsertIds);
|
|
655
|
-
for (const id of removedIds) {
|
|
656
|
-
const existing = pendingUpserts.get(id);
|
|
657
|
-
if (existing?.reconcileMode === "authoritative") pendingUpserts.set(id, {
|
|
658
|
-
...existing,
|
|
659
|
-
version: ++sequence,
|
|
660
|
-
gate,
|
|
661
|
-
ready: false
|
|
662
|
-
});
|
|
663
|
-
else pendingUpserts.delete(id);
|
|
664
|
-
}
|
|
665
|
-
const context = getContext();
|
|
666
|
-
if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter && !impact.allResolved) try {
|
|
667
|
-
Promise.resolve(reconcile(context, {
|
|
668
|
-
upsertIds: /* @__PURE__ */ new Set(),
|
|
669
|
-
removedIds
|
|
670
|
-
})).catch(() => void 0);
|
|
671
|
-
} catch {}
|
|
672
|
-
for (const id of upsertIds) if (!removedIds.has(id)) {
|
|
673
|
-
const existing = pendingUpserts.get(id);
|
|
674
|
-
pendingUpserts.set(id, {
|
|
675
|
-
version: ++sequence,
|
|
676
|
-
gate,
|
|
677
|
-
ready: false,
|
|
678
|
-
reconcileMode: reconcileMode === "authoritative" || existing?.reconcileMode === "authoritative" ? "authoritative" : "targeted"
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
const enqueue = (impact) => enqueueWithGate(impact, "render");
|
|
683
|
-
const onRender = (supersedesInFlight = false) => {
|
|
684
|
-
const madeReady = markRenderGatedReady();
|
|
685
|
-
if (flushInFlight && (madeReady || supersedesInFlight)) flushRequested = true;
|
|
686
|
-
flush();
|
|
687
|
-
};
|
|
688
|
-
return {
|
|
689
|
-
enqueue,
|
|
690
|
-
enqueueAfterPaint(impact, waitForPaint) {
|
|
691
|
-
if (!impact || typeof waitForPaint !== "function") {
|
|
692
|
-
enqueue(impact);
|
|
693
|
-
return Promise.resolve(false);
|
|
694
|
-
}
|
|
695
|
-
enqueueWithGate(impact, "exact-paint");
|
|
696
|
-
const context = getContext();
|
|
697
|
-
const capturedGeneration = generation;
|
|
698
|
-
const capturedSequence = sequence;
|
|
699
|
-
if (!context?.adapter) {
|
|
700
|
-
markEntriesReadyThrough(capturedSequence);
|
|
701
|
-
return Promise.resolve(false);
|
|
702
|
-
}
|
|
703
|
-
const completion = new Promise((resolve) => {
|
|
704
|
-
settlementWaiters.push({
|
|
705
|
-
generation: capturedGeneration,
|
|
706
|
-
version: capturedSequence,
|
|
707
|
-
requiresAllResolved: Boolean(impact.allResolved),
|
|
708
|
-
resolve
|
|
709
|
-
});
|
|
710
|
-
});
|
|
711
|
-
const impactedIds = /* @__PURE__ */ new Set([...normalizeIds(impact.upsertIds), ...normalizeIds(impact.removedIds)]);
|
|
712
|
-
const capturedEntries = new Map([...impactedIds].flatMap((id) => {
|
|
713
|
-
const entry = pendingUpserts.get(id);
|
|
714
|
-
return entry ? [[id, entry]] : [];
|
|
715
|
-
}));
|
|
716
|
-
Promise.resolve().then(() => waitForPaint()).then(async () => {
|
|
717
|
-
if (!isCurrent(capturedGeneration, context)) {
|
|
718
|
-
resolveSettlementsThrough(capturedSequence, false);
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
let madeReady = markRenderGatedReady();
|
|
722
|
-
if (markEntriesReadyThrough(capturedSequence)) madeReady = true;
|
|
723
|
-
for (const [id, entry] of capturedEntries) {
|
|
724
|
-
if (pendingUpserts.get(id) !== entry) continue;
|
|
725
|
-
if (!entry.ready) madeReady = true;
|
|
726
|
-
entry.ready = true;
|
|
727
|
-
}
|
|
728
|
-
if (flushInFlight && (madeReady || capturedSequence > 0)) flushRequested = true;
|
|
729
|
-
const preceding = flushInFlight;
|
|
730
|
-
const pending = flush();
|
|
731
|
-
if (pending) await pending;
|
|
732
|
-
if (preceding) {
|
|
733
|
-
const followUp = flushInFlight;
|
|
734
|
-
if (followUp && followUp !== preceding) await followUp;
|
|
735
|
-
}
|
|
736
|
-
if (!isCurrent(capturedGeneration, context)) {
|
|
737
|
-
resolveSettlementsThrough(capturedSequence, false);
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
resolveSettlementsThrough(capturedSequence, true);
|
|
741
|
-
}).catch(() => {
|
|
742
|
-
if (!isCurrent(capturedGeneration, context)) {
|
|
743
|
-
resolveSettlementsThrough(capturedSequence, false);
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
for (const [id, entry] of capturedEntries) if (pendingUpserts.get(id) === entry) entry.gate = "render";
|
|
747
|
-
if (pendingAllResolved?.gate === "exact-paint") pendingAllResolved.gate = "render";
|
|
748
|
-
resolveSettlementsThrough(capturedSequence, false);
|
|
749
|
-
});
|
|
750
|
-
return completion;
|
|
751
|
-
},
|
|
752
|
-
onRender,
|
|
753
|
-
reset() {
|
|
754
|
-
for (const waiter of settlementWaiters.splice(0)) waiter.resolve(false);
|
|
755
|
-
generation += 1;
|
|
756
|
-
pendingUpserts.clear();
|
|
757
|
-
pendingAllResolved = null;
|
|
758
|
-
flushRequested = false;
|
|
759
|
-
},
|
|
760
|
-
getPendingIds() {
|
|
761
|
-
return new Set(pendingUpserts.keys());
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
//#endregion
|
|
766
491
|
//#region src/public/ui/reasons.ts
|
|
767
492
|
/**
|
|
768
493
|
* Stable public reason taxonomy for the v2-native `superdoc/ui` controller.
|
|
@@ -1836,6 +1561,17 @@ function collectEntityHitsFromChain(start, stopAt) {
|
|
|
1836
1561
|
for (const hit of pendingStory) hit.story = layoutStory;
|
|
1837
1562
|
pendingStory = [];
|
|
1838
1563
|
}
|
|
1564
|
+
const citationId = node.getAttribute("data-citation-id");
|
|
1565
|
+
if (citationId) {
|
|
1566
|
+
const key = `citation:${citationId}`;
|
|
1567
|
+
if (!seen.has(key)) {
|
|
1568
|
+
seen.add(key);
|
|
1569
|
+
hits.push({
|
|
1570
|
+
type: "citation",
|
|
1571
|
+
id: citationId
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1839
1575
|
const commentIds = node.getAttribute("data-comment-ids");
|
|
1840
1576
|
if (commentIds) for (const id of parseCommaSeparatedIds(commentIds)) {
|
|
1841
1577
|
const key = `comment:${id}`;
|
|
@@ -2401,22 +2137,24 @@ function isHostGeometryTarget(target) {
|
|
|
2401
2137
|
* address lets the host prefer the exact painted review carrier after the
|
|
2402
2138
|
* containing block has been materialized.
|
|
2403
2139
|
*/
|
|
2404
|
-
function readTrackedChangeNavigationTarget(row) {
|
|
2140
|
+
function readTrackedChangeNavigationTarget(row, options) {
|
|
2405
2141
|
if (!row || typeof row !== "object") return null;
|
|
2406
2142
|
const target = readEntityTarget(row);
|
|
2407
|
-
if (isHostGeometryTarget(target)) return target;
|
|
2408
2143
|
const record = row;
|
|
2409
2144
|
const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
|
|
2410
|
-
if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0)
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2145
|
+
if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) {
|
|
2146
|
+
const stableBlockTarget = {
|
|
2147
|
+
kind: "text",
|
|
2148
|
+
blockId: navigationTarget.blockId,
|
|
2149
|
+
range: {
|
|
2150
|
+
start: 0,
|
|
2151
|
+
end: 0
|
|
2152
|
+
},
|
|
2153
|
+
...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
|
|
2154
|
+
...record.address && typeof record.address === "object" ? { address: record.address } : {}
|
|
2155
|
+
};
|
|
2156
|
+
if (options?.preferStableBlock || !isHostGeometryTarget(target)) return stableBlockTarget;
|
|
2157
|
+
}
|
|
2420
2158
|
return target;
|
|
2421
2159
|
}
|
|
2422
2160
|
function storyLocatorSignature(story) {
|
|
@@ -3731,8 +3469,8 @@ function createSuperDocUI(options) {
|
|
|
3731
3469
|
const getV2ReviewWindowSource = () => {
|
|
3732
3470
|
const editor = getEditor();
|
|
3733
3471
|
if (editor?.editorVersion !== 2) return null;
|
|
3734
|
-
const
|
|
3735
|
-
return
|
|
3472
|
+
const reviewWindow = editor.reviewWindow;
|
|
3473
|
+
return reviewWindow && typeof reviewWindow === "object" ? reviewWindow : null;
|
|
3736
3474
|
};
|
|
3737
3475
|
const getV2ReviewWindowSnapshot = () => {
|
|
3738
3476
|
const source = getV2ReviewWindowSource();
|
|
@@ -4011,7 +3749,6 @@ function createSuperDocUI(options) {
|
|
|
4011
3749
|
return `e${id}`;
|
|
4012
3750
|
};
|
|
4013
3751
|
let documentMutationRevision = 0;
|
|
4014
|
-
let reviewMutationToken = {};
|
|
4015
3752
|
const uiTrackedChangesCatalogHost = options.superdoc;
|
|
4016
3753
|
const uiTrackedChangesCatalogState = acquireSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost);
|
|
4017
3754
|
const supersedeUiTrackedChangesCatalogRead = () => {
|
|
@@ -4043,7 +3780,6 @@ function createSuperDocUI(options) {
|
|
|
4043
3780
|
return fallback();
|
|
4044
3781
|
}
|
|
4045
3782
|
if (uiTrackedChangesCatalogState.activeMutationTokens.size > 0) return Promise.reject(/* @__PURE__ */ new Error("ui-review-catalog-superseded"));
|
|
4046
|
-
if ((getEditor()?.reviewHydration?.getDiagnostics?.())?.inFlight === true) return Promise.reject(/* @__PURE__ */ new Error("shell-review-hydration-in-flight"));
|
|
4047
3783
|
const generation = uiTrackedChangesCatalogState.generation;
|
|
4048
3784
|
const existing = uiTrackedChangesCatalogState.inFlight;
|
|
4049
3785
|
if (existing?.generation === generation) return existing.promise;
|
|
@@ -4069,15 +3805,6 @@ function createSuperDocUI(options) {
|
|
|
4069
3805
|
});
|
|
4070
3806
|
return promise;
|
|
4071
3807
|
};
|
|
4072
|
-
let authoritativeTrackChangesPendingToken = null;
|
|
4073
|
-
let authoritativeTrackChangesRetryTimer = null;
|
|
4074
|
-
let authoritativeTrackChangesFailureCount = 0;
|
|
4075
|
-
const clearAuthoritativeTrackChangesRetry = () => {
|
|
4076
|
-
if (authoritativeTrackChangesRetryTimer) clearTimeout(authoritativeTrackChangesRetryTimer);
|
|
4077
|
-
authoritativeTrackChangesRetryTimer = null;
|
|
4078
|
-
authoritativeTrackChangesFailureCount = 0;
|
|
4079
|
-
};
|
|
4080
|
-
let scheduleAuthoritativeTrackChangesRetry = (_reconcileToken) => void 0;
|
|
4081
3808
|
let postDecisionTrackChangesToken = null;
|
|
4082
3809
|
let postDecisionTrackChangeIds = /* @__PURE__ */ new Set();
|
|
4083
3810
|
let allTrackedChangesResolvedToken = null;
|
|
@@ -4197,65 +3924,6 @@ function createSuperDocUI(options) {
|
|
|
4197
3924
|
publishPostDecisionTrackChanges(newlyRemoved, receipt);
|
|
4198
3925
|
};
|
|
4199
3926
|
const postDecisionTrackChangeIdsForToken = (token) => postDecisionTrackChangesToken === token && postDecisionTrackChangeIds.size > 0 ? postDecisionTrackChangeIds : null;
|
|
4200
|
-
/**
|
|
4201
|
-
* Merge a narrow tracked-change read into the two settled review caches.
|
|
4202
|
-
* Existing enriched adapter fields are retained while the fresh Document
|
|
4203
|
-
* API fields win. The entry keeps its original content token, so a row that
|
|
4204
|
-
* settles after the typing invalidation is truthfully served as stale rather
|
|
4205
|
-
* than discarded or mislabeled as a complete current-token catalog.
|
|
4206
|
-
*/
|
|
4207
|
-
const mergeTrackedChangeItemsIntoCache = (items, orderedItems = []) => {
|
|
4208
|
-
const projected = items.map(projectTrackChangesItem).filter((item) => item != null && readEntityId(item) != null);
|
|
4209
|
-
if (projected.length === 0) return;
|
|
4210
|
-
const orderedIds = orderedItems.map(projectTrackChangesItem).map((item) => item == null ? null : readEntityId(item)).filter((id) => id != null);
|
|
4211
|
-
for (const key of ["trackChanges", "trackChanges:all"]) {
|
|
4212
|
-
const entry = asyncReads.get(key);
|
|
4213
|
-
const nextValue = entry?.hasSettled && Array.isArray(entry.value) ? [...entry.value] : [];
|
|
4214
|
-
const insertedIds = /* @__PURE__ */ new Set();
|
|
4215
|
-
let changed = false;
|
|
4216
|
-
for (const fresh of projected) {
|
|
4217
|
-
const id = readEntityId(fresh);
|
|
4218
|
-
if (!id) continue;
|
|
4219
|
-
const index = nextValue.findIndex((candidate) => readEntityId(candidate) === id);
|
|
4220
|
-
if (index < 0) {
|
|
4221
|
-
nextValue.push(fresh);
|
|
4222
|
-
insertedIds.add(id);
|
|
4223
|
-
} else {
|
|
4224
|
-
const previous = nextValue[index];
|
|
4225
|
-
nextValue[index] = isLooseObject(previous) ? {
|
|
4226
|
-
...previous,
|
|
4227
|
-
...fresh
|
|
4228
|
-
} : fresh;
|
|
4229
|
-
}
|
|
4230
|
-
changed = true;
|
|
4231
|
-
}
|
|
4232
|
-
if (key === "trackChanges" && insertedIds.size > 0 && orderedIds.length > 0) {
|
|
4233
|
-
const placeableIds = new Set(orderedIds.filter((id) => insertedIds.has(id)));
|
|
4234
|
-
const insertedRows = new Map(nextValue.filter((item) => placeableIds.has(readEntityId(item) ?? "")).map((item) => [readEntityId(item), item]));
|
|
4235
|
-
const placedValue = nextValue.filter((item) => !placeableIds.has(readEntityId(item) ?? ""));
|
|
4236
|
-
for (const [orderIndex, id] of orderedIds.entries()) {
|
|
4237
|
-
if (!placeableIds.has(id)) continue;
|
|
4238
|
-
const item = insertedRows.get(id);
|
|
4239
|
-
if (!item) continue;
|
|
4240
|
-
const followingAnchor = orderedIds.slice(orderIndex + 1).map((candidateId) => placedValue.findIndex((candidate) => readEntityId(candidate) === candidateId)).find((index) => index >= 0);
|
|
4241
|
-
if (followingAnchor != null) {
|
|
4242
|
-
placedValue.splice(followingAnchor, 0, item);
|
|
4243
|
-
continue;
|
|
4244
|
-
}
|
|
4245
|
-
const precedingAnchor = orderedIds.slice(0, orderIndex).reverse().map((candidateId) => placedValue.findIndex((candidate) => readEntityId(candidate) === candidateId)).find((index) => index >= 0);
|
|
4246
|
-
placedValue.splice(precedingAnchor == null ? placedValue.length : precedingAnchor + 1, 0, item);
|
|
4247
|
-
}
|
|
4248
|
-
nextValue.splice(0, nextValue.length, ...placedValue);
|
|
4249
|
-
}
|
|
4250
|
-
if (!changed) continue;
|
|
4251
|
-
asyncReads.set(key, {
|
|
4252
|
-
token: entry?.token ?? null,
|
|
4253
|
-
value: nextValue,
|
|
4254
|
-
hasSettled: true,
|
|
4255
|
-
inflightToken: null
|
|
4256
|
-
});
|
|
4257
|
-
}
|
|
4258
|
-
};
|
|
4259
3927
|
const replaceTrackedChangeItemsInCache = (items) => {
|
|
4260
3928
|
const token = contentToken();
|
|
4261
3929
|
const suppressedIds = postDecisionTrackChangeIdsForToken(token);
|
|
@@ -4271,183 +3939,6 @@ function createSuperDocUI(options) {
|
|
|
4271
3939
|
inflightToken: null
|
|
4272
3940
|
});
|
|
4273
3941
|
};
|
|
4274
|
-
const hydrateTrackedChanges = async (context, options) => {
|
|
4275
|
-
try {
|
|
4276
|
-
const listTrackedChanges = context.v2TrackedChanges?.listTrackedChanges;
|
|
4277
|
-
if (typeof listTrackedChanges === "function") {
|
|
4278
|
-
const result = await Promise.resolve(listTrackedChanges.call(context.v2TrackedChanges, {
|
|
4279
|
-
mode: options.trackedChangesListMode,
|
|
4280
|
-
refreshReason: options.refreshReason,
|
|
4281
|
-
blocking: options.blocking,
|
|
4282
|
-
...options.targetIds?.length ? { targetIds: options.targetIds } : {}
|
|
4283
|
-
}));
|
|
4284
|
-
const ok = options.trackedChangesListMode === "all" && options.refreshReason === "mutation-history-render-reconcile" ? result?.ok === true : result?.ok !== false;
|
|
4285
|
-
return {
|
|
4286
|
-
ok,
|
|
4287
|
-
items: ok && Array.isArray(result?.items) ? result.items : [],
|
|
4288
|
-
...typeof result?.complete === "boolean" ? { complete: result.complete } : {},
|
|
4289
|
-
...typeof result?.sourceCoverageComplete === "boolean" ? { sourceCoverageComplete: result.sourceCoverageComplete } : {}
|
|
4290
|
-
};
|
|
4291
|
-
}
|
|
4292
|
-
const list = context.docTrackChanges?.list;
|
|
4293
|
-
if (typeof list !== "function") return {
|
|
4294
|
-
ok: false,
|
|
4295
|
-
items: []
|
|
4296
|
-
};
|
|
4297
|
-
const result = await Promise.resolve(list.call(context.docTrackChanges));
|
|
4298
|
-
return {
|
|
4299
|
-
ok: true,
|
|
4300
|
-
items: Array.isArray(result?.items) ? result.items : []
|
|
4301
|
-
};
|
|
4302
|
-
} catch (error) {
|
|
4303
|
-
return {
|
|
4304
|
-
ok: false,
|
|
4305
|
-
items: [],
|
|
4306
|
-
failures: [error]
|
|
4307
|
-
};
|
|
4308
|
-
}
|
|
4309
|
-
};
|
|
4310
|
-
/**
|
|
4311
|
-
* Read receipt-addressed review effects after their exact canonical paint.
|
|
4312
|
-
* The adapter's narrow get preserves its enriched row projection; the public
|
|
4313
|
-
* Document API is the fallback for hosts without that bridge method. Empty
|
|
4314
|
-
* successful reads remain unresolved and immediately use one bounded
|
|
4315
|
-
* interaction-prime list instead of waiting for the typing-idle catalog.
|
|
4316
|
-
*/
|
|
4317
|
-
const reviewMutationReconciler = createV2ReviewMutationReconciler({
|
|
4318
|
-
getContext: () => {
|
|
4319
|
-
const editor = getEditor();
|
|
4320
|
-
if (!editor) return null;
|
|
4321
|
-
const v2TrackedChanges = getV2TrackedChanges();
|
|
4322
|
-
const docTrackChanges = getDoc()?.trackChanges ?? null;
|
|
4323
|
-
const adapter = v2TrackedChanges ?? docTrackChanges;
|
|
4324
|
-
if (!adapter) return null;
|
|
4325
|
-
const rawDocumentId = editor.documentId ?? v2TrackedChanges?.documentId;
|
|
4326
|
-
return {
|
|
4327
|
-
editor,
|
|
4328
|
-
adapter,
|
|
4329
|
-
documentId: rawDocumentId == null ? null : String(rawDocumentId),
|
|
4330
|
-
reconcileToken: reviewMutationToken,
|
|
4331
|
-
v2TrackedChanges,
|
|
4332
|
-
docTrackChanges
|
|
4333
|
-
};
|
|
4334
|
-
},
|
|
4335
|
-
reconcile: async (context, impact) => {
|
|
4336
|
-
if (impact.removedIds.size > 0) markPostDecisionTrackChanges(new Set(impact.removedIds));
|
|
4337
|
-
const requestedIds = [...new Set(impact.upsertIds)];
|
|
4338
|
-
if (requestedIds.length === 0) return {
|
|
4339
|
-
ok: true,
|
|
4340
|
-
items: [],
|
|
4341
|
-
resolvedIds: [],
|
|
4342
|
-
unresolvedIds: []
|
|
4343
|
-
};
|
|
4344
|
-
const settledTrackChanges = asyncReads.get("trackChanges");
|
|
4345
|
-
const settledTrackChangeItems = settledTrackChanges?.hasSettled && Array.isArray(settledTrackChanges.value) ? settledTrackChanges.value : null;
|
|
4346
|
-
const cachedIds = new Set(settledTrackChangeItems ? settledTrackChangeItems.map(readEntityId).filter((id) => id != null) : []);
|
|
4347
|
-
const settledCatalogIsProvenEmpty = settledTrackChangeItems?.length === 0 && hostSourceLoadPhase() === "complete";
|
|
4348
|
-
const idsNeedingPlacement = new Set(requestedIds.filter((id) => !cachedIds.has(id)));
|
|
4349
|
-
const getTrackedChange = context.v2TrackedChanges?.getTrackedChange;
|
|
4350
|
-
const getRawTrackedChange = context.docTrackChanges?.get;
|
|
4351
|
-
if (typeof getTrackedChange !== "function" && typeof getRawTrackedChange !== "function") return {
|
|
4352
|
-
ok: false,
|
|
4353
|
-
items: [],
|
|
4354
|
-
resolvedIds: [],
|
|
4355
|
-
unresolvedIds: requestedIds
|
|
4356
|
-
};
|
|
4357
|
-
const reads = await Promise.all(requestedIds.map(async (id) => {
|
|
4358
|
-
try {
|
|
4359
|
-
if (typeof getTrackedChange === "function") {
|
|
4360
|
-
const result = await Promise.resolve(getTrackedChange.call(context.v2TrackedChanges, id));
|
|
4361
|
-
return {
|
|
4362
|
-
id,
|
|
4363
|
-
ok: result?.ok !== false,
|
|
4364
|
-
items: result?.ok !== false && Array.isArray(result?.items) ? result.items : [],
|
|
4365
|
-
result
|
|
4366
|
-
};
|
|
4367
|
-
}
|
|
4368
|
-
const result = await Promise.resolve(getRawTrackedChange.call(context.docTrackChanges, { id }));
|
|
4369
|
-
return {
|
|
4370
|
-
id,
|
|
4371
|
-
ok: true,
|
|
4372
|
-
items: result && typeof result === "object" ? [result] : [],
|
|
4373
|
-
result
|
|
4374
|
-
};
|
|
4375
|
-
} catch (error) {
|
|
4376
|
-
return {
|
|
4377
|
-
id,
|
|
4378
|
-
ok: false,
|
|
4379
|
-
items: [],
|
|
4380
|
-
result: error
|
|
4381
|
-
};
|
|
4382
|
-
}
|
|
4383
|
-
}));
|
|
4384
|
-
const items = [];
|
|
4385
|
-
const resolvedIds = [];
|
|
4386
|
-
const unresolvedIds = [];
|
|
4387
|
-
for (const read of reads) {
|
|
4388
|
-
const matchingItems = read.items.filter((item) => {
|
|
4389
|
-
const projected = projectTrackChangesItem(item);
|
|
4390
|
-
return projected != null && readEntityId(projected) === read.id;
|
|
4391
|
-
});
|
|
4392
|
-
if (read.ok && matchingItems.length > 0) {
|
|
4393
|
-
items.push(...matchingItems);
|
|
4394
|
-
resolvedIds.push(read.id);
|
|
4395
|
-
} else unresolvedIds.push(read.id);
|
|
4396
|
-
}
|
|
4397
|
-
let orderedItems;
|
|
4398
|
-
const placementIds = resolvedIds.filter((id) => idsNeedingPlacement.has(id));
|
|
4399
|
-
if (placementIds.length > 0 && !settledCatalogIsProvenEmpty) {
|
|
4400
|
-
const orderResult = await hydrateTrackedChanges(context, {
|
|
4401
|
-
trackedChangesListMode: "interaction-prime",
|
|
4402
|
-
refreshReason: "mutation-render-reconcile",
|
|
4403
|
-
blocking: false,
|
|
4404
|
-
targetIds: placementIds
|
|
4405
|
-
});
|
|
4406
|
-
if (orderResult.ok) orderedItems = orderResult.items;
|
|
4407
|
-
}
|
|
4408
|
-
return {
|
|
4409
|
-
ok: unresolvedIds.length === 0,
|
|
4410
|
-
items,
|
|
4411
|
-
orderedItems,
|
|
4412
|
-
resolvedIds,
|
|
4413
|
-
unresolvedIds,
|
|
4414
|
-
failures: reads.filter((read) => !read.ok).map((read) => read.result)
|
|
4415
|
-
};
|
|
4416
|
-
},
|
|
4417
|
-
hydrate: hydrateTrackedChanges,
|
|
4418
|
-
onReconciled: (context, ids, result) => {
|
|
4419
|
-
if (result?.reconcileMode === "authoritative") {
|
|
4420
|
-
if (context.reconcileToken !== reviewMutationToken) return;
|
|
4421
|
-
if (result.ok === true && result.complete === true && result.sourceCoverageComplete === true) {
|
|
4422
|
-
clearAuthoritativeTrackChangesRetry();
|
|
4423
|
-
replaceTrackedChangeItemsInCache(result.items ?? []);
|
|
4424
|
-
authoritativeTrackChangesPendingToken = null;
|
|
4425
|
-
} else if (result.ok === true) {
|
|
4426
|
-
clearAuthoritativeTrackChangesRetry();
|
|
4427
|
-
mergeTrackedChangeItemsIntoCache(result.items ?? []);
|
|
4428
|
-
} else scheduleAuthoritativeTrackChangesRetry(context.reconcileToken);
|
|
4429
|
-
recompute();
|
|
4430
|
-
return;
|
|
4431
|
-
}
|
|
4432
|
-
const items = (result?.items ?? []).filter((item) => {
|
|
4433
|
-
const projected = projectTrackChangesItem(item);
|
|
4434
|
-
const id = projected == null ? null : readEntityId(projected);
|
|
4435
|
-
return id != null && ids.has(id);
|
|
4436
|
-
});
|
|
4437
|
-
mergeTrackedChangeItemsIntoCache(items, result?.orderedItems ?? result?.items ?? []);
|
|
4438
|
-
recompute();
|
|
4439
|
-
}
|
|
4440
|
-
});
|
|
4441
|
-
scheduleAuthoritativeTrackChangesRetry = (reconcileToken) => {
|
|
4442
|
-
if (disposed || authoritativeTrackChangesRetryTimer) return;
|
|
4443
|
-
const delayMs = Math.min(ASYNC_READ_FAILURE_RETRY_BASE_MS * 2 ** authoritativeTrackChangesFailureCount, ASYNC_READ_FAILURE_RETRY_MAX_MS);
|
|
4444
|
-
authoritativeTrackChangesFailureCount += 1;
|
|
4445
|
-
authoritativeTrackChangesRetryTimer = setTimeout(() => {
|
|
4446
|
-
authoritativeTrackChangesRetryTimer = null;
|
|
4447
|
-
if (disposed || authoritativeTrackChangesPendingToken === null || reconcileToken !== reviewMutationToken) return;
|
|
4448
|
-
reviewMutationReconciler.onRender(true);
|
|
4449
|
-
}, delayMs);
|
|
4450
|
-
};
|
|
4451
3942
|
/** Token for the live selection read, including the mode that controls its public projection. */
|
|
4452
3943
|
const selectionReadToken = () => `${contentToken()}|m${readDocumentMode()}|s${selectionEpoch}`;
|
|
4453
3944
|
/** Stable signature of a settled selection, used to key selection-scoped reads. */
|
|
@@ -4473,6 +3964,11 @@ function createSuperDocUI(options) {
|
|
|
4473
3964
|
return "unknown";
|
|
4474
3965
|
};
|
|
4475
3966
|
const demandedHeavyReads = /* @__PURE__ */ new Map();
|
|
3967
|
+
const incompleteTrackChangesDirectoryReadTokens = /* @__PURE__ */ new Map();
|
|
3968
|
+
const postSourceCompletionRefreshTokens = /* @__PURE__ */ new Map();
|
|
3969
|
+
let sourceCompletionObservedToken = null;
|
|
3970
|
+
let commentsDirectoryLeaseCount = 0;
|
|
3971
|
+
let trackChangesDirectoryLeaseCount = 0;
|
|
4476
3972
|
const demandHeavyDocRead = (key) => {
|
|
4477
3973
|
const token = contentToken();
|
|
4478
3974
|
if (demandedHeavyReads.get(key) === token) return;
|
|
@@ -4485,6 +3981,41 @@ function createSuperDocUI(options) {
|
|
|
4485
3981
|
return Array.isArray(entry.value) && entry.value.length > 0;
|
|
4486
3982
|
};
|
|
4487
3983
|
const heavyReadDemandActive = (key, token) => demandedHeavyReads.get(key) === token;
|
|
3984
|
+
const refreshIncompleteTrackChangesDirectories = () => {
|
|
3985
|
+
const token = contentToken();
|
|
3986
|
+
let invalidated = false;
|
|
3987
|
+
for (const [key, incompleteToken] of incompleteTrackChangesDirectoryReadTokens) {
|
|
3988
|
+
if (incompleteToken !== token) {
|
|
3989
|
+
incompleteTrackChangesDirectoryReadTokens.delete(key);
|
|
3990
|
+
continue;
|
|
3991
|
+
}
|
|
3992
|
+
if (postSourceCompletionRefreshTokens.get(key) === token) continue;
|
|
3993
|
+
const entry = asyncReads.get(key);
|
|
3994
|
+
if (!entry?.hasSettled || entry.token !== token || entry.inflightToken != null) continue;
|
|
3995
|
+
asyncReads.set(key, {
|
|
3996
|
+
...entry,
|
|
3997
|
+
token: null,
|
|
3998
|
+
inflightToken: null
|
|
3999
|
+
});
|
|
4000
|
+
postSourceCompletionRefreshTokens.set(key, token);
|
|
4001
|
+
invalidated = true;
|
|
4002
|
+
}
|
|
4003
|
+
if (invalidated) scheduleAsyncRefresh();
|
|
4004
|
+
};
|
|
4005
|
+
const acquireDirectoryLease = (family) => {
|
|
4006
|
+
if (family === "comments") commentsDirectoryLeaseCount += 1;
|
|
4007
|
+
else trackChangesDirectoryLeaseCount += 1;
|
|
4008
|
+
demandHeavyDocRead(family);
|
|
4009
|
+
let released = false;
|
|
4010
|
+
return () => {
|
|
4011
|
+
if (released) return;
|
|
4012
|
+
released = true;
|
|
4013
|
+
if (family === "comments") commentsDirectoryLeaseCount = Math.max(0, commentsDirectoryLeaseCount - 1);
|
|
4014
|
+
else trackChangesDirectoryLeaseCount = Math.max(0, trackChangesDirectoryLeaseCount - 1);
|
|
4015
|
+
if ((family === "comments" ? commentsDirectoryLeaseCount : trackChangesDirectoryLeaseCount) === 0 && demandedHeavyReads.get(family) === contentToken()) demandedHeavyReads.delete(family);
|
|
4016
|
+
scheduleAsyncRefresh();
|
|
4017
|
+
};
|
|
4018
|
+
};
|
|
4488
4019
|
/**
|
|
4489
4020
|
* Demand-driven route for the content-controls catalog (panel / API use).
|
|
4490
4021
|
* Explicit demand bypasses both active-loading and post-complete idle holds.
|
|
@@ -4555,6 +4086,8 @@ function createSuperDocUI(options) {
|
|
|
4555
4086
|
const handleLoadingSnapshot = (snapshot) => {
|
|
4556
4087
|
const stage = snapshot?.sourceStage;
|
|
4557
4088
|
if (stage === "source-complete" || stage === "source-failed" || stage === "source-cancelled") {
|
|
4089
|
+
sourceCompletionObservedToken = contentToken();
|
|
4090
|
+
refreshIncompleteTrackChangesDirectories();
|
|
4558
4091
|
lastEditableMutationAtMs = Math.max(lastEditableMutationAtMs, Date.now());
|
|
4559
4092
|
scheduleHeavyReadCompletionRecompute();
|
|
4560
4093
|
}
|
|
@@ -4879,12 +4412,8 @@ function createSuperDocUI(options) {
|
|
|
4879
4412
|
const editor = getEditor();
|
|
4880
4413
|
if (editor === lastCoordinatorEditor) return;
|
|
4881
4414
|
resetPostPaintContentRefresh();
|
|
4882
|
-
reviewMutationReconciler.reset();
|
|
4883
|
-
reviewMutationToken = {};
|
|
4884
4415
|
lastCoordinatorEditor = editor;
|
|
4885
4416
|
asyncReads.clear();
|
|
4886
|
-
authoritativeTrackChangesPendingToken = null;
|
|
4887
|
-
clearAuthoritativeTrackChangesRetry();
|
|
4888
4417
|
if (asyncReadFailureRetryTimer) clearTimeout(asyncReadFailureRetryTimer);
|
|
4889
4418
|
asyncReadFailureRetryTimer = null;
|
|
4890
4419
|
asyncReadFailureRetryAtMs = 0;
|
|
@@ -4896,6 +4425,9 @@ function createSuperDocUI(options) {
|
|
|
4896
4425
|
pendingSelectionSeedValidationToken = null;
|
|
4897
4426
|
coldAsyncReadDeferrals.clear();
|
|
4898
4427
|
demandedHeavyReads.clear();
|
|
4428
|
+
incompleteTrackChangesDirectoryReadTokens.clear();
|
|
4429
|
+
postSourceCompletionRefreshTokens.clear();
|
|
4430
|
+
sourceCompletionObservedToken = null;
|
|
4899
4431
|
heavyReadsHeldUntilIdle = false;
|
|
4900
4432
|
if (detachSourceLoading) detachSourceLoading();
|
|
4901
4433
|
detachSourceLoading = null;
|
|
@@ -4905,9 +4437,10 @@ function createSuperDocUI(options) {
|
|
|
4905
4437
|
};
|
|
4906
4438
|
/** Bump the document-mutation revision so content reads re-fetch on the next compute. */
|
|
4907
4439
|
const invalidateDocumentContent = () => {
|
|
4908
|
-
const carryAuthoritativeTrackChangesHold = authoritativeTrackChangesPendingToken !== null;
|
|
4909
4440
|
documentMutationRevision += 1;
|
|
4910
|
-
|
|
4441
|
+
incompleteTrackChangesDirectoryReadTokens.clear();
|
|
4442
|
+
postSourceCompletionRefreshTokens.clear();
|
|
4443
|
+
sourceCompletionObservedToken = null;
|
|
4911
4444
|
clearPostDecisionTrackChanges();
|
|
4912
4445
|
};
|
|
4913
4446
|
/**
|
|
@@ -5135,17 +4668,51 @@ function createSuperDocUI(options) {
|
|
|
5135
4668
|
recompute();
|
|
5136
4669
|
return state.selection;
|
|
5137
4670
|
};
|
|
5138
|
-
const
|
|
4671
|
+
const readCommentsDirectory = () => {
|
|
5139
4672
|
const commentsApi = getDoc()?.comments;
|
|
4673
|
+
return readAsync("comments", contentToken(), () => commentsApi?.list ? commentsApi.list() : void 0, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
|
|
4674
|
+
};
|
|
4675
|
+
const readTrackChangesDirectory = (key = "trackChanges") => {
|
|
4676
|
+
const token = contentToken();
|
|
4677
|
+
const tcApi = getDoc()?.trackChanges;
|
|
4678
|
+
const v2TrackedChanges = getV2TrackedChanges();
|
|
4679
|
+
const listTrackedChanges = typeof v2TrackedChanges?.listTrackedChanges === "function" ? v2TrackedChanges.listTrackedChanges : null;
|
|
4680
|
+
const read = readAsync(key, token, () => {
|
|
4681
|
+
if (listTrackedChanges) return runUiTrackedChangesCatalogRead(() => tcApi?.list?.({ in: "all" }));
|
|
4682
|
+
return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
|
|
4683
|
+
}, (raw) => {
|
|
4684
|
+
const result = raw && typeof raw === "object" ? raw : null;
|
|
4685
|
+
if (result?.complete === false || result?.sourceCoverageComplete === false) {
|
|
4686
|
+
incompleteTrackChangesDirectoryReadTokens.set(key, token);
|
|
4687
|
+
armSourceLoadCompletionRecompute();
|
|
4688
|
+
if (sourceCompletionObservedToken === token || hostSourceLoadPhase() === "complete") {
|
|
4689
|
+
const refresh = () => {
|
|
4690
|
+
if (!disposed) refreshIncompleteTrackChangesDirectories();
|
|
4691
|
+
};
|
|
4692
|
+
if (typeof queueMicrotask === "function") queueMicrotask(refresh);
|
|
4693
|
+
else Promise.resolve().then(refresh);
|
|
4694
|
+
}
|
|
4695
|
+
} else if (incompleteTrackChangesDirectoryReadTokens.get(key) === token) incompleteTrackChangesDirectoryReadTokens.delete(key);
|
|
4696
|
+
return result && Array.isArray(result.items) ? result.items : [];
|
|
4697
|
+
});
|
|
4698
|
+
return incompleteTrackChangesDirectoryReadTokens.get(key) === token && read.status === "ready" ? {
|
|
4699
|
+
...read,
|
|
4700
|
+
status: "stale"
|
|
4701
|
+
} : read;
|
|
4702
|
+
};
|
|
4703
|
+
const computeComments = (selection) => {
|
|
5140
4704
|
const reviewWindow = getV2ReviewWindowSnapshot();
|
|
5141
|
-
const
|
|
5142
|
-
const
|
|
5143
|
-
const
|
|
5144
|
-
const value =
|
|
5145
|
-
const listStatus =
|
|
4705
|
+
const hasWindowFeed = hasV2ReviewWindowFeed();
|
|
4706
|
+
const catalog = !hasWindowFeed ? readCommentsDirectory() : null;
|
|
4707
|
+
const windowItems = hasWindowFeed && Array.isArray(reviewWindow?.commentItems) ? reviewWindow.commentItems : null;
|
|
4708
|
+
const value = hasWindowFeed ? windowItems : catalog.value;
|
|
4709
|
+
const listStatus = hasWindowFeed ? reviewWindowSliceStatus(reviewWindow) : catalog.status;
|
|
5146
4710
|
const items = value ?? [];
|
|
5147
4711
|
const activeIds = selection.activeCommentIds;
|
|
5148
|
-
|
|
4712
|
+
const directory = asyncReads.get("comments");
|
|
4713
|
+
const directoryItems = directory?.token === contentToken() && directory.hasSettled && Array.isArray(directory.value) ? directory.value : null;
|
|
4714
|
+
const activeValidationItems = commentsDirectoryLeaseCount > 0 ? directoryItems : items;
|
|
4715
|
+
if (explicitActiveCommentId && activeValidationItems && !activeValidationItems.some((item) => readEntityId(item) === explicitActiveCommentId)) explicitActiveCommentId = null;
|
|
5149
4716
|
const activeId = explicitActiveCommentId ?? activeIds[0] ?? null;
|
|
5150
4717
|
return {
|
|
5151
4718
|
status: combineStatus(listStatus, selection.status),
|
|
@@ -5156,51 +4723,32 @@ function createSuperDocUI(options) {
|
|
|
5156
4723
|
activeId
|
|
5157
4724
|
};
|
|
5158
4725
|
};
|
|
5159
|
-
const
|
|
5160
|
-
|
|
5161
|
-
const
|
|
5162
|
-
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
} : readAsync("trackChanges:all", token, () => {
|
|
5167
|
-
if (!tcApi?.list) return void 0;
|
|
5168
|
-
const fallback = () => tcApi.list({ in: "all" });
|
|
5169
|
-
const bridgeOwnsCatalog = typeof getV2TrackedChanges()?.listTrackedChanges === "function";
|
|
5170
|
-
const shared = runUiTrackedChangesCatalogRead(fallback);
|
|
5171
|
-
if (!bridgeOwnsCatalog) return shared;
|
|
5172
|
-
return Promise.resolve(shared).then((raw) => {
|
|
5173
|
-
const record = isLooseObject(raw) ? raw : null;
|
|
5174
|
-
return (Array.isArray(record?.items) ? record.items : []).some((item) => readEntityStory(item) == null) ? fallback() : raw;
|
|
5175
|
-
});
|
|
5176
|
-
}, (raw) => {
|
|
5177
|
-
return (raw && Array.isArray(raw.items) ? raw.items : []).map(projectTrackChangesItem).filter((item) => item != null);
|
|
5178
|
-
});
|
|
5179
|
-
return status === "ready" ? value : null;
|
|
4726
|
+
const projectedTrackChangesCache = /* @__PURE__ */ new WeakMap();
|
|
4727
|
+
const projectTrackChangesItems = (source) => {
|
|
4728
|
+
const cached = projectedTrackChangesCache.get(source);
|
|
4729
|
+
if (cached) return cached;
|
|
4730
|
+
const projected = source.map(projectTrackChangesItem).filter((item) => item != null);
|
|
4731
|
+
projectedTrackChangesCache.set(source, projected);
|
|
4732
|
+
return projected;
|
|
5180
4733
|
};
|
|
5181
|
-
const
|
|
5182
|
-
|
|
5183
|
-
const
|
|
5184
|
-
|
|
5185
|
-
const
|
|
5186
|
-
const postDecisionIds = postDecisionTrackChangeIdsForToken(token);
|
|
5187
|
-
const heldEntry = asyncReads.get("trackChanges");
|
|
5188
|
-
const reviewWindow = getV2ReviewWindowSnapshot();
|
|
5189
|
-
const catalog = authoritativeTrackChangesPendingToken === token ? {
|
|
5190
|
-
value: heldEntry?.hasSettled ? heldEntry.value : null,
|
|
5191
|
-
status: heldEntry?.hasSettled ? "stale" : "pending"
|
|
5192
|
-
} : readAsync("trackChanges", token, () => {
|
|
5193
|
-
if (listTrackedChanges) return runUiTrackedChangesCatalogRead(() => tcApi?.list?.());
|
|
5194
|
-
return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
|
|
5195
|
-
}, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
|
|
5196
|
-
const windowItems = hasV2ReviewWindowFeed() && Array.isArray(reviewWindow?.trackedChangeItems) ? reviewWindow.trackedChangeItems : null;
|
|
5197
|
-
const useWindowSlice = catalog.status === "pending" && windowItems != null;
|
|
5198
|
-
const value = useWindowSlice ? windowItems : catalog.value;
|
|
5199
|
-
const listStatus = useWindowSlice ? reviewWindowSliceStatus(reviewWindow) : catalog.status;
|
|
5200
|
-
const items = (value ?? []).map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
|
|
4734
|
+
const filteredTrackChangesCache = /* @__PURE__ */ new WeakMap();
|
|
4735
|
+
const filterPostDecisionTrackChanges = (source, suppressedIds) => {
|
|
4736
|
+
const cached = filteredTrackChangesCache.get(source);
|
|
4737
|
+
if (cached?.suppressedIds === suppressedIds) return cached.value;
|
|
4738
|
+
const filtered = suppressedIds ? source.filter((item) => {
|
|
5201
4739
|
const id = readEntityId(item);
|
|
5202
|
-
return !id || !
|
|
4740
|
+
return !id || !suppressedIds.has(id);
|
|
4741
|
+
}) : source;
|
|
4742
|
+
filteredTrackChangesCache.set(source, {
|
|
4743
|
+
suppressedIds,
|
|
4744
|
+
value: filtered
|
|
5203
4745
|
});
|
|
4746
|
+
return filtered;
|
|
4747
|
+
};
|
|
4748
|
+
const trackChangeAuthorsCache = /* @__PURE__ */ new WeakMap();
|
|
4749
|
+
const trackChangeAuthors = (items) => {
|
|
4750
|
+
const cached = trackChangeAuthorsCache.get(items);
|
|
4751
|
+
if (cached) return cached;
|
|
5204
4752
|
const authors = /* @__PURE__ */ new Set();
|
|
5205
4753
|
for (const item of items) {
|
|
5206
4754
|
const change = trackChangesItemPayload(item);
|
|
@@ -5208,12 +4756,38 @@ function createSuperDocUI(options) {
|
|
|
5208
4756
|
const author = change.author ?? change.authorEmail ?? row.author ?? row.authorEmail;
|
|
5209
4757
|
if (typeof author === "string") authors.add(author);
|
|
5210
4758
|
}
|
|
5211
|
-
const
|
|
4759
|
+
const value = [...authors];
|
|
4760
|
+
trackChangeAuthorsCache.set(items, value);
|
|
4761
|
+
return value;
|
|
4762
|
+
};
|
|
4763
|
+
const readAllStoryTrackChanges = () => {
|
|
4764
|
+
const token = contentToken();
|
|
4765
|
+
if (hasV2ReviewWindowFeed()) {
|
|
4766
|
+
const directory = asyncReads.get("trackChanges");
|
|
4767
|
+
const source = directory?.token === token && directory.hasSettled && Array.isArray(directory.value) ? directory.value : getV2ReviewWindowSnapshot()?.trackedChangeItems;
|
|
4768
|
+
if (!Array.isArray(source)) return null;
|
|
4769
|
+
return projectTrackChangesItems(source);
|
|
4770
|
+
}
|
|
4771
|
+
const { value, status } = readTrackChangesDirectory("trackChanges:all");
|
|
4772
|
+
return status === "ready" ? projectTrackChangesItems(value ?? []) : null;
|
|
4773
|
+
};
|
|
4774
|
+
const computeTrackChanges = (selection) => {
|
|
4775
|
+
const token = contentToken();
|
|
4776
|
+
const postDecisionIds = postDecisionTrackChangeIdsForToken(token);
|
|
4777
|
+
const reviewWindow = getV2ReviewWindowSnapshot();
|
|
4778
|
+
const hasWindowFeed = hasV2ReviewWindowFeed();
|
|
4779
|
+
const catalog = !hasWindowFeed ? readTrackChangesDirectory() : null;
|
|
4780
|
+
const windowItems = hasWindowFeed && Array.isArray(reviewWindow?.trackedChangeItems) ? reviewWindow.trackedChangeItems : null;
|
|
4781
|
+
const value = hasWindowFeed ? windowItems : catalog.value;
|
|
4782
|
+
const listStatus = hasWindowFeed ? reviewWindowSliceStatus(reviewWindow) : catalog.status;
|
|
4783
|
+
const items = filterPostDecisionTrackChanges(projectTrackChangesItems(value ?? []), postDecisionIds);
|
|
4784
|
+
const allStoryItems = readAllStoryTrackChanges();
|
|
5212
4785
|
const active = explicitActiveChange;
|
|
5213
|
-
if (active) {
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
4786
|
+
if (active && !pendingTrackChangeRevealFocuses.has(active)) {
|
|
4787
|
+
const directory = asyncReads.get("trackChanges");
|
|
4788
|
+
const directoryItems = directory?.token === token && directory.hasSettled && Array.isArray(directory.value) ? filterPostDecisionTrackChanges(projectTrackChangesItems(directory.value), postDecisionIds) : null;
|
|
4789
|
+
const activeValidationItems = trackChangesDirectoryLeaseCount > 0 ? directoryItems : active.story ? allStoryItems : items;
|
|
4790
|
+
if (activeValidationItems && !activeValidationItems.some((row) => entityRowMatchesRequest(row, active.id, active.story))) setExplicitActiveChange(null);
|
|
5217
4791
|
}
|
|
5218
4792
|
const publicIdItems = allStoryItems ?? items;
|
|
5219
4793
|
const selectionIdContext = buildStoryScopedTrackedChangeIdContext(publicIdItems, selectionStory(selection));
|
|
@@ -5234,7 +4808,37 @@ function createSuperDocUI(options) {
|
|
|
5234
4808
|
items,
|
|
5235
4809
|
total: items.length,
|
|
5236
4810
|
activeId,
|
|
5237
|
-
authors:
|
|
4811
|
+
authors: trackChangeAuthors(items)
|
|
4812
|
+
};
|
|
4813
|
+
};
|
|
4814
|
+
const computeCommentsDirectorySnapshot = (selection, windowSnapshot) => {
|
|
4815
|
+
if (!hasV2ReviewWindowFeed()) return windowSnapshot;
|
|
4816
|
+
demandHeavyDocRead("comments");
|
|
4817
|
+
const directory = readCommentsDirectory();
|
|
4818
|
+
const items = directory.value ?? windowSnapshot.items;
|
|
4819
|
+
return {
|
|
4820
|
+
status: combineStatus(directory.status, selection.status),
|
|
4821
|
+
listStatus: directory.status,
|
|
4822
|
+
items,
|
|
4823
|
+
total: items.length,
|
|
4824
|
+
activeIds: selection.activeCommentIds,
|
|
4825
|
+
activeId: explicitActiveCommentId ?? selection.activeCommentIds[0] ?? null
|
|
4826
|
+
};
|
|
4827
|
+
};
|
|
4828
|
+
const computeTrackChangesDirectorySnapshot = (selection, windowSnapshot) => {
|
|
4829
|
+
if (!hasV2ReviewWindowFeed()) return windowSnapshot;
|
|
4830
|
+
demandHeavyDocRead("trackChanges");
|
|
4831
|
+
const directory = readTrackChangesDirectory();
|
|
4832
|
+
const postDecisionIds = postDecisionTrackChangeIdsForToken(contentToken());
|
|
4833
|
+
const items = filterPostDecisionTrackChanges(projectTrackChangesItems(directory.value ?? windowSnapshot.items), postDecisionIds);
|
|
4834
|
+
const explicitIdContext = explicitActiveChange?.story ? buildStoryScopedTrackedChangeIdContext(items, explicitActiveChange.story) : buildTrackedChangeIdContext(items);
|
|
4835
|
+
const activeId = explicitActiveChange ? explicitIdContext.toPublicId(explicitActiveChange.id) ?? explicitActiveChange.id : windowSnapshot.activeId;
|
|
4836
|
+
return {
|
|
4837
|
+
status: combineStatus(directory.status, selection.status),
|
|
4838
|
+
items,
|
|
4839
|
+
total: items.length,
|
|
4840
|
+
activeId,
|
|
4841
|
+
authors: trackChangeAuthors(items)
|
|
5238
4842
|
};
|
|
5239
4843
|
};
|
|
5240
4844
|
const computeContentControls = (selection) => {
|
|
@@ -7167,54 +6771,19 @@ function createSuperDocUI(options) {
|
|
|
7167
6771
|
}
|
|
7168
6772
|
if (type === "mutation:committed" && event.reviewMutation) settleUiReviewMutation(event.reviewMutation.token);
|
|
7169
6773
|
if (type === "source:complete" || type === "source:signals-complete") {
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
reviewMutationReconciler.onRender(true);
|
|
7173
|
-
}
|
|
6774
|
+
sourceCompletionObservedToken = contentToken();
|
|
6775
|
+
refreshIncompleteTrackChangesDirectories();
|
|
7174
6776
|
return;
|
|
7175
6777
|
}
|
|
7176
6778
|
if (type === "mutation:committed" || type === "collaboration:remote-changed") lastEditableMutationAtMs = Date.now();
|
|
7177
|
-
if (type === "mutation:committed") {
|
|
7178
|
-
clearAuthoritativeTrackChangesRetry();
|
|
7179
|
-
reviewMutationToken = {};
|
|
7180
|
-
}
|
|
7181
6779
|
if (type === "mutation:committed" || type === "save:completed" || type === "collaboration:remote-changed") {
|
|
7182
6780
|
const impact = type === "mutation:committed" ? getV2TrackedChangeMutationImpact(event) : null;
|
|
7183
|
-
const supersedesPendingReviewReconcile = type === "mutation:committed" && reviewMutationReconciler.getPendingIds().size > 0;
|
|
7184
6781
|
if (impact?.allResolved) {
|
|
7185
|
-
reviewMutationReconciler.reset();
|
|
7186
6782
|
replaceTrackedChangeItemsInCache([]);
|
|
7187
|
-
authoritativeTrackChangesPendingToken = null;
|
|
7188
6783
|
publishAllTrackedChangesResolved(event.receipt);
|
|
7189
6784
|
return;
|
|
7190
6785
|
}
|
|
7191
6786
|
if (impact?.removedIds.size) markPostDecisionTrackChanges(new Set(impact.removedIds), event.receipt);
|
|
7192
|
-
const authoritativeHistoryImpact = impact?.reconcileMode === "authoritative" && impact.upsertIds.size > 0;
|
|
7193
|
-
if (authoritativeHistoryImpact) {
|
|
7194
|
-
invalidateDocumentContent();
|
|
7195
|
-
clearAuthoritativeTrackChangesRetry();
|
|
7196
|
-
authoritativeTrackChangesPendingToken = contentToken();
|
|
7197
|
-
recompute();
|
|
7198
|
-
}
|
|
7199
|
-
if (impact?.upsertIds.size || supersedesPendingReviewReconcile) {
|
|
7200
|
-
const receipt = event.origin === "history" ? null : event.receipt;
|
|
7201
|
-
const readiness = getEditor()?.documentMutationReadiness;
|
|
7202
|
-
const canWaitForReceiptPaint = typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
|
|
7203
|
-
const historyRenderEpoch = event.origin === "history" && typeof readiness?.getRenderEpoch === "function" ? safeCall(() => readiness.getRenderEpoch.call(readiness), null) : null;
|
|
7204
|
-
const canWaitForHistoryPaint = event.origin === "history" && typeof readiness?.whenPainted === "function";
|
|
7205
|
-
const upsertImpact = {
|
|
7206
|
-
upsertIds: impact?.upsertIds ?? /* @__PURE__ */ new Set(),
|
|
7207
|
-
removedIds: /* @__PURE__ */ new Set(),
|
|
7208
|
-
reconcileMode: impact?.reconcileMode === "authoritative" ? "authoritative" : "targeted"
|
|
7209
|
-
};
|
|
7210
|
-
if (canWaitForReceiptPaint) reviewMutationReconciler.enqueueAfterPaint(upsertImpact, () => readiness.whenPainted.call(readiness, receipt));
|
|
7211
|
-
else if (canWaitForHistoryPaint) reviewMutationReconciler.enqueueAfterPaint(upsertImpact, () => readiness.whenPainted.call(readiness, typeof historyRenderEpoch === "number" ? { afterEpoch: historyRenderEpoch } : void 0));
|
|
7212
|
-
else {
|
|
7213
|
-
reviewMutationReconciler.enqueue(upsertImpact);
|
|
7214
|
-
reviewMutationReconciler.onRender(supersedesPendingReviewReconcile);
|
|
7215
|
-
}
|
|
7216
|
-
}
|
|
7217
|
-
if (authoritativeHistoryImpact) return;
|
|
7218
6787
|
const isTypingBurst = type === "mutation:committed" && isEditableTextMutationEvent(event) || type === "collaboration:remote-changed" && Array.isArray(event.changedStoryIds) && event.changedStoryIds.length > 0;
|
|
7219
6788
|
if (isTypingBurst) lastTypingMutationAtMs = Date.now();
|
|
7220
6789
|
if (type === "mutation:committed" && isTypingBurst) {
|
|
@@ -7373,6 +6942,25 @@ function createSuperDocUI(options) {
|
|
|
7373
6942
|
subscribe: (listener) => observe((snapshot) => listener({ snapshot }))
|
|
7374
6943
|
};
|
|
7375
6944
|
};
|
|
6945
|
+
const directorySnapshotHandle = (passiveSub, directorySub, family) => {
|
|
6946
|
+
const passive = snapshotHandle(passiveSub);
|
|
6947
|
+
const directory = snapshotHandle(directorySub);
|
|
6948
|
+
const observe = (listener) => {
|
|
6949
|
+
const release = acquireDirectoryLease(family);
|
|
6950
|
+
recompute();
|
|
6951
|
+
const unsubscribe = directory.observe(listener);
|
|
6952
|
+
return () => {
|
|
6953
|
+
unsubscribe();
|
|
6954
|
+
release();
|
|
6955
|
+
};
|
|
6956
|
+
};
|
|
6957
|
+
return {
|
|
6958
|
+
get: passive.get,
|
|
6959
|
+
getSnapshot: passive.getSnapshot,
|
|
6960
|
+
observe,
|
|
6961
|
+
subscribe: (listener) => observe((snapshot) => listener({ snapshot }))
|
|
6962
|
+
};
|
|
6963
|
+
};
|
|
7376
6964
|
function readTrackDecisionTargetId(target) {
|
|
7377
6965
|
return target && typeof target === "object" && typeof target.id === "string" ? String(target.id) : null;
|
|
7378
6966
|
}
|
|
@@ -9177,7 +8765,7 @@ function createSuperDocUI(options) {
|
|
|
9177
8765
|
const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
|
|
9178
8766
|
const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
|
|
9179
8767
|
const readResolvedTarget = (row) => {
|
|
9180
|
-
if (!moveSide) return readTarget(row);
|
|
8768
|
+
if (!moveSide) return namespace === "trackChanges" ? readTrackedChangeNavigationTarget(row, { preferStableBlock: true }) : readTarget(row);
|
|
9181
8769
|
const moveTarget = readEntityTarget(row);
|
|
9182
8770
|
if (moveTarget?.kind !== "move") return null;
|
|
9183
8771
|
const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
|
|
@@ -9235,7 +8823,7 @@ function createSuperDocUI(options) {
|
|
|
9235
8823
|
return filtered.slice(offset, end);
|
|
9236
8824
|
};
|
|
9237
8825
|
const commentsSub = sliceHandle((s) => s.comments);
|
|
9238
|
-
const commentsSnap =
|
|
8826
|
+
const commentsSnap = directorySnapshotHandle(commentsSub, select((s) => computeCommentsDirectorySnapshot(s.selection, s.comments)), "comments");
|
|
9239
8827
|
const comments = {
|
|
9240
8828
|
get: commentsSnap.get,
|
|
9241
8829
|
getSnapshot: commentsSnap.getSnapshot,
|
|
@@ -9243,12 +8831,8 @@ function createSuperDocUI(options) {
|
|
|
9243
8831
|
observe: commentsSnap.observe,
|
|
9244
8832
|
list: (query) => {
|
|
9245
8833
|
ensureCommentsCatalog();
|
|
9246
|
-
const
|
|
9247
|
-
|
|
9248
|
-
const commentsApi = getDoc()?.comments;
|
|
9249
|
-
const result = safeCall(commentsApi?.list ? () => commentsApi.list(query) : void 0, null);
|
|
9250
|
-
if (isPromiseLike(result)) return [];
|
|
9251
|
-
return filterCommentsSnapshot(Array.isArray(result?.items) ? result.items : [], query);
|
|
8834
|
+
const directory = readCommentsDirectory();
|
|
8835
|
+
return filterCommentsSnapshot(directory.value ?? commentsSub.get().items, query);
|
|
9252
8836
|
},
|
|
9253
8837
|
getById: (commentId) => {
|
|
9254
8838
|
const fromSnapshot = commentsSub.get().items.find((item) => readEntityId(item) === commentId) ?? null;
|
|
@@ -9334,10 +8918,13 @@ function createSuperDocUI(options) {
|
|
|
9334
8918
|
}
|
|
9335
8919
|
if (!getEditor()) return false;
|
|
9336
8920
|
const snapshot = commentsSub.get();
|
|
9337
|
-
|
|
9338
|
-
const
|
|
8921
|
+
const directory = asyncReads.get("comments");
|
|
8922
|
+
const directoryItems = directory?.token === contentToken() && directory.hasSettled && Array.isArray(directory.value) ? directory.value : null;
|
|
8923
|
+
const items = directoryItems ?? snapshot.items;
|
|
8924
|
+
if (!directoryItems && snapshot.listStatus !== "ready") return false;
|
|
8925
|
+
const resolvedId = resolveActiveCommentId(items, commentId);
|
|
9339
8926
|
if (!resolvedId) return false;
|
|
9340
|
-
if (!
|
|
8927
|
+
if (!items.some((entry) => readEntityId(entry) === resolvedId)) return false;
|
|
9341
8928
|
explicitActiveCommentId = resolvedId;
|
|
9342
8929
|
recompute();
|
|
9343
8930
|
return true;
|
|
@@ -9359,7 +8946,8 @@ function createSuperDocUI(options) {
|
|
|
9359
8946
|
};
|
|
9360
8947
|
const trackChangesSub = sliceHandle((s) => s.trackChanges);
|
|
9361
8948
|
const navigateTrackChange = (step, options) => {
|
|
9362
|
-
|
|
8949
|
+
ensureTrackChangesCatalog();
|
|
8950
|
+
const allRows = (readTrackChangesDirectory().value ?? trackChangesSub.get().items).map(projectTrackChangesItem).filter((item) => item != null && readEntityId(item) !== null);
|
|
9363
8951
|
if (allRows.length === 0) return null;
|
|
9364
8952
|
const currentId = state.trackChanges.activeId;
|
|
9365
8953
|
const currentStory = explicitActiveChange?.id === currentId ? explicitActiveChange.story : void 0;
|
|
@@ -9448,7 +9036,7 @@ function createSuperDocUI(options) {
|
|
|
9448
9036
|
trackChangeNavigationTail = run.then(() => void 0, () => void 0);
|
|
9449
9037
|
return run;
|
|
9450
9038
|
};
|
|
9451
|
-
const trackChangesSnap =
|
|
9039
|
+
const trackChangesSnap = directorySnapshotHandle(trackChangesSub, select((s) => computeTrackChangesDirectorySnapshot(s.selection, s.trackChanges)), "trackChanges");
|
|
9452
9040
|
const trackChanges = {
|
|
9453
9041
|
get: trackChangesSnap.get,
|
|
9454
9042
|
getSnapshot: trackChangesSnap.getSnapshot,
|
|
@@ -9456,7 +9044,12 @@ function createSuperDocUI(options) {
|
|
|
9456
9044
|
observe: trackChangesSnap.observe,
|
|
9457
9045
|
list: () => {
|
|
9458
9046
|
ensureTrackChangesCatalog();
|
|
9459
|
-
|
|
9047
|
+
const source = readTrackChangesDirectory().value ?? trackChangesSub.get().items;
|
|
9048
|
+
const suppressed = postDecisionTrackChangeIdsForToken(contentToken());
|
|
9049
|
+
return source.map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
|
|
9050
|
+
const id = readEntityId(item);
|
|
9051
|
+
return !id || !suppressed?.has(id);
|
|
9052
|
+
});
|
|
9460
9053
|
},
|
|
9461
9054
|
accept: (changeId) => executeTrackDecision("accept", changeId),
|
|
9462
9055
|
reject: (changeId) => executeTrackDecision("reject", changeId),
|
|
@@ -9513,7 +9106,7 @@ function createSuperDocUI(options) {
|
|
|
9513
9106
|
if (!allStoryItems.some((row) => entityRowMatchesRequest(row, publicId, requested.story))) return false;
|
|
9514
9107
|
requested.id = publicId;
|
|
9515
9108
|
} else {
|
|
9516
|
-
const items = trackChangesSub.get().items;
|
|
9109
|
+
const items = readAllStoryTrackChanges() ?? trackChangesSub.get().items;
|
|
9517
9110
|
const publicId = buildTrackedChangeIdContext(items).toPublicId(requested.id) ?? requested.id;
|
|
9518
9111
|
const item = items.find((candidate) => readEntityId(candidate) === publicId);
|
|
9519
9112
|
if (!item) return false;
|
|
@@ -10723,8 +10316,6 @@ function createSuperDocUI(options) {
|
|
|
10723
10316
|
if (disposed) return;
|
|
10724
10317
|
disposed = true;
|
|
10725
10318
|
releaseSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost, uiTrackedChangesCatalogState);
|
|
10726
|
-
reviewMutationReconciler.reset();
|
|
10727
|
-
clearAuthoritativeTrackChangesRetry();
|
|
10728
10319
|
if (foregroundAsyncRetryTimer) {
|
|
10729
10320
|
clearTimeout(foregroundAsyncRetryTimer);
|
|
10730
10321
|
foregroundAsyncRetryTimer = null;
|
|
@@ -10750,7 +10341,12 @@ function createSuperDocUI(options) {
|
|
|
10750
10341
|
if (detachSourceLoading) detachSourceLoading();
|
|
10751
10342
|
detachSourceLoading = null;
|
|
10752
10343
|
sourceLoadingSubscriptionHost = null;
|
|
10344
|
+
commentsDirectoryLeaseCount = 0;
|
|
10345
|
+
trackChangesDirectoryLeaseCount = 0;
|
|
10753
10346
|
demandedHeavyReads.clear();
|
|
10347
|
+
incompleteTrackChangesDirectoryReadTokens.clear();
|
|
10348
|
+
postSourceCompletionRefreshTokens.clear();
|
|
10349
|
+
sourceCompletionObservedToken = null;
|
|
10754
10350
|
coldAsyncReadDeferrals.clear();
|
|
10755
10351
|
for (const detach of detachers.splice(0)) detach();
|
|
10756
10352
|
if (detachHostSelection) detachHostSelection();
|
|
@@ -11009,4 +10605,4 @@ function commandIsActive(descriptor, selection) {
|
|
|
11009
10605
|
return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
|
|
11010
10606
|
}
|
|
11011
10607
|
//#endregion
|
|
11012
|
-
export {
|
|
10608
|
+
export { DOM_CLASS_NAMES as a, getV2TrackedChangeMutationImpact as i, shallowEqual as n, BUILT_IN_COMMAND_IDS as r, createSuperDocUI as t };
|