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