@agent-native/core 0.187.0-nightly-20260924172231 → 0.187.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +11 -0
  3. package/corpus/templates/analytics/app/i18n/en-US.ts +1 -0
  4. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +101 -11
  5. package/corpus/templates/analytics/changelog/2026-09-24-session-list-hides-0m-recordings-by-default.md +6 -0
  6. package/corpus/templates/assets/CHANGELOG.md +6 -0
  7. package/corpus/templates/brain/actions/backfill-search-embeddings.ts +337 -0
  8. package/corpus/templates/brain/actions/get-brain-health.ts +1 -1
  9. package/corpus/templates/brain/app/lib/brain.ts +18 -0
  10. package/corpus/templates/brain/jobs/process-ingest-queue.ts +25 -3
  11. package/corpus/templates/brain/server/lib/brain-health.ts +197 -57
  12. package/corpus/templates/brain/server/lib/demo.ts +25 -4
  13. package/corpus/templates/brain/server/lib/hybrid-search.ts +4 -1
  14. package/corpus/templates/brain/server/lib/ingest-queue.ts +1 -0
  15. package/corpus/templates/brain/server/lib/search-index.ts +254 -17
  16. package/corpus/templates/brain/server/lib/search.ts +27 -0
  17. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  18. package/corpus/templates/clips/CHANGELOG.md +10 -0
  19. package/corpus/templates/content/CHANGELOG.md +22 -0
  20. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +559 -227
  21. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +25 -1
  22. package/corpus/templates/design/CHANGELOG.md +36 -0
  23. package/corpus/templates/dispatch/CHANGELOG.md +7 -0
  24. package/corpus/templates/mail/CHANGELOG.md +17 -0
  25. package/corpus/templates/slides/CHANGELOG.md +7 -0
  26. package/dist/automation/index.d.ts +1 -1
  27. package/dist/collab/awareness.d.ts +2 -2
  28. package/dist/collab/struct-routes.d.ts +1 -1
  29. package/dist/embeddings/index.d.ts +5 -0
  30. package/dist/embeddings/index.js +44 -11
  31. package/dist/observability/routes.d.ts +5 -5
  32. package/dist/progress/routes.d.ts +1 -1
  33. package/dist/secrets/routes.d.ts +6 -6
  34. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  35. package/package.json +4 -4
@@ -24,7 +24,10 @@ import {
24
24
  useResourceSuggestions,
25
25
  useUpdateResourceSuggestion,
26
26
  } from "@agent-native/core/client/review";
27
- import type { ResourceSuggestion } from "@agent-native/core/review";
27
+ import type {
28
+ ResourceSuggestion,
29
+ SuggestionDecision,
30
+ } from "@agent-native/core/review";
28
31
  import { normalizeDocumentTitle } from "@agent-native/core/shared";
29
32
  import type { Document, DocumentSyncStatus } from "@shared/api";
30
33
  import { canonicalizeNfm } from "@shared/nfm";
@@ -325,6 +328,56 @@ export function suggestionAmendmentTargetIsResolved(
325
328
  return !!suggestion && suggestion.status !== "pending";
326
329
  }
327
330
 
331
+ export function suggestionDecisionPreviewContent(
332
+ suggestion: Pick<ResourceSuggestion, "operations">,
333
+ decision: SuggestionDecision,
334
+ canonicalContent: string,
335
+ optimistic = true,
336
+ ) {
337
+ if (!optimistic || decision === "rejected") return canonicalContent;
338
+ const after = suggestion.operations[0]?.after as {
339
+ markdown?: unknown;
340
+ } | null;
341
+ return typeof after?.markdown === "string"
342
+ ? after.markdown
343
+ : canonicalContent;
344
+ }
345
+
346
+ export function materializedSuggestionForDraft(
347
+ persisted: ReadonlyMap<string, ResourceSuggestion>,
348
+ draft: Pick<DraftSuggestion, "operations">,
349
+ ) {
350
+ const exact = persisted.get(suggestionOperationKey(draft.operations[0]!));
351
+ if (exact) return exact;
352
+ return persisted.size === 1
353
+ ? (persisted.values().next().value ?? null)
354
+ : null;
355
+ }
356
+
357
+ export function sameSuggestionAnchorIds(
358
+ current: string[] | null,
359
+ next: string[],
360
+ ) {
361
+ return (
362
+ current !== null &&
363
+ current.length === next.length &&
364
+ current.every((id) => next.includes(id))
365
+ );
366
+ }
367
+
368
+ export function documentEditorReservesInlineReviewSpace(args: {
369
+ showInlineComments: boolean;
370
+ preserveInlineReviewSpace: boolean;
371
+ hasInlineCommentSpace: boolean;
372
+ isDatabasePage: boolean;
373
+ }) {
374
+ return (
375
+ !args.isDatabasePage &&
376
+ (args.showInlineComments ||
377
+ (args.preserveInlineReviewSpace && args.hasInlineCommentSpace))
378
+ );
379
+ }
380
+
328
381
  export function suggestionPresentation(
329
382
  suggestion: Pick<ResourceSuggestion, "id" | "status" | "operations">,
330
383
  currentMarkdown: string,
@@ -1667,6 +1720,18 @@ function PageEditorSessionBody({
1667
1720
  { enabled: !isLocalFileDocument },
1668
1721
  );
1669
1722
  const [isSuggesting, setIsSuggesting] = useState(false);
1723
+ const [isStartingSuggestion, setIsStartingSuggestion] = useState(false);
1724
+ const [pendingSuggestionDecision, setPendingSuggestionDecision] = useState<{
1725
+ suggestion: ResourceSuggestion;
1726
+ decision: SuggestionDecision;
1727
+ continueSuggesting: boolean;
1728
+ optimistic: boolean;
1729
+ } | null>(null);
1730
+ const [decisionRefreshFailed, setDecisionRefreshFailed] = useState(false);
1731
+ const decisionRefreshInFlightRef = useRef(false);
1732
+ const suggestionDecisionInFlightRef = useRef(false);
1733
+ const [preserveInlineReviewSpace, setPreserveInlineReviewSpace] =
1734
+ useState(false);
1670
1735
  // Registry blocks do not yet produce typed suggestion operations. Keep their
1671
1736
  // canonical child actions read-only while the surrounding body is a draft.
1672
1737
  const blockRenderContext = useMemo(
@@ -1678,16 +1743,21 @@ function PageEditorSessionBody({
1678
1743
  [documentId, canEdit, isSuggesting],
1679
1744
  );
1680
1745
  const [isSubmittingSuggestions, setIsSubmittingSuggestions] = useState(false);
1681
- const [decidingSuggestionIds, setDecidingSuggestionIds] = useState<
1682
- ReadonlySet<string>
1683
- >(() => new Set());
1684
- const decidingSuggestionIdsRef = useRef(new Set<string>());
1685
1746
  const [suggestionAmendmentConflict, setSuggestionAmendmentConflict] =
1686
1747
  useState(false);
1687
1748
  const [suggestionDraft, setSuggestionDraft] = useState(document.content);
1688
1749
  const [anchoredSuggestionIds, setAnchoredSuggestionIds] = useState<
1689
1750
  string[] | null
1690
1751
  >(null);
1752
+ const handleSuggestionAnchorsChange = useCallback(
1753
+ (next: string[]) => {
1754
+ if (isSuggesting) return;
1755
+ setAnchoredSuggestionIds((current) =>
1756
+ sameSuggestionAnchorIds(current, next) ? current : next,
1757
+ );
1758
+ },
1759
+ [isSuggesting],
1760
+ );
1691
1761
  const [selectedSuggestionId, setSelectedSuggestionId] = useState<
1692
1762
  string | null
1693
1763
  >(null);
@@ -1834,15 +1904,21 @@ function PageEditorSessionBody({
1834
1904
  useRef<VisualEditorPersistenceController | null>(null);
1835
1905
  const [editorHistoryState, setEditorHistoryState] =
1836
1906
  useState<VisualEditorHistoryState>({ canUndo: false, canRedo: false });
1907
+ const editorHistoryStateRef = useRef(editorHistoryState);
1837
1908
  const handleHistoryStateChange = useCallback(
1838
1909
  (next: VisualEditorHistoryState) => {
1839
- setEditorHistoryState((current) =>
1840
- current.canUndo === next.canUndo && current.canRedo === next.canRedo
1841
- ? current
1842
- : next,
1843
- );
1910
+ const current = editorHistoryStateRef.current;
1911
+ if (
1912
+ current.canUndo === next.canUndo &&
1913
+ current.canRedo === next.canRedo
1914
+ ) {
1915
+ return;
1916
+ }
1917
+ editorHistoryStateRef.current = next;
1918
+ if (isSuggesting) return;
1919
+ setEditorHistoryState(next);
1844
1920
  },
1845
- [],
1921
+ [isSuggesting],
1846
1922
  );
1847
1923
  const handleHistoryControllerChange = useCallback(
1848
1924
  (controller: VisualEditorHistoryController | null) => {
@@ -3676,6 +3752,28 @@ function PageEditorSessionBody({
3676
3752
  suggestionsQuery.data?.suggestions ?? [],
3677
3753
  );
3678
3754
  }, [locallyCreatedSuggestions, suggestionsQuery.data?.suggestions]);
3755
+ const presentedSuggestions = useMemo(() => {
3756
+ if (!pendingSuggestionDecision?.continueSuggesting) return savedSuggestions;
3757
+ return savedSuggestions.map((suggestion) =>
3758
+ suggestion.id === pendingSuggestionDecision.suggestion.id
3759
+ ? {
3760
+ ...suggestion,
3761
+ status: pendingSuggestionDecision.optimistic
3762
+ ? pendingSuggestionDecision.decision
3763
+ : pendingSuggestionDecision.suggestion.status,
3764
+ }
3765
+ : suggestion,
3766
+ );
3767
+ }, [pendingSuggestionDecision, savedSuggestions]);
3768
+ const pendingSuggestionDecisionContent =
3769
+ pendingSuggestionDecision?.continueSuggesting
3770
+ ? suggestionDecisionPreviewContent(
3771
+ pendingSuggestionDecision.suggestion,
3772
+ pendingSuggestionDecision.decision,
3773
+ document.content,
3774
+ pendingSuggestionDecision.optimistic,
3775
+ )
3776
+ : null;
3679
3777
  const amendmentTargetIsResolved = suggestionAmendmentTargetIsResolved(
3680
3778
  editingSuggestionId,
3681
3779
  savedSuggestions,
@@ -3714,154 +3812,166 @@ function PageEditorSessionBody({
3714
3812
  setSuggestionPersistenceRevision((revision) => revision + 1);
3715
3813
  }, []);
3716
3814
 
3717
- const flushSuggestionDraft = useCallback(async () => {
3718
- if (!isSuggesting) return null;
3719
- if (isSubmittingSuggestions) return null;
3720
- const base = suggestionBaseRef.current;
3721
- if (!base) return null;
3722
- if (base.existingSuggestion && suggestionDraft === base.initialContent) {
3723
- setIsSuggesting(false);
3724
- suggestionBaseRef.current = null;
3725
- setEditingSuggestionId(null);
3726
- setSuggestionInitialSelection(null);
3727
- setSuggestionAmendmentConflict(false);
3728
- suggestionAmendmentKeysRef.current.clear();
3729
- return new Map<string, ResourceSuggestion>();
3730
- }
3731
- if (
3732
- base.existingSuggestion &&
3733
- (suggestionAmendmentConflict || amendmentTargetIsResolved)
3734
- ) {
3735
- setSuggestionAmendmentConflict(true);
3736
- return null;
3737
- }
3738
- if (suggestionDraft === base.baseContent) {
3739
- if (base.existingSuggestion) {
3740
- toast.error(t("editor.suggestionAmendmentEmpty"));
3815
+ const flushSuggestionDraft = useCallback(
3816
+ async ({ keepMode = false }: { keepMode?: boolean } = {}) => {
3817
+ if (!isSuggesting) return null;
3818
+ if (isSubmittingSuggestions) return null;
3819
+ const base = suggestionBaseRef.current;
3820
+ if (!base) return null;
3821
+ if (base.existingSuggestion && suggestionDraft === base.initialContent) {
3822
+ if (!keepMode) {
3823
+ setIsSuggesting(false);
3824
+ suggestionBaseRef.current = null;
3825
+ setEditingSuggestionId(null);
3826
+ setSuggestionInitialSelection(null);
3827
+ setSuggestionAmendmentConflict(false);
3828
+ suggestionAmendmentKeysRef.current.clear();
3829
+ }
3830
+ return new Map<string, ResourceSuggestion>();
3831
+ }
3832
+ if (
3833
+ base.existingSuggestion &&
3834
+ (suggestionAmendmentConflict || amendmentTargetIsResolved)
3835
+ ) {
3836
+ setSuggestionAmendmentConflict(true);
3741
3837
  return null;
3742
3838
  }
3743
- setIsSuggesting(false);
3744
- suggestionBaseRef.current = null;
3745
- setEditingSuggestionId(null);
3746
- setSuggestionInitialSelection(null);
3747
- setSuggestionAmendmentConflict(false);
3748
- createdSuggestionOperationsRef.current.clear();
3749
- return new Map<string, ResourceSuggestion>();
3750
- }
3751
- setIsSubmittingSuggestions(true);
3752
- try {
3753
- const operations = suggestionDraftOperations(base, suggestionDraft);
3754
- if (operations.length === 0) {
3839
+ if (suggestionDraft === base.baseContent) {
3755
3840
  if (base.existingSuggestion) {
3756
3841
  toast.error(t("editor.suggestionAmendmentEmpty"));
3757
3842
  return null;
3758
3843
  }
3759
- setIsSuggesting(false);
3760
- suggestionBaseRef.current = null;
3761
- setEditingSuggestionId(null);
3762
- setSuggestionInitialSelection(null);
3763
- setSuggestionAmendmentConflict(false);
3844
+ if (!keepMode) {
3845
+ setIsSuggesting(false);
3846
+ suggestionBaseRef.current = null;
3847
+ setEditingSuggestionId(null);
3848
+ setSuggestionInitialSelection(null);
3849
+ setSuggestionAmendmentConflict(false);
3850
+ createdSuggestionOperationsRef.current.clear();
3851
+ }
3764
3852
  return new Map<string, ResourceSuggestion>();
3765
3853
  }
3766
- let persisted: Map<string, ResourceSuggestion>;
3767
- if (base.existingSuggestion) {
3768
- const operationKey = JSON.stringify(operations);
3769
- const idempotencyKey =
3770
- suggestionAmendmentKeysRef.current.get(operationKey) ??
3771
- globalThis.crypto.randomUUID();
3772
- suggestionAmendmentKeysRef.current.set(operationKey, idempotencyKey);
3773
- const amended = await updateSuggestion.mutateAsync({
3774
- id: base.existingSuggestion.id,
3775
- observedRevision: base.existingSuggestion.revision,
3776
- idempotencyKey,
3777
- operations,
3778
- summary: t("editor.toolbar.suggestEdits"),
3779
- });
3780
- setLocallyCreatedSuggestions((current) => {
3781
- const byId = new Map(
3782
- current.map((suggestion) => [suggestion.id, suggestion]),
3854
+ setIsSubmittingSuggestions(true);
3855
+ try {
3856
+ const operations = suggestionDraftOperations(base, suggestionDraft);
3857
+ if (operations.length === 0) {
3858
+ if (base.existingSuggestion) {
3859
+ toast.error(t("editor.suggestionAmendmentEmpty"));
3860
+ return null;
3861
+ }
3862
+ if (!keepMode) {
3863
+ setIsSuggesting(false);
3864
+ suggestionBaseRef.current = null;
3865
+ setEditingSuggestionId(null);
3866
+ setSuggestionInitialSelection(null);
3867
+ setSuggestionAmendmentConflict(false);
3868
+ }
3869
+ return new Map<string, ResourceSuggestion>();
3870
+ }
3871
+ let persisted: Map<string, ResourceSuggestion>;
3872
+ if (base.existingSuggestion) {
3873
+ const operationKey = JSON.stringify(operations);
3874
+ const idempotencyKey =
3875
+ suggestionAmendmentKeysRef.current.get(operationKey) ??
3876
+ globalThis.crypto.randomUUID();
3877
+ suggestionAmendmentKeysRef.current.set(operationKey, idempotencyKey);
3878
+ const amended = await updateSuggestion.mutateAsync({
3879
+ id: base.existingSuggestion.id,
3880
+ observedRevision: base.existingSuggestion.revision,
3881
+ idempotencyKey,
3882
+ operations,
3883
+ summary: t("editor.toolbar.suggestEdits"),
3884
+ });
3885
+ setLocallyCreatedSuggestions((current) => {
3886
+ const byId = new Map(
3887
+ current.map((suggestion) => [suggestion.id, suggestion]),
3888
+ );
3889
+ byId.set(amended.id, amended);
3890
+ return [...byId.values()];
3891
+ });
3892
+ setSuggestionPersistenceRevision((revision) => revision + 1);
3893
+ persisted = new Map([
3894
+ [suggestionOperationKey(operations[0]!), amended],
3895
+ ]);
3896
+ } else {
3897
+ persisted = await persistSuggestionDraftOperations(
3898
+ operations,
3899
+ createdSuggestionOperationsRef.current,
3900
+ (operation, idempotencyKey) =>
3901
+ createSuggestion.mutateAsync({
3902
+ resourceType: "document",
3903
+ resourceId: documentId,
3904
+ adapterKind: "content.document-markdown",
3905
+ baseRevision: base.baseRevision,
3906
+ summary: t("editor.toolbar.suggestEdits"),
3907
+ idempotencyKey,
3908
+ operations: [operation],
3909
+ }),
3783
3910
  );
3784
- byId.set(amended.id, amended);
3785
- return [...byId.values()];
3786
- });
3787
- setSuggestionPersistenceRevision((revision) => revision + 1);
3788
- persisted = new Map([
3789
- [suggestionOperationKey(operations[0]!), amended],
3790
- ]);
3791
- } else {
3792
- persisted = await persistSuggestionDraftOperations(
3793
- operations,
3794
- createdSuggestionOperationsRef.current,
3795
- (operation, idempotencyKey) =>
3796
- createSuggestion.mutateAsync({
3797
- resourceType: "document",
3798
- resourceId: documentId,
3799
- adapterKind: "content.document-markdown",
3800
- baseRevision: base.baseRevision,
3801
- summary: t("editor.toolbar.suggestEdits"),
3802
- idempotencyKey,
3803
- operations: [operation],
3804
- }),
3805
- );
3911
+ adoptConfirmedSuggestions();
3912
+ }
3913
+ if (!keepMode) {
3914
+ setIsSuggesting(false);
3915
+ suggestionBaseRef.current = null;
3916
+ setEditingSuggestionId(null);
3917
+ setSuggestionInitialSelection(null);
3918
+ setSuggestionAmendmentConflict(false);
3919
+ createdSuggestionOperationsRef.current.clear();
3920
+ suggestionAmendmentKeysRef.current.clear();
3921
+ }
3922
+ return persisted;
3923
+ } catch (error) {
3806
3924
  adoptConfirmedSuggestions();
3807
- }
3808
- setIsSuggesting(false);
3809
- suggestionBaseRef.current = null;
3810
- setEditingSuggestionId(null);
3811
- setSuggestionInitialSelection(null);
3812
- setSuggestionAmendmentConflict(false);
3813
- createdSuggestionOperationsRef.current.clear();
3814
- suggestionAmendmentKeysRef.current.clear();
3815
- return persisted;
3816
- } catch (error) {
3817
- adoptConfirmedSuggestions();
3818
- if (error instanceof SuggestionFormattingMappingError) {
3819
- toast.error(t("editor.suggestionFormattingUnsupported"));
3820
- return null;
3821
- }
3822
- if (base.existingSuggestion && isSuggestionConflictActionError(error)) {
3823
- setSuggestionAmendmentConflict(true);
3824
- void suggestionsQuery.refetch();
3825
- void queryClient.invalidateQueries(documentQueryFilter(documentId));
3925
+ if (error instanceof SuggestionFormattingMappingError) {
3926
+ toast.error(t("editor.suggestionFormattingUnsupported"));
3927
+ return null;
3928
+ }
3929
+ if (base.existingSuggestion && isSuggestionConflictActionError(error)) {
3930
+ setSuggestionAmendmentConflict(true);
3931
+ void suggestionsQuery.refetch();
3932
+ void queryClient.invalidateQueries(documentQueryFilter(documentId));
3933
+ return null;
3934
+ }
3935
+ toast.error(
3936
+ t(
3937
+ base.existingSuggestion
3938
+ ? "editor.suggestionAmendmentFailed"
3939
+ : "editor.suggestionCreateFailed",
3940
+ ),
3941
+ {
3942
+ description: actionErrorMessage(error) ?? t("empty.genericError"),
3943
+ },
3944
+ );
3826
3945
  return null;
3946
+ } finally {
3947
+ setIsSubmittingSuggestions(false);
3827
3948
  }
3828
- toast.error(
3829
- t(
3830
- base.existingSuggestion
3831
- ? "editor.suggestionAmendmentFailed"
3832
- : "editor.suggestionCreateFailed",
3833
- ),
3834
- {
3835
- description: actionErrorMessage(error) ?? t("empty.genericError"),
3836
- },
3837
- );
3838
- return null;
3839
- } finally {
3840
- setIsSubmittingSuggestions(false);
3841
- }
3842
- }, [
3843
- createSuggestion,
3844
- updateSuggestion,
3845
- adoptConfirmedSuggestions,
3846
- documentId,
3847
- isSuggesting,
3848
- isSubmittingSuggestions,
3849
- amendmentTargetIsResolved,
3850
- queryClient,
3851
- suggestionAmendmentConflict,
3852
- suggestionDraft,
3853
- suggestionsQuery,
3854
- t,
3855
- ]);
3949
+ },
3950
+ [
3951
+ createSuggestion,
3952
+ updateSuggestion,
3953
+ adoptConfirmedSuggestions,
3954
+ documentId,
3955
+ isSuggesting,
3956
+ isSubmittingSuggestions,
3957
+ amendmentTargetIsResolved,
3958
+ queryClient,
3959
+ suggestionAmendmentConflict,
3960
+ suggestionDraft,
3961
+ suggestionsQuery,
3962
+ t,
3963
+ ],
3964
+ );
3856
3965
 
3857
3966
  const startSuggestionDraft = useCallback(
3858
3967
  (
3968
+ nextDocument: Document,
3859
3969
  suggestion?: ResourceSuggestion,
3860
3970
  initialSelection?: VisualEditorSelectionSnapshot | null,
3861
3971
  ) => {
3862
3972
  if (!canSuggest || isSuggesting) return false;
3863
3973
  try {
3864
- suggestionMarkedSourceRanges(document.content);
3974
+ suggestionMarkedSourceRanges(nextDocument.content);
3865
3975
  } catch (error) {
3866
3976
  if (!(error instanceof SuggestionFormattingMappingError)) throw error;
3867
3977
  toast.error(t("editor.suggestionFormattingBaselineUnsupported"));
@@ -3871,9 +3981,9 @@ function PageEditorSessionBody({
3871
3981
  ? editableSuggestionDraft({
3872
3982
  suggestion,
3873
3983
  currentUserEmail: session?.email,
3874
- canonicalContent: document.content,
3984
+ canonicalContent: nextDocument.content,
3875
3985
  canonicalRevision: canonicalSuggestionRevision(
3876
- document,
3986
+ nextDocument,
3877
3987
  suggestion,
3878
3988
  ),
3879
3989
  })
@@ -3886,11 +3996,11 @@ function PageEditorSessionBody({
3886
3996
  existing?.session ??
3887
3997
  createSuggestionDraftSession({
3888
3998
  id: globalThis.crypto.randomUUID(),
3889
- baseContent: document.content,
3890
- baseRevision: canonicalSuggestionRevision(document),
3999
+ baseContent: nextDocument.content,
4000
+ baseRevision: canonicalSuggestionRevision(nextDocument),
3891
4001
  startedAt: new Date().toISOString(),
3892
4002
  });
3893
- setSuggestionDraft(existing?.content ?? document.content);
4003
+ setSuggestionDraft(existing?.content ?? nextDocument.content);
3894
4004
  setSuggestionInitialSelection(
3895
4005
  existing?.caret ?? initialSelection ?? null,
3896
4006
  );
@@ -3899,13 +4009,127 @@ function PageEditorSessionBody({
3899
4009
  setIsSuggesting(true);
3900
4010
  return true;
3901
4011
  },
4012
+ [canSuggest, isSuggesting, session?.email, t],
4013
+ );
4014
+
4015
+ const prepareSuggestionDraftDocument = useCallback(async () => {
4016
+ const content = localContentRef.current;
4017
+ const title = localTitleRef.current;
4018
+ const pending = pendingDocumentSaveRef.current;
4019
+ if (
4020
+ !pending &&
4021
+ content === document.content &&
4022
+ title === document.title &&
4023
+ title === lastSavedTitleRef.current.title
4024
+ )
4025
+ return document;
4026
+ if (pending) {
4027
+ clearTimeout(pending.timeout);
4028
+ pendingDocumentSaveRef.current = null;
4029
+ saveTimeoutRef.current = null;
4030
+ }
4031
+ try {
4032
+ const saved = await queueDocumentSave(title, content, {
4033
+ historySessionId: pending?.historySessionId,
4034
+ contentEditVersion: contentEditVersionRef.current,
4035
+ });
4036
+ if (
4037
+ !saved.contentPersisted ||
4038
+ localContentRef.current !== content ||
4039
+ localTitleRef.current !== title ||
4040
+ lastSavedTitleRef.current.title !== title
4041
+ ) {
4042
+ toast.error(t("editor.suggestionCreateFailed"));
4043
+ return null;
4044
+ }
4045
+ const refreshedDocument = await callAction(
4046
+ "get-document",
4047
+ {
4048
+ id: documentId,
4049
+ ...(databaseId ? { databaseId } : {}),
4050
+ ...(databaseDocumentId ? { databaseDocumentId } : {}),
4051
+ },
4052
+ { method: "GET" },
4053
+ );
4054
+ if (
4055
+ refreshedDocument.content !== content ||
4056
+ refreshedDocument.title !== title
4057
+ ) {
4058
+ toast.error(t("editor.toolbar.conflict"));
4059
+ return null;
4060
+ }
4061
+ patchDocumentCaches(queryClient, documentId, refreshedDocument);
4062
+ return refreshedDocument;
4063
+ } catch (error) {
4064
+ toast.error(t("editor.suggestionCreateFailed"), {
4065
+ description:
4066
+ error instanceof Error ? error.message : t("empty.genericError"),
4067
+ });
4068
+ return null;
4069
+ }
4070
+ }, [
4071
+ databaseDocumentId,
4072
+ databaseId,
4073
+ document,
4074
+ documentId,
4075
+ queryClient,
4076
+ queueDocumentSave,
4077
+ t,
4078
+ ]);
4079
+
4080
+ const continueSuggestionModeFrom = useCallback((nextDocument: Document) => {
4081
+ createdSuggestionOperationsRef.current.clear();
4082
+ suggestionAmendmentKeysRef.current.clear();
4083
+ suggestionBaseRef.current = createSuggestionDraftSession({
4084
+ id: globalThis.crypto.randomUUID(),
4085
+ baseContent: nextDocument.content,
4086
+ baseRevision: canonicalSuggestionRevision(nextDocument),
4087
+ startedAt: new Date().toISOString(),
4088
+ });
4089
+ setSuggestionDraft(nextDocument.content);
4090
+ setEditingSuggestionId(null);
4091
+ setSuggestionInitialSelection(null);
4092
+ setSuggestionAmendmentConflict(false);
4093
+ setIsSuggesting(true);
4094
+ setSuggestionPersistenceRevision((revision) => revision + 1);
4095
+ }, []);
4096
+
4097
+ const refreshSuggestionDecisionDocument = useCallback(
4098
+ async (continueSuggesting: boolean) => {
4099
+ if (decisionRefreshInFlightRef.current) return;
4100
+ decisionRefreshInFlightRef.current = true;
4101
+ setDecisionRefreshFailed(false);
4102
+ try {
4103
+ const refreshedDocument = await callAction(
4104
+ "get-document",
4105
+ {
4106
+ id: documentId,
4107
+ ...(databaseId ? { databaseId } : {}),
4108
+ ...(databaseDocumentId ? { databaseDocumentId } : {}),
4109
+ },
4110
+ { method: "GET" },
4111
+ );
4112
+ patchDocumentCaches(queryClient, documentId, refreshedDocument);
4113
+ if (continueSuggesting) continueSuggestionModeFrom(refreshedDocument);
4114
+ setPendingSuggestionDecision(null);
4115
+ suggestionDecisionInFlightRef.current = false;
4116
+ } catch (error) {
4117
+ setDecisionRefreshFailed(true);
4118
+ void queryClient.invalidateQueries(documentQueryFilter(documentId));
4119
+ toast.error(t("empty.genericError"), {
4120
+ description:
4121
+ error instanceof Error ? error.message : t("empty.genericError"),
4122
+ });
4123
+ } finally {
4124
+ decisionRefreshInFlightRef.current = false;
4125
+ }
4126
+ },
3902
4127
  [
3903
- canSuggest,
3904
- document.content,
3905
- document.revision,
3906
- document.updatedAt,
3907
- isSuggesting,
3908
- session?.email,
4128
+ continueSuggestionModeFrom,
4129
+ databaseDocumentId,
4130
+ databaseId,
4131
+ documentId,
4132
+ queryClient,
3909
4133
  t,
3910
4134
  ],
3911
4135
  );
@@ -3913,28 +4137,40 @@ function PageEditorSessionBody({
3913
4137
  const handleSuggestionModeChange = useCallback(
3914
4138
  async (next: boolean) => {
3915
4139
  if (next) {
4140
+ if (isStartingSuggestion) return;
3916
4141
  const initialSelection = pageActionsSelectionRef.current;
3917
4142
  pageActionsSelectionRef.current = null;
3918
- // The captured range belongs to canonical content; an existing
3919
- // suggestion draft can have a different document and is edited through
3920
- // its own activation path.
3921
- if (!shouldResumeSelectedSuggestionFromPageActions(initialSelection)) {
3922
- const started = startSuggestionDraft(undefined, initialSelection);
3923
- pageActionsSelectionRef.current = null;
3924
- if (!started && initialSelection) {
4143
+ setIsStartingSuggestion(true);
4144
+ try {
4145
+ const readyDocument = await prepareSuggestionDraftDocument();
4146
+ if (!readyDocument) {
3925
4147
  restoreCapturedEditorSelection(
3926
4148
  editorSelectionControllerRef.current,
3927
4149
  initialSelection,
3928
4150
  );
4151
+ return;
3929
4152
  }
3930
- return;
3931
- }
3932
- pageActionsSelectionRef.current = null;
3933
- const selected = savedSuggestions.find(
3934
- (suggestion) => suggestion.id === selectedSuggestionId,
3935
- );
3936
- if (!selected || !startSuggestionDraft(selected)) {
3937
- startSuggestionDraft();
4153
+ if (
4154
+ !shouldResumeSelectedSuggestionFromPageActions(initialSelection)
4155
+ ) {
4156
+ if (
4157
+ !startSuggestionDraft(readyDocument, undefined, initialSelection)
4158
+ ) {
4159
+ restoreCapturedEditorSelection(
4160
+ editorSelectionControllerRef.current,
4161
+ initialSelection,
4162
+ );
4163
+ }
4164
+ return;
4165
+ }
4166
+ const selected = savedSuggestions.find(
4167
+ (suggestion) => suggestion.id === selectedSuggestionId,
4168
+ );
4169
+ if (!selected || !startSuggestionDraft(readyDocument, selected)) {
4170
+ startSuggestionDraft(readyDocument);
4171
+ }
4172
+ } finally {
4173
+ setIsStartingSuggestion(false);
3938
4174
  }
3939
4175
  return;
3940
4176
  }
@@ -3942,6 +4178,8 @@ function PageEditorSessionBody({
3942
4178
  },
3943
4179
  [
3944
4180
  flushSuggestionDraft,
4181
+ isStartingSuggestion,
4182
+ prepareSuggestionDraftDocument,
3945
4183
  savedSuggestions,
3946
4184
  selectedSuggestionId,
3947
4185
  startSuggestionDraft,
@@ -4015,8 +4253,15 @@ function PageEditorSessionBody({
4015
4253
  setEditingSuggestionId(null);
4016
4254
  setSuggestionInitialSelection(null);
4017
4255
  setSuggestionAmendmentConflict(false);
4256
+ setPendingSuggestionDecision(null);
4257
+ setDecisionRefreshFailed(false);
4258
+ setPreserveInlineReviewSpace(false);
4018
4259
  }, [documentId]);
4019
4260
 
4261
+ useEffect(() => {
4262
+ if (!isSuggesting) setPreserveInlineReviewSpace(false);
4263
+ }, [isSuggesting]);
4264
+
4020
4265
  const discardConflictedSuggestionDraft = useCallback(() => {
4021
4266
  setIsSuggesting(false);
4022
4267
  setSuggestionDraft(document.content);
@@ -4056,9 +4301,11 @@ function PageEditorSessionBody({
4056
4301
  }, [sessionDraftSuggestions, suggestionPersistenceRevision]);
4057
4302
 
4058
4303
  const visualSuggestions = useMemo<VisualEditorSuggestion[]>(() => {
4059
- const currentMarkdown = isSuggesting ? suggestionDraft : document.content;
4304
+ const currentMarkdown =
4305
+ pendingSuggestionDecisionContent ??
4306
+ (isSuggesting ? suggestionDraft : document.content);
4060
4307
  const byId = new Map<string, VisualEditorSuggestion>();
4061
- for (const suggestion of savedSuggestions) {
4308
+ for (const suggestion of presentedSuggestions) {
4062
4309
  if (suggestion.id === editingSuggestionId) continue;
4063
4310
  const presentation = suggestionPresentation(suggestion, currentMarkdown);
4064
4311
  if (presentation) byId.set(presentation.id, presentation);
@@ -4067,6 +4314,7 @@ function PageEditorSessionBody({
4067
4314
  sessionDraftSuggestions,
4068
4315
  createdSuggestionOperationsRef.current,
4069
4316
  )) {
4317
+ if (suggestion.id === pendingSuggestionDecision?.suggestion.id) continue;
4070
4318
  const operation = suggestion.operations[0]!;
4071
4319
  const before = operation.before as { changedText: string };
4072
4320
  const after = operation.after as {
@@ -4100,21 +4348,23 @@ function PageEditorSessionBody({
4100
4348
  document.content,
4101
4349
  editingSuggestionId,
4102
4350
  isSuggesting,
4103
- savedSuggestions,
4351
+ pendingSuggestionDecision,
4352
+ pendingSuggestionDecisionContent,
4353
+ presentedSuggestions,
4104
4354
  sessionDraftSuggestions,
4105
4355
  suggestionPersistenceRevision,
4106
4356
  suggestionDraft,
4107
4357
  ]);
4108
4358
  const sidebarSuggestions = useMemo(() => {
4109
4359
  if (!editingSuggestionId || sessionDraftSuggestions.length !== 1) {
4110
- return savedSuggestions;
4360
+ return presentedSuggestions;
4111
4361
  }
4112
- return savedSuggestions.map((suggestion) =>
4362
+ return presentedSuggestions.map((suggestion) =>
4113
4363
  suggestion.id === editingSuggestionId
4114
4364
  ? { ...suggestion, operations: sessionDraftSuggestions[0]!.operations }
4115
4365
  : suggestion,
4116
4366
  );
4117
- }, [editingSuggestionId, savedSuggestions, sessionDraftSuggestions]);
4367
+ }, [editingSuggestionId, presentedSuggestions, sessionDraftSuggestions]);
4118
4368
 
4119
4369
  useEffect(() => {
4120
4370
  void setClientAppState(
@@ -4123,7 +4373,7 @@ function PageEditorSessionBody({
4123
4373
  documentId,
4124
4374
  suggesting: isSuggesting,
4125
4375
  draftChanged: isSuggesting && suggestionDraft !== document.content,
4126
- pendingCount: savedSuggestions.filter(
4376
+ pendingCount: presentedSuggestions.filter(
4127
4377
  (suggestion) => suggestion.status === "pending",
4128
4378
  ).length,
4129
4379
  },
@@ -4136,7 +4386,7 @@ function PageEditorSessionBody({
4136
4386
  documentId,
4137
4387
  isSuggesting,
4138
4388
  suggestionDraft,
4139
- savedSuggestions,
4389
+ presentedSuggestions,
4140
4390
  ]);
4141
4391
 
4142
4392
  const handleContentSaveNow = useCallback(
@@ -4478,8 +4728,9 @@ function PageEditorSessionBody({
4478
4728
  const hasOpenCommentThreads =
4479
4729
  threads?.some((thread) => !thread.resolved) ?? false;
4480
4730
  const hasOpenSuggestions =
4481
- savedSuggestions.some((suggestion) => suggestion.status === "pending") ||
4482
- draftSuggestions.length > 0;
4731
+ presentedSuggestions.some(
4732
+ (suggestion) => suggestion.status === "pending",
4733
+ ) || draftSuggestions.length > 0;
4483
4734
  const hasSelectedCommentThread =
4484
4735
  !!selectedThreadId &&
4485
4736
  (threads?.some((thread) => thread.threadId === selectedThreadId) ?? false);
@@ -4492,6 +4743,12 @@ function PageEditorSessionBody({
4492
4743
  hasSelectedCommentThread,
4493
4744
  hasPendingComment: !!pendingComment,
4494
4745
  });
4746
+ const reserveInlineReviewSpace = documentEditorReservesInlineReviewSpace({
4747
+ showInlineComments,
4748
+ preserveInlineReviewSpace,
4749
+ hasInlineCommentSpace,
4750
+ isDatabasePage: Boolean(document.database),
4751
+ });
4495
4752
  const showDesktopInfoPanel = utilityPanel === "info" && hasUtilityRailSpace;
4496
4753
  const showDesktopRightRail = showInlineComments || showDesktopInfoPanel;
4497
4754
  const showAnchoredCommentPopover =
@@ -4607,7 +4864,7 @@ function PageEditorSessionBody({
4607
4864
  }, []);
4608
4865
 
4609
4866
  const activateInlineSuggestion = useCallback(
4610
- (suggestionId: string) => {
4867
+ async (suggestionId: string) => {
4611
4868
  if (
4612
4869
  isSuggesting &&
4613
4870
  suggestionBaseRef.current?.existingSuggestion?.id === suggestionId
@@ -4617,17 +4874,36 @@ function PageEditorSessionBody({
4617
4874
  const suggestion = savedSuggestions.find(
4618
4875
  (candidate) => candidate.id === suggestionId,
4619
4876
  );
4620
- if (suggestion && startSuggestionDraft(suggestion)) {
4621
- setPendingComment(null);
4622
- setHoveredThreadId(null);
4623
- setSelectedThreadId(null);
4624
- setSelectedSuggestionId(null);
4625
- setHoveredSuggestionId(null);
4626
- return;
4877
+ if (suggestion && !isStartingSuggestion) {
4878
+ setIsStartingSuggestion(true);
4879
+ try {
4880
+ const readyDocument = await prepareSuggestionDraftDocument();
4881
+ if (
4882
+ readyDocument &&
4883
+ startSuggestionDraft(readyDocument, suggestion)
4884
+ ) {
4885
+ setPendingComment(null);
4886
+ setHoveredThreadId(null);
4887
+ setSelectedThreadId(null);
4888
+ setSelectedSuggestionId(null);
4889
+ setHoveredSuggestionId(null);
4890
+ return;
4891
+ }
4892
+ if (!readyDocument) return;
4893
+ } finally {
4894
+ setIsStartingSuggestion(false);
4895
+ }
4627
4896
  }
4628
4897
  activateSuggestion(suggestionId);
4629
4898
  },
4630
- [activateSuggestion, isSuggesting, savedSuggestions, startSuggestionDraft],
4899
+ [
4900
+ activateSuggestion,
4901
+ isStartingSuggestion,
4902
+ isSuggesting,
4903
+ prepareSuggestionDraftDocument,
4904
+ savedSuggestions,
4905
+ startSuggestionDraft,
4906
+ ],
4631
4907
  );
4632
4908
  const handledCommentDeepLinkRef = useRef<string | null>(null);
4633
4909
 
@@ -5108,62 +5384,87 @@ function PageEditorSessionBody({
5108
5384
  suggestions={sidebarSuggestions}
5109
5385
  draftSuggestions={draftSuggestions}
5110
5386
  onMaterializeDraft={async (draft) => {
5111
- const persisted = await flushSuggestionDraft();
5387
+ const persisted = await flushSuggestionDraft({ keepMode: true });
5112
5388
  if (!persisted) return null;
5113
- const suggestion = persisted.get(
5114
- suggestionOperationKey(draft.operations[0]!),
5115
- );
5389
+ const suggestion = materializedSuggestionForDraft(persisted, draft);
5116
5390
  if (suggestion) setSelectedSuggestionId(suggestion.id);
5117
5391
  return suggestion ?? null;
5118
5392
  }}
5119
5393
  canDecideSuggestions={canEdit}
5120
- decidingSuggestion={(suggestionId) =>
5121
- decidingSuggestionIds.has(suggestionId)
5394
+ decidingSuggestion={() =>
5395
+ decideSuggestion.isPending ||
5396
+ isSubmittingSuggestions ||
5397
+ !!pendingSuggestionDecision
5122
5398
  }
5123
5399
  onDecideSuggestion={async (suggestion, decision) => {
5124
- if (decidingSuggestionIdsRef.current.has(suggestion.id)) return;
5400
+ if (
5401
+ suggestionDecisionInFlightRef.current ||
5402
+ pendingSuggestionDecision ||
5403
+ decideSuggestion.isPending ||
5404
+ isSubmittingSuggestions
5405
+ )
5406
+ return;
5407
+ suggestionDecisionInFlightRef.current = true;
5408
+ const continueSuggesting = isSuggesting;
5125
5409
  let observedSuggestion = suggestion;
5126
- if (decision === "accepted" && suggestion.id === editingSuggestionId) {
5127
- if (isSubmittingSuggestions) return;
5128
- const persisted = await flushSuggestionDraft();
5129
- if (!persisted) return;
5130
- observedSuggestion = [...persisted.values()][0] ?? suggestion;
5410
+ if (continueSuggesting) {
5411
+ const persisted = await flushSuggestionDraft({ keepMode: true });
5412
+ if (!persisted) {
5413
+ suggestionDecisionInFlightRef.current = false;
5414
+ return;
5415
+ }
5416
+ if (suggestion.id === editingSuggestionId) {
5417
+ observedSuggestion = [...persisted.values()][0] ?? suggestion;
5418
+ }
5131
5419
  }
5132
- if (decidingSuggestionIdsRef.current.has(observedSuggestion.id)) return;
5133
- decidingSuggestionIdsRef.current.add(observedSuggestion.id);
5134
- setDecidingSuggestionIds(new Set(decidingSuggestionIdsRef.current));
5420
+ if (showInlineComments) setPreserveInlineReviewSpace(true);
5421
+ setPendingSuggestionDecision({
5422
+ suggestion: observedSuggestion,
5423
+ decision,
5424
+ continueSuggesting,
5425
+ optimistic: true,
5426
+ });
5427
+ setDecisionRefreshFailed(false);
5428
+ let result: Awaited<ReturnType<typeof decideSuggestion.mutateAsync>>;
5135
5429
  try {
5136
- const result = await decideSuggestion.mutateAsync({
5430
+ result = await decideSuggestion.mutateAsync({
5137
5431
  id: observedSuggestion.id,
5138
5432
  decision,
5139
5433
  idempotencyKey: globalThis.crypto.randomUUID(),
5140
5434
  observedBase: observedSuggestion.baseRevision,
5141
5435
  observedRevision: observedSuggestion.revision,
5142
5436
  });
5143
- if (
5144
- result.suggestion.id === editingSuggestionId &&
5145
- result.suggestion.status !== "pending"
5146
- ) {
5147
- setIsSuggesting(false);
5148
- suggestionBaseRef.current = null;
5149
- setEditingSuggestionId(null);
5150
- setSuggestionInitialSelection(null);
5151
- suggestionAmendmentKeysRef.current.clear();
5152
- }
5153
- if (result.suggestion.status === "stale") {
5154
- toast.error(t("editor.toolbar.conflict"));
5155
- setSelectedSuggestionId(result.suggestion.id);
5156
- setUtilityPanel("comments");
5157
- setCommentsBrowseOpen(true);
5158
- }
5159
5437
  } catch (error) {
5438
+ setPendingSuggestionDecision(null);
5439
+ setDecisionRefreshFailed(false);
5440
+ suggestionDecisionInFlightRef.current = false;
5160
5441
  toast.error(t("empty.genericError"), {
5161
5442
  description:
5162
5443
  error instanceof Error ? error.message : t("empty.genericError"),
5163
5444
  });
5164
- } finally {
5165
- decidingSuggestionIdsRef.current.delete(observedSuggestion.id);
5166
- setDecidingSuggestionIds(new Set(decidingSuggestionIdsRef.current));
5445
+ return;
5446
+ }
5447
+ if (result.suggestion.status !== decision) {
5448
+ setPendingSuggestionDecision((current) =>
5449
+ current?.suggestion.id === observedSuggestion.id
5450
+ ? { ...current, suggestion: result.suggestion, optimistic: false }
5451
+ : current,
5452
+ );
5453
+ }
5454
+ setLocallyCreatedSuggestions((current) => {
5455
+ const byId = new Map(
5456
+ current.map((candidate) => [candidate.id, candidate]),
5457
+ );
5458
+ byId.set(result.suggestion.id, result.suggestion);
5459
+ return [...byId.values()];
5460
+ });
5461
+ void suggestionsQuery.refetch();
5462
+ await refreshSuggestionDecisionDocument(continueSuggesting);
5463
+ if (result.suggestion.status === "stale") {
5464
+ toast.error(t("editor.toolbar.conflict"));
5465
+ setSelectedSuggestionId(result.suggestion.id);
5466
+ setUtilityPanel("comments");
5467
+ setCommentsBrowseOpen(true);
5167
5468
  }
5168
5469
  }}
5169
5470
  canSuggest={canSuggest}
@@ -5397,8 +5698,16 @@ function PageEditorSessionBody({
5397
5698
  onOpenBreadcrumbItem={
5398
5699
  host === "page" ? handleOpenToolbarBreadcrumb : undefined
5399
5700
  }
5400
- canUndo={editorHistoryState.canUndo}
5401
- canRedo={editorHistoryState.canRedo}
5701
+ canUndo={
5702
+ isSuggesting
5703
+ ? editorHistoryStateRef.current.canUndo
5704
+ : editorHistoryState.canUndo
5705
+ }
5706
+ canRedo={
5707
+ isSuggesting
5708
+ ? editorHistoryStateRef.current.canRedo
5709
+ : editorHistoryState.canRedo
5710
+ }
5402
5711
  onUndo={() => editorHistoryControllerRef.current?.undo()}
5403
5712
  onRedo={() => editorHistoryControllerRef.current?.redo()}
5404
5713
  canSuggest={canSuggest}
@@ -5552,7 +5861,7 @@ function PageEditorSessionBody({
5552
5861
  className={cn(
5553
5862
  "min-w-0",
5554
5863
  showDesktopInfoPanel ? "flex-1" : "w-full",
5555
- showInlineComments && !isDatabasePage && "pr-80",
5864
+ reserveInlineReviewSpace && "pr-80",
5556
5865
  )}
5557
5866
  >
5558
5867
  <div
@@ -5770,6 +6079,19 @@ function PageEditorSessionBody({
5770
6079
  />
5771
6080
  </div>
5772
6081
  ) : null}
6082
+ {decisionRefreshFailed &&
6083
+ pendingSuggestionDecision ? (
6084
+ <div role="alert">
6085
+ <QueryErrorState
6086
+ compact
6087
+ onRetry={() =>
6088
+ void refreshSuggestionDecisionDocument(
6089
+ pendingSuggestionDecision.continueSuggesting,
6090
+ )
6091
+ }
6092
+ />
6093
+ </div>
6094
+ ) : null}
5773
6095
  {suggestionDraftPreview.status ===
5774
6096
  "unsupported-formatting" ? (
5775
6097
  <div
@@ -5781,6 +6103,11 @@ function PageEditorSessionBody({
5781
6103
  ) : null}
5782
6104
  <VisualEditor
5783
6105
  onEscape={handleEditorEscape}
6106
+ contentResetKey={
6107
+ pendingSuggestionDecision
6108
+ ? `${pendingSuggestionDecision.suggestion.id}:${pendingSuggestionDecision.decision}:${pendingSuggestionDecision.optimistic ? "optimistic" : "canonical"}`
6109
+ : null
6110
+ }
5784
6111
  key={`${visualEditorInstanceKey({
5785
6112
  documentId,
5786
6113
  documentUpdatedAt: document.updatedAt,
@@ -5795,9 +6122,10 @@ function PageEditorSessionBody({
5795
6122
  content={
5796
6123
  isLocalFileDocument
5797
6124
  ? localContent
5798
- : isSuggesting
5799
- ? suggestionDraft
5800
- : document.content
6125
+ : (pendingSuggestionDecisionContent ??
6126
+ (isSuggesting
6127
+ ? suggestionDraft
6128
+ : document.content))
5801
6129
  }
5802
6130
  contentUpdatedAt={
5803
6131
  isLocalFileDocument
@@ -5841,7 +6169,9 @@ function PageEditorSessionBody({
5841
6169
  user={currentUser}
5842
6170
  editable={
5843
6171
  suggestionEditorIsolation.editable &&
5844
- !isSubmittingSuggestions
6172
+ !isStartingSuggestion &&
6173
+ !isSubmittingSuggestions &&
6174
+ !pendingSuggestionDecision
5845
6175
  }
5846
6176
  suggesting={isSuggesting}
5847
6177
  localFileMode={isLocalFileDocument}
@@ -5872,7 +6202,9 @@ function PageEditorSessionBody({
5872
6202
  : undefined
5873
6203
  }
5874
6204
  initialSelection={suggestionInitialSelection}
5875
- onSuggestionAnchorsChange={setAnchoredSuggestionIds}
6205
+ onSuggestionAnchorsChange={
6206
+ handleSuggestionAnchorsChange
6207
+ }
5876
6208
  showCommentIndicators={showCommentIndicators}
5877
6209
  onJoinTitle={joinFirstBodyBlockToTitle}
5878
6210
  notionPageLinks={notionPageLinks}