@parhelia/core 0.3.12788 → 0.3.12798

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 (235) hide show
  1. package/dist/components/ui/LanguageSelector.js +4 -1
  2. package/dist/components/ui/LanguageSelector.js.map +1 -1
  3. package/dist/components/ui/PlaceholderInput.js +16 -2
  4. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  5. package/dist/components/ui/select.js +12 -1
  6. package/dist/components/ui/select.js.map +1 -1
  7. package/dist/editor/FieldHistory.js +14 -4
  8. package/dist/editor/FieldHistory.js.map +1 -1
  9. package/dist/editor/ai/InlineAiDialog.js +35 -8
  10. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  11. package/dist/editor/ai/agentTodoExtraction.d.ts +1 -0
  12. package/dist/editor/ai/agentTodoExtraction.js +3 -0
  13. package/dist/editor/ai/agentTodoExtraction.js.map +1 -1
  14. package/dist/editor/ai/terminal/agentSessionState.js +26 -4
  15. package/dist/editor/ai/terminal/agentSessionState.js.map +1 -1
  16. package/dist/editor/ai/terminal/components/AgentProjectProgress.d.ts +4 -3
  17. package/dist/editor/ai/terminal/components/AgentProjectProgress.js +34 -50
  18. package/dist/editor/ai/terminal/components/AgentProjectProgress.js.map +1 -1
  19. package/dist/editor/ai/terminal/components/AgentTerminalView.d.ts +2 -1
  20. package/dist/editor/ai/terminal/components/AgentTerminalView.js +2 -2
  21. package/dist/editor/ai/terminal/components/AgentTerminalView.js.map +1 -1
  22. package/dist/editor/ai/terminal/components/AgentThinkingIndicator.js +2 -1
  23. package/dist/editor/ai/terminal/components/AgentThinkingIndicator.js.map +1 -1
  24. package/dist/editor/ai/terminal/components/AiResponseMessage.js +55 -41
  25. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  26. package/dist/editor/ai/terminal/components/ApprovalCard.d.ts +1 -0
  27. package/dist/editor/ai/terminal/components/ApprovalCard.js +15 -1
  28. package/dist/editor/ai/terminal/components/ApprovalCard.js.map +1 -1
  29. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.d.ts +24 -0
  30. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.js +97 -0
  31. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.js.map +1 -0
  32. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js +2 -1
  33. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js.map +1 -1
  34. package/dist/editor/ai/terminal/components/TodoListPanel.js +5 -3
  35. package/dist/editor/ai/terminal/components/TodoListPanel.js.map +1 -1
  36. package/dist/editor/ai/terminal/components/ToolCallDisplay.d.ts +4 -1
  37. package/dist/editor/ai/terminal/components/ToolCallDisplay.js +33 -5
  38. package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
  39. package/dist/editor/ai/terminal/components/ValueDiff.d.ts +15 -0
  40. package/dist/editor/ai/terminal/components/ValueDiff.js +21 -0
  41. package/dist/editor/ai/terminal/components/ValueDiff.js.map +1 -0
  42. package/dist/editor/ai/terminal/components/agentThinkingStyles.d.ts +1 -0
  43. package/dist/editor/ai/terminal/components/agentThinkingStyles.js +2 -0
  44. package/dist/editor/ai/terminal/components/agentThinkingStyles.js.map +1 -0
  45. package/dist/editor/ai/terminal/components/toolCallContentRenderers.d.ts +15 -0
  46. package/dist/editor/ai/terminal/components/toolCallContentRenderers.js +12 -0
  47. package/dist/editor/ai/terminal/components/toolCallContentRenderers.js.map +1 -0
  48. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.d.ts +1 -0
  49. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.js +6 -1
  50. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.js.map +1 -1
  51. package/dist/editor/ai/terminal/useAgentStreamPipeline.js +1 -0
  52. package/dist/editor/ai/terminal/useAgentStreamPipeline.js.map +1 -1
  53. package/dist/editor/ai/terminal/useAgentTerminalController.js +1 -0
  54. package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
  55. package/dist/editor/ai/terminal/useAgentTerminalViewProps.d.ts +1 -0
  56. package/dist/editor/ai/terminal/useAgentTerminalViewProps.js +2 -1
  57. package/dist/editor/ai/terminal/useAgentTerminalViewProps.js.map +1 -1
  58. package/dist/editor/ai/types.d.ts +1 -0
  59. package/dist/editor/bridge/protocol.d.ts +11 -0
  60. package/dist/editor/bridge/protocol.js +1 -0
  61. package/dist/editor/bridge/protocol.js.map +1 -1
  62. package/dist/editor/client/EditorShell.js +184 -68
  63. package/dist/editor/client/EditorShell.js.map +1 -1
  64. package/dist/editor/client/editContext.d.ts +13 -3
  65. package/dist/editor/client/editContext.js.map +1 -1
  66. package/dist/editor/client/hooks/activeSessionSnapshots.d.ts +2 -0
  67. package/dist/editor/client/hooks/activeSessionSnapshots.js +43 -0
  68. package/dist/editor/client/hooks/activeSessionSnapshots.js.map +1 -0
  69. package/dist/editor/client/hooks/feedbackSocketState.js +3 -0
  70. package/dist/editor/client/hooks/feedbackSocketState.js.map +1 -1
  71. package/dist/editor/client/hooks/operationRefreshDeduplication.d.ts +22 -0
  72. package/dist/editor/client/hooks/operationRefreshDeduplication.js +45 -0
  73. package/dist/editor/client/hooks/operationRefreshDeduplication.js.map +1 -0
  74. package/dist/editor/client/hooks/socketMessageRetention.d.ts +7 -0
  75. package/dist/editor/client/hooks/socketMessageRetention.js +11 -0
  76. package/dist/editor/client/hooks/socketMessageRetention.js.map +1 -0
  77. package/dist/editor/client/hooks/useSocketMessageHandler.js +53 -19
  78. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  79. package/dist/editor/client/hooks/useWorkbox.d.ts +4 -0
  80. package/dist/editor/client/hooks/useWorkbox.js +132 -2
  81. package/dist/editor/client/hooks/useWorkbox.js.map +1 -1
  82. package/dist/editor/client/itemsRepository.d.ts +7 -1
  83. package/dist/editor/client/itemsRepository.js +55 -19
  84. package/dist/editor/client/itemsRepository.js.map +1 -1
  85. package/dist/editor/client/operations.d.ts +7 -3
  86. package/dist/editor/client/operations.js +216 -150
  87. package/dist/editor/client/operations.js.map +1 -1
  88. package/dist/editor/client/pageModelBuilder.js +21 -4
  89. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  90. package/dist/editor/client/sidebarAvailability.d.ts +4 -0
  91. package/dist/editor/client/sidebarAvailability.js +15 -0
  92. package/dist/editor/client/sidebarAvailability.js.map +1 -1
  93. package/dist/editor/client/waitForEditOperationTerminal.js +1 -1
  94. package/dist/editor/client/waitForEditOperationTerminal.js.map +1 -1
  95. package/dist/editor/commands/itemCommands.js +1 -1
  96. package/dist/editor/commands/itemCommands.js.map +1 -1
  97. package/dist/editor/content-tree/TreeSettingsMenu.js +1 -1
  98. package/dist/editor/content-tree/TreeSettingsMenu.js.map +1 -1
  99. package/dist/editor/field-types/RichTextEditorComponent.js +6 -1
  100. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  101. package/dist/editor/field-types/SingleLineText.js +2 -3
  102. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  103. package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +6 -1
  104. package/dist/editor/field-types/richtext/components/ReactSlate.js +91 -5
  105. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  106. package/dist/editor/field-types/singleLineTextValue.d.ts +3 -0
  107. package/dist/editor/field-types/singleLineTextValue.js +9 -0
  108. package/dist/editor/field-types/singleLineTextValue.js.map +1 -0
  109. package/dist/editor/menubar/ItemLanguageVersion.js +1 -1
  110. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  111. package/dist/editor/menubar/VersionSelector.js +18 -6
  112. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  113. package/dist/editor/menubar/WorkflowButton.js +8 -3
  114. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  115. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  116. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  117. package/dist/editor/notifications/NotificationCenter.js +26 -7
  118. package/dist/editor/notifications/NotificationCenter.js.map +1 -1
  119. package/dist/editor/notifications/useNotifications.d.ts +3 -1
  120. package/dist/editor/notifications/useNotifications.js +27 -9
  121. package/dist/editor/notifications/useNotifications.js.map +1 -1
  122. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +18 -0
  123. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
  124. package/dist/editor/page-editor-chrome/DragInsertionOverlay.js +54 -19
  125. package/dist/editor/page-editor-chrome/DragInsertionOverlay.js.map +1 -1
  126. package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js +30 -8
  127. package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js.map +1 -1
  128. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js +30 -8
  129. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js.map +1 -1
  130. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.d.ts +4 -0
  131. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js.map +1 -1
  132. package/dist/editor/page-editor-chrome/overlay/placeholderZones.js +23 -1
  133. package/dist/editor/page-editor-chrome/overlay/placeholderZones.js.map +1 -1
  134. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +5 -3
  135. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +111 -19
  136. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
  137. package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
  138. package/dist/editor/page-viewer/EditorForm.js +41 -12
  139. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  140. package/dist/editor/page-viewer/PageViewer.d.ts +2 -0
  141. package/dist/editor/page-viewer/PageViewer.js +11 -2
  142. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  143. package/dist/editor/page-viewer/PageViewerFrame.js +266 -44
  144. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  145. package/dist/editor/page-viewer/bridgeFieldPatch.js +1 -0
  146. package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -1
  147. package/dist/editor/page-viewer/bridgeInlineAiSelection.d.ts +17 -0
  148. package/dist/editor/page-viewer/bridgeInlineAiSelection.js +80 -0
  149. package/dist/editor/page-viewer/bridgeInlineAiSelection.js.map +1 -0
  150. package/dist/editor/page-viewer/bridgeInlineEditCommit.d.ts +18 -0
  151. package/dist/editor/page-viewer/bridgeInlineEditCommit.js +24 -0
  152. package/dist/editor/page-viewer/bridgeInlineEditCommit.js.map +1 -0
  153. package/dist/editor/page-viewer/bridgeInlineEditSession.d.ts +3 -0
  154. package/dist/editor/page-viewer/bridgeInlineEditSession.js +30 -0
  155. package/dist/editor/page-viewer/bridgeInlineEditSession.js.map +1 -1
  156. package/dist/editor/page-viewer/bridgeItemChangeTarget.d.ts +9 -0
  157. package/dist/editor/page-viewer/bridgeItemChangeTarget.js +38 -0
  158. package/dist/editor/page-viewer/bridgeItemChangeTarget.js.map +1 -0
  159. package/dist/editor/page-viewer/bridgePointerDragRelease.d.ts +31 -0
  160. package/dist/editor/page-viewer/bridgePointerDragRelease.js +32 -0
  161. package/dist/editor/page-viewer/bridgePointerDragRelease.js.map +1 -0
  162. package/dist/editor/page-viewer/renderUrl.d.ts +8 -0
  163. package/dist/editor/page-viewer/renderUrl.js +10 -0
  164. package/dist/editor/page-viewer/renderUrl.js.map +1 -1
  165. package/dist/editor/personalization/PersonalizationPanel.js +52 -25
  166. package/dist/editor/personalization/PersonalizationPanel.js.map +1 -1
  167. package/dist/editor/reviews/Comment.js +3 -1
  168. package/dist/editor/reviews/Comment.js.map +1 -1
  169. package/dist/editor/reviews/CommentDisplayPopover.js +2 -0
  170. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  171. package/dist/editor/reviews/Comments.js +16 -3
  172. package/dist/editor/reviews/Comments.js.map +1 -1
  173. package/dist/editor/reviews/SuggestedEdit.js +25 -2
  174. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  175. package/dist/editor/reviews/SuggestionCommentThread.js +2 -0
  176. package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
  177. package/dist/editor/reviews/applySuggestedEditActions.js +105 -25
  178. package/dist/editor/reviews/applySuggestedEditActions.js.map +1 -1
  179. package/dist/editor/reviews/commentState.d.ts +4 -0
  180. package/dist/editor/reviews/commentState.js +15 -0
  181. package/dist/editor/reviews/commentState.js.map +1 -0
  182. package/dist/editor/reviews/feedbackOrdering.d.ts +1 -0
  183. package/dist/editor/reviews/feedbackOrdering.js +6 -0
  184. package/dist/editor/reviews/feedbackOrdering.js.map +1 -1
  185. package/dist/editor/reviews/suggestionContentChange.d.ts +7 -0
  186. package/dist/editor/reviews/suggestionContentChange.js +10 -0
  187. package/dist/editor/reviews/suggestionContentChange.js.map +1 -1
  188. package/dist/editor/reviews/suggestionPatch.js +6 -0
  189. package/dist/editor/reviews/suggestionPatch.js.map +1 -1
  190. package/dist/editor/services/agentService.d.ts +6 -0
  191. package/dist/editor/services/agentService.js.map +1 -1
  192. package/dist/editor/services/personalizationService.js +30 -6
  193. package/dist/editor/services/personalizationService.js.map +1 -1
  194. package/dist/editor/services/suggestedEditsService.d.ts +1 -0
  195. package/dist/editor/services/suggestedEditsService.js +3 -0
  196. package/dist/editor/services/suggestedEditsService.js.map +1 -1
  197. package/dist/editor/settings/SettingsView.js +15 -7
  198. package/dist/editor/settings/SettingsView.js.map +1 -1
  199. package/dist/editor/settings/panels/MemoriesPanel.js +3 -2
  200. package/dist/editor/settings/panels/MemoriesPanel.js.map +1 -1
  201. package/dist/editor/settings/panels/ProjectTemplatesPanel.d.ts +7 -0
  202. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +232 -21
  203. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  204. package/dist/editor/settings/panels/projectTemplateDuplication.d.ts +5 -0
  205. package/dist/editor/settings/panels/projectTemplateDuplication.js +57 -0
  206. package/dist/editor/settings/panels/projectTemplateDuplication.js.map +1 -0
  207. package/dist/editor/setup-wizard/steps/CreateSettingsStep.js +9 -4
  208. package/dist/editor/setup-wizard/steps/CreateSettingsStep.js.map +1 -1
  209. package/dist/editor/sidebar/MainContentTree.js +1 -1
  210. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  211. package/dist/editor/ui/PerfectTree.js +2 -2
  212. package/dist/editor/ui/PerfectTree.js.map +1 -1
  213. package/dist/editor/ui/PublishRestrictionsDialog.js +9 -1
  214. package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
  215. package/dist/editor/utils.js +5 -2
  216. package/dist/editor/utils.js.map +1 -1
  217. package/dist/editor/views/EditorSlot.js +5 -1
  218. package/dist/editor/views/EditorSlot.js.map +1 -1
  219. package/dist/editor/views/SingleEditView.d.ts +4 -1
  220. package/dist/editor/views/SingleEditView.js +2 -2
  221. package/dist/editor/views/SingleEditView.js.map +1 -1
  222. package/dist/licensing/LicenseContext.js +7 -0
  223. package/dist/licensing/LicenseContext.js.map +1 -1
  224. package/dist/revision.d.ts +2 -2
  225. package/dist/revision.js +2 -2
  226. package/dist/task-board/TaskBoardWorkspace.js +10 -10
  227. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  228. package/dist/task-board/taskExecutionRecords.d.ts +1 -0
  229. package/dist/task-board/taskExecutionRecords.js +29 -0
  230. package/dist/task-board/taskExecutionRecords.js.map +1 -1
  231. package/dist/task-board/types.d.ts +9 -0
  232. package/dist/task-board/views/DependencyGraphView.d.ts +1 -0
  233. package/dist/task-board/views/DependencyGraphView.js +15 -12
  234. package/dist/task-board/views/DependencyGraphView.js.map +1 -1
  235. package/package.json +1 -1
@@ -22,7 +22,7 @@ import { loadFieldButtons } from "../services/editService";
22
22
  import { usePathname } from "../client/navigation";
23
23
  import { EditorWarnings } from "../EditorWarnings";
24
24
  import { FieldActionsOverlay, } from "../FieldActionsOverlay";
25
- import { alignLoopbackHostToEditor, alignXmCloudManagedEditingHostToEditor, applyParheliaRenderContext, getEditorQueryParam, } from "./renderUrl";
25
+ import { alignLoopbackHostToEditor, alignXmCloudManagedEditingHostToEditor, applyParheliaRenderContext, crossesPreviewRenderBoundary, getEditorQueryParam, } from "./renderUrl";
26
26
  import { NoLayout } from "../page-editor-chrome/NoLayout";
27
27
  import { Spinner } from "../ui/Spinner";
28
28
  import { DeviceToolbar } from "./DeviceToolbar";
@@ -33,11 +33,15 @@ import { pickVersionAtDate } from "../utils/versionAtDate";
33
33
  import { BridgeClient } from "../bridge/BridgeClient";
34
34
  import { beginPerfTrace, isPerfTraceActive, isPerfTraceEnabled, perfTraceStep, } from "../perfTrace";
35
35
  import { getPendingFieldSuggestions, getSuggestionSelectionKey, getSuggestionDisplayValue, } from "../reviews/suggestionDisplayValue";
36
+ import { bridgeItemDescriptorMatchesItem, bridgeStructureTargetsChangedItem, } from "./bridgeItemChangeTarget";
36
37
  import { buildBridgeFieldPatchPayload, buildBridgeFieldPatchSignature, buildRemoteCaretFieldPatches, getBridgeFieldPatchValue, hasRemoteCaretDraftForField, isBridgePatchableTextField, isSuggestionPreviewPatchSignature, } from "./bridgeFieldPatch";
37
38
  import { bridgeSelectionMatchesInteraction, resolveBridgeTextCompletionInput, } from "./bridgeTextCompletion";
39
+ import { resolveBridgeInlineAiSelectionRange } from "./bridgeInlineAiSelection";
40
+ import { BRIDGE_AUTHORITATIVE_REFRESH_EVENT, BRIDGE_INLINE_EDIT_COMMIT_EVENT, } from "./bridgeInlineEditCommit";
38
41
  import { logFieldValueDiagnostic, summarizeFieldDescriptor, summarizeFieldValue, } from "../fieldValueDiagnostics";
39
42
  import { createKeyboardEventFromBridgeInteraction, getBridgeFieldHistoryStepDirection, INLINE_FIELD_HISTORY_STEP_EVENT, } from "./bridgeKeyboardShortcuts";
40
43
  import { formatParentBridgeInjectionDiagnostic, injectParheliaBridgeFromParent, } from "./parentBridgeInjection";
44
+ import { projectParentPointerToHost, resolveParentBridgePointerRelease, } from "./bridgePointerDragRelease";
41
45
  import { getTextCompletion, normalizeTextCompletion, } from "../services/textCompletionService";
42
46
  import { resolvePageAndSiteByPath } from "../services/contentService";
43
47
  import { extractInternalLinkPath, extractInternalLinkTargetItemId, shouldOpenInternalLinkInNewSlot, } from "./internalLinkNavigation";
@@ -482,25 +486,6 @@ function bridgeComponentSelectionsMatch(left, right) {
482
486
  return false;
483
487
  return (left ?? []).every((leftId, index) => bridgeIdsMatch(leftId, right?.[index]));
484
488
  }
485
- function bridgeDescriptorMatchesItem(bridgeItem, item) {
486
- if (!bridgeItem?.id)
487
- return true;
488
- if (!bridgeIdsMatch(bridgeItem.id, item.id))
489
- return false;
490
- if (bridgeItem.language &&
491
- item.language &&
492
- bridgeItem.language.toLowerCase() !== item.language.toLowerCase()) {
493
- return false;
494
- }
495
- if (typeof bridgeItem.version === "number" &&
496
- Number.isFinite(bridgeItem.version) &&
497
- typeof item.version === "number" &&
498
- Number.isFinite(item.version) &&
499
- bridgeItem.version !== item.version) {
500
- return false;
501
- }
502
- return true;
503
- }
504
489
  function fieldIdentifierMatches(field, fieldId) {
505
490
  return (bridgeIdsMatch(field.id, fieldId) ||
506
491
  bridgeIdsMatch(field.name, fieldId) ||
@@ -516,7 +501,7 @@ function findModifiedBridgeField({ modifiedFields, structureField, field, itemDe
516
501
  return modifiedFields?.find((candidate) => {
517
502
  if (dirtyOnly && !candidate.isDirty)
518
503
  return false;
519
- if (!bridgeDescriptorMatchesItem(candidate.item, itemDescriptor)) {
504
+ if (!bridgeItemDescriptorMatchesItem(candidate.item, itemDescriptor)) {
520
505
  return false;
521
506
  }
522
507
  return (bridgeIdsMatch(candidate.fieldId, structureField.fieldId) ||
@@ -619,6 +604,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
619
604
  const iframeRef = useRef(null);
620
605
  const [iframeElement, setIframeElement] = useState(null);
621
606
  const bridgeClientRef = useRef(null);
607
+ const activeBridgePointerDragRef = useRef(null);
622
608
  const parentBridgeInjectionRef = useRef(null);
623
609
  const latestParentBridgeInjectionRef = useRef(null);
624
610
  useEffect(() => {
@@ -631,6 +617,15 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
631
617
  const bridgeTextRangeSourcesRef = useRef(new Map());
632
618
  const activeBridgeInlineEditRef = useRef(null);
633
619
  const discardActiveFieldDraftOnNextRefreshRef = useRef(false);
620
+ useEffect(() => {
621
+ const markAuthoritativeRefresh = () => {
622
+ discardActiveFieldDraftOnNextRefreshRef.current = true;
623
+ };
624
+ window.addEventListener(BRIDGE_AUTHORITATIVE_REFRESH_EVENT, markAuthoritativeRefresh);
625
+ return () => {
626
+ window.removeEventListener(BRIDGE_AUTHORITATIVE_REFRESH_EVENT, markAuthoritativeRefresh);
627
+ };
628
+ }, []);
634
629
  const [isBridgeInlineEditing, setIsBridgeInlineEditing] = useState(false);
635
630
  const bridgeInlineEditSessionRef = useRef(null);
636
631
  if (!bridgeInlineEditSessionRef.current) {
@@ -641,6 +636,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
641
636
  });
642
637
  }
643
638
  const bridgeInlineEditSession = bridgeInlineEditSessionRef.current;
639
+ const bridgeInlineEditCommitRef = useRef(null);
644
640
  const clearActiveBridgeInlineEdit = useCallback(() => {
645
641
  bridgeInlineEditSession.clear();
646
642
  }, [bridgeInlineEditSession]);
@@ -934,7 +930,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
934
930
  bridgeTextCompletionAbortRef.current?.abort();
935
931
  };
936
932
  }, []);
937
- const { beginInlineEdit: beginBridgeInlineEdit, clearInlineDedupe: clearBridgeInlineDedupe, endFieldFocus: endBridgeFieldFocus, handleFieldValueChanged: handleBridgeFieldValueChanged, handleInlineEditEnded: handleBridgeInlineEditEnded, } = useBridgeInlineEditing({
933
+ const { beginInlineEdit: beginBridgeInlineEdit, clearInlineDedupe: clearBridgeInlineDedupe, endFieldFocus: endBridgeFieldFocus, flushPendingFieldValueChanges: flushPendingBridgeFieldValueChanges, handleFieldValueChanged: handleBridgeFieldValueChanged, handleInlineEditEnded: handleBridgeInlineEditEnded, supersedeInlineEdit: supersedeBridgeInlineEdit, } = useBridgeInlineEditing({
938
934
  pageViewContextRef,
939
935
  inlineEditSession: bridgeInlineEditSession,
940
936
  blockIframeBlurUntil,
@@ -958,7 +954,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
958
954
  database: interaction.item.database,
959
955
  }
960
956
  : undefined;
961
- const started = await bridgeInlineEditSession.beginSession({
957
+ // A repeated click on an already-active host field must still revalidate
958
+ // the server lock. The inline editor can remain mounted after another UI
959
+ // transition released that lock, so UI-session identity alone is not proof
960
+ // of ownership. Pending begins remain deduplicated by the coordinator.
961
+ const started = await bridgeInlineEditSession.revalidateSession({
962
962
  elementKey: interaction.elementKey,
963
963
  fieldId: interaction.fieldId,
964
964
  item: interactionItem,
@@ -975,6 +975,50 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
975
975
  clearActiveBridgeInlineEdit();
976
976
  endBridgeFieldFocus();
977
977
  }, [clearActiveBridgeInlineEdit, endBridgeFieldFocus]);
978
+ const commitActiveBridgeInlineEdit = useCallback(() => {
979
+ if (bridgeInlineEditCommitRef.current) {
980
+ return bridgeInlineEditCommitRef.current;
981
+ }
982
+ const activeEdit = activeBridgeInlineEditRef.current;
983
+ if (!activeEdit) {
984
+ return flushPendingBridgeFieldValueChanges().then(() => undefined);
985
+ }
986
+ const bridge = bridgeClientRef.current;
987
+ if (!bridge) {
988
+ return Promise.reject(new Error("Cannot commit the inline edit because the bridge is unavailable."));
989
+ }
990
+ const commit = bridge
991
+ .requestCommand("commitInlineEdit", {
992
+ elementKey: activeEdit.elementKey,
993
+ fieldId: activeEdit.fieldId,
994
+ }, "inlineEditEnded")
995
+ .then(async () => {
996
+ // The correlated acknowledgement is dispatched after fieldValueChanged.
997
+ // Drain its repository update, then flush the debounced backend save.
998
+ await flushPendingBridgeFieldValueChanges();
999
+ await editContextRef.current?.operations.onFieldBlur?.();
1000
+ })
1001
+ .finally(() => {
1002
+ if (bridgeInlineEditCommitRef.current === commit) {
1003
+ bridgeInlineEditCommitRef.current = null;
1004
+ }
1005
+ });
1006
+ bridgeInlineEditCommitRef.current = commit;
1007
+ return commit;
1008
+ }, [editContextRef, flushPendingBridgeFieldValueChanges]);
1009
+ useEffect(() => {
1010
+ const handleCommitRequest = (event) => {
1011
+ const detail = event
1012
+ .detail;
1013
+ if (!detail)
1014
+ return;
1015
+ detail.barriers.push(commitActiveBridgeInlineEdit());
1016
+ };
1017
+ window.addEventListener(BRIDGE_INLINE_EDIT_COMMIT_EVENT, handleCommitRequest);
1018
+ return () => {
1019
+ window.removeEventListener(BRIDGE_INLINE_EDIT_COMMIT_EVENT, handleCommitRequest);
1020
+ };
1021
+ }, [commitActiveBridgeInlineEdit]);
978
1022
  const beginTrackedBridgeInlineEditAfterSelection = useCallback((interaction) => {
979
1023
  window.setTimeout(() => {
980
1024
  void beginTrackedBridgeInlineEdit(interaction);
@@ -997,22 +1041,36 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
997
1041
  activeInlineEdit: activeEdit,
998
1042
  field: summarizeFieldDescriptor(field),
999
1043
  });
1000
- clearActiveBridgeInlineEdit();
1044
+ // The form is now authoritative for this field. Superseding closes the
1045
+ // host-side contenteditable session and tombstones any already-posted
1046
+ // draft events, so the form patch is neither rejected by the host nor
1047
+ // raced back to the previous canvas value.
1048
+ supersedeBridgeInlineEdit(activeEdit);
1001
1049
  bridgePatchSignatureRef.current.delete(`${activeEdit.elementKey}:${activeEdit.fieldId}`);
1002
1050
  };
1003
1051
  window.addEventListener(BRIDGE_INLINE_EDIT_RELEASE_EVENT, handleBridgeInlineEditRelease);
1004
1052
  return () => {
1005
1053
  window.removeEventListener(BRIDGE_INLINE_EDIT_RELEASE_EVENT, handleBridgeInlineEditRelease);
1006
1054
  };
1007
- }, [clearActiveBridgeInlineEdit]);
1055
+ }, [supersedeBridgeInlineEdit]);
1008
1056
  useEffect(() => {
1009
1057
  return editContext.registerModeChangeParticipant({
1010
- beforeModeChange: (nextMode) => {
1058
+ captureBeforeModeChange: () => commitActiveBridgeInlineEdit(),
1059
+ beforeModeChange: async (nextMode) => {
1011
1060
  if (nextMode === "suggestions" &&
1012
1061
  getSelectedRangeInlineEditInteraction(editContextRef.current?.selectedRange)) {
1062
+ // Keep the live selection/focus so it can seed the suggestion edit,
1063
+ // but still cross the persistence boundary for text typed in edit
1064
+ // mode. Skipping this flush could make suggestions use an older
1065
+ // repository value as their baseline.
1066
+ await flushPendingBridgeFieldValueChanges();
1067
+ await editContext.operations.onFieldBlur?.();
1068
+ await editContext.operations.prepareForModeChange?.();
1013
1069
  return;
1014
1070
  }
1015
- editContext.operations.onFieldBlur?.();
1071
+ await flushPendingBridgeFieldValueChanges();
1072
+ await editContext.operations.onFieldBlur?.();
1073
+ await editContext.operations.prepareForModeChange?.();
1016
1074
  void fieldsContextRef.current?.setFocusedField(undefined, false);
1017
1075
  clearActiveBridgeInlineEdit();
1018
1076
  endBridgeFieldFocus();
@@ -1024,10 +1082,12 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1024
1082
  }, [
1025
1083
  clearBridgeInlineDedupe,
1026
1084
  clearActiveBridgeInlineEdit,
1085
+ commitActiveBridgeInlineEdit,
1027
1086
  editContext,
1028
1087
  editContextRef,
1029
1088
  endBridgeFieldFocus,
1030
1089
  fieldsContextRef,
1090
+ flushPendingBridgeFieldValueChanges,
1031
1091
  ]);
1032
1092
  const getBridgePatchDisplayValue = useCallback(({ field, structureField, itemDescriptor, preferRepositoryValue, }) => {
1033
1093
  const showSuggestions = editContext.mode === "suggestions";
@@ -1069,19 +1129,23 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1069
1129
  fieldsContext?.modifiedFields,
1070
1130
  pageViewContext.pageItemDescriptor,
1071
1131
  ]);
1072
- const buildBridgeFieldPatch = useCallback(({ field, structureField, itemDescriptor, preferRepositoryValue, }) => {
1132
+ const buildBridgeFieldPatch = useCallback(({ field, structureField, itemDescriptor, preferRepositoryValue, replaceActiveInlineEdit, }) => {
1073
1133
  const display = getBridgePatchDisplayValue({
1074
1134
  field,
1075
1135
  structureField,
1076
1136
  itemDescriptor,
1077
1137
  preferRepositoryValue,
1078
1138
  });
1079
- const activeInlineEdit = activeBridgeInlineEditRef.current;
1139
+ const effectiveDisplay = replaceActiveInlineEdit
1140
+ ? { ...display, source: "reset" }
1141
+ : display;
1080
1142
  const patch = buildBridgeFieldPatchPayload({
1081
1143
  field,
1082
1144
  structureField,
1083
- display,
1084
- activeInlineEdit,
1145
+ display: effectiveDisplay,
1146
+ activeInlineEdit: replaceActiveInlineEdit
1147
+ ? undefined
1148
+ : activeBridgeInlineEditRef.current,
1085
1149
  });
1086
1150
  return patch;
1087
1151
  }, [getBridgePatchDisplayValue]);
@@ -1165,6 +1229,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1165
1229
  structureFields,
1166
1230
  activeInlineEdit: activeBridgeInlineEditRef.current,
1167
1231
  });
1232
+ // Keep the host informed about the complete live-draft set. Unlike an
1233
+ // ordinary field patch, this state survives host-app/React renders: the
1234
+ // bridge reapplies these values in its mutation observer before other
1235
+ // observers can see stale server markup.
1236
+ bridge.sendCommand("syncRemoteFieldDrafts", { drafts: patches });
1168
1237
  for (const patch of patches) {
1169
1238
  const structureField = structureFields.find((field) => field.elementKey === patch.elementKey);
1170
1239
  sendBridgeFieldPatch(bridge, patch, structureField?.textContent);
@@ -1643,6 +1712,13 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1643
1712
  }
1644
1713
  const iframeRect = iframe.getBoundingClientRect();
1645
1714
  const bridgeSelection = latestBridgeSelectionRef.current;
1715
+ const commandRange = resolveBridgeInlineAiSelectionRange({
1716
+ interaction,
1717
+ bridgeSelection,
1718
+ iframeRect,
1719
+ fallbackItem: pageViewContextRef.current?.page?.item ??
1720
+ pageViewContextRef.current?.pageItemDescriptor,
1721
+ });
1646
1722
  const fallbackCaretRect = !interaction.selectionRect &&
1647
1723
  !interaction.caretRect &&
1648
1724
  bridgeSelection?.collapsed &&
@@ -1655,7 +1731,9 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1655
1731
  interaction.caretRect ??
1656
1732
  fallbackCaretRect;
1657
1733
  const toolbarRect = await waitForVisibleBridgeToolbarRect();
1658
- const selectedRange = editContextRef.current?.selectedRange ?? editor?.selectedRange;
1734
+ const selectedRange = commandRange ??
1735
+ editContextRef.current?.selectedRange ??
1736
+ editor?.selectedRange;
1659
1737
  const toolbarAnchor = getInlineAiAnchorFromBridgeToolbar(selectedRange, toolbarRect ?? getVisibleBridgeToolbarRect());
1660
1738
  const caretAnchor = toolbarAnchor
1661
1739
  ? toolbarAnchor
@@ -1678,7 +1756,9 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
1678
1756
  document.dispatchEvent(new CustomEvent("inline-ai-open", {
1679
1757
  bubbles: true,
1680
1758
  cancelable: true,
1681
- detail: caretAnchor ? { anchor: caretAnchor } : undefined,
1759
+ detail: caretAnchor || commandRange
1760
+ ? { anchor: caretAnchor, selectionRange: commandRange }
1761
+ : undefined,
1682
1762
  }));
1683
1763
  return;
1684
1764
  }
@@ -2045,6 +2125,62 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2045
2125
  useEffect(() => {
2046
2126
  pageViewContext.setEditorIframe(iframeElement);
2047
2127
  }, [iframeElement, pageViewContext.setEditorIframe]);
2128
+ // A host drag normally ends inside the iframe and is forwarded by the
2129
+ // bridge. If Chromium loses pointer capture while the pointer crosses into
2130
+ // parent-rendered editor chrome, however, the release belongs to this
2131
+ // document and the iframe never receives pointerup. Finish that same drag
2132
+ // here so it cannot leave the editor stuck in insertion mode.
2133
+ useEffect(() => {
2134
+ const iframe = iframeElement;
2135
+ if (!iframe)
2136
+ return;
2137
+ const forwardParentPointerMove = (event) => {
2138
+ const active = activeBridgePointerDragRef.current;
2139
+ if (!active || event.pointerId !== active.pointerId)
2140
+ return;
2141
+ const projected = projectParentPointerToHost(event.clientX, event.clientY, iframe.getBoundingClientRect(), pageViewContextRef.current?.bridgeGeometry?.viewport);
2142
+ if (!projected.inside)
2143
+ return;
2144
+ active.clientX = projected.clientX;
2145
+ active.clientY = projected.clientY;
2146
+ iframe.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2147
+ detail: {
2148
+ phase: "move",
2149
+ pointerId: active.pointerId,
2150
+ clientX: projected.clientX,
2151
+ clientY: projected.clientY,
2152
+ },
2153
+ }));
2154
+ };
2155
+ const finishBridgePointerDrag = (event) => {
2156
+ const active = activeBridgePointerDragRef.current;
2157
+ if (!active)
2158
+ return;
2159
+ const rect = iframe.getBoundingClientRect();
2160
+ const payload = resolveParentBridgePointerRelease(active, event, rect, pageViewContextRef.current?.bridgeGeometry?.viewport);
2161
+ if (!payload)
2162
+ return;
2163
+ // Clear before dispatching and before asking the host to clean up. Any
2164
+ // delayed move/end messages from the bridge then become harmless echoes.
2165
+ activeBridgePointerDragRef.current = null;
2166
+ iframe.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2167
+ detail: {
2168
+ phase: "end",
2169
+ ...payload,
2170
+ },
2171
+ }));
2172
+ bridgeClientRef.current?.sendCommand("cancelPointerDrag", {});
2173
+ };
2174
+ window.addEventListener("pointermove", forwardParentPointerMove, true);
2175
+ window.addEventListener("pointerup", finishBridgePointerDrag, true);
2176
+ window.addEventListener("mouseup", finishBridgePointerDrag, true);
2177
+ return () => {
2178
+ window.removeEventListener("pointermove", forwardParentPointerMove, true);
2179
+ window.removeEventListener("pointerup", finishBridgePointerDrag, true);
2180
+ window.removeEventListener("mouseup", finishBridgePointerDrag, true);
2181
+ activeBridgePointerDragRef.current = null;
2182
+ };
2183
+ }, [iframeElement]);
2048
2184
  // Same-origin fast path for overlay scroll tracking. The bridge's
2049
2185
  // scrollChanged postMessage is delivered as a task after the host's
2050
2186
  // rendering update, so overlays driven by it paint at least one frame
@@ -2173,8 +2309,15 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2173
2309
  // Ready confirms the bridge transport, not that its initial
2174
2310
  // mutation-driven geometry snapshot reached the editor. Query the
2175
2311
  // current DOM explicitly so editing chrome never depends on that
2176
- // first unsolicited event surviving startup contention.
2177
- bridge.sendCommand("queryGeometry", {});
2312
+ // first unsolicited event surviving startup contention. A newly
2313
+ // connected iframe has no memory of the text-range union registered
2314
+ // by comments, suggestions, locks, and version diffs, so replay it
2315
+ // here. Sending a bare query can otherwise clear those ranges while
2316
+ // the contributing overlay still has the same request signature and
2317
+ // therefore has no reason to emit it again.
2318
+ bridge.sendCommand("queryGeometry", {
2319
+ textRanges: Array.from(bridgeTextRangeSourcesRef.current.values()).flat(),
2320
+ });
2178
2321
  if (pendingRefreshScrollRef.current) {
2179
2322
  restorePendingRefreshScroll(iframe, pendingRefreshScrollRef.current);
2180
2323
  }
@@ -2205,6 +2348,29 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2205
2348
  case "pageSkeletonUpdated":
2206
2349
  pageViewContext.setPageSkeleton(event.payload.pageSkeleton);
2207
2350
  break;
2351
+ case "resizeChanged": {
2352
+ const currentGeometry = pageViewContext.bridgeGeometry;
2353
+ const viewportChanged = !currentGeometry ||
2354
+ currentGeometry.viewport.width !== event.payload.viewport.width ||
2355
+ currentGeometry.viewport.height !== event.payload.viewport.height ||
2356
+ currentGeometry.devicePixelRatio !==
2357
+ event.payload.devicePixelRatio;
2358
+ if (!viewportChanged)
2359
+ break;
2360
+ // Target rects are viewport-relative. Once the host reports a new
2361
+ // viewport, retaining the previous snapshot can visibly displace
2362
+ // chrome until the host's debounced geometry update arrives (for
2363
+ // example, when the minimap narrows the canvas). Remove that stale
2364
+ // snapshot immediately and request its replacement now, preserving
2365
+ // every overlay's registered text-range query.
2366
+ pageViewContext.bridgeGeometry = undefined;
2367
+ pageViewContext.setBridgeGeometry(undefined);
2368
+ scheduleBridgeGeometryRevision();
2369
+ bridge.sendCommand("queryGeometry", {
2370
+ textRanges: Array.from(bridgeTextRangeSourcesRef.current.values()).flat(),
2371
+ });
2372
+ break;
2373
+ }
2208
2374
  case "geometryUpdated": {
2209
2375
  if (isPerfTraceActive()) {
2210
2376
  perfTraceStep("frame:geometry-updated-received", {
@@ -2373,21 +2539,39 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2373
2539
  void handleBridgeInteraction(event.payload, iframe);
2374
2540
  break;
2375
2541
  case "dragGestureStarted":
2542
+ activeBridgePointerDragRef.current = {
2543
+ pointerId: event.payload.pointerId,
2544
+ clientX: event.payload.clientX,
2545
+ clientY: event.payload.clientY,
2546
+ };
2376
2547
  iframe?.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2377
2548
  detail: { phase: "start", ...event.payload },
2378
2549
  }));
2379
2550
  break;
2380
- case "dragGestureMoved":
2551
+ case "dragGestureMoved": {
2552
+ const active = activeBridgePointerDragRef.current;
2553
+ if (!active || active.pointerId !== event.payload.pointerId)
2554
+ break;
2555
+ active.clientX = event.payload.clientX;
2556
+ active.clientY = event.payload.clientY;
2381
2557
  iframe?.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2382
2558
  detail: { phase: "move", ...event.payload },
2383
2559
  }));
2384
2560
  break;
2385
- case "dragGestureEnded":
2561
+ }
2562
+ case "dragGestureEnded": {
2563
+ const active = activeBridgePointerDragRef.current;
2564
+ if (!active || active.pointerId !== event.payload.pointerId)
2565
+ break;
2566
+ activeBridgePointerDragRef.current = null;
2386
2567
  iframe?.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2387
2568
  detail: { phase: "end", ...event.payload },
2388
2569
  }));
2389
2570
  break;
2571
+ }
2390
2572
  case "dragSnapshot":
2573
+ if (!activeBridgePointerDragRef.current)
2574
+ break;
2391
2575
  iframe?.dispatchEvent(new CustomEvent(IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, {
2392
2576
  detail: { phase: "snapshot", ...event.payload },
2393
2577
  }));
@@ -2463,17 +2647,41 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2463
2647
  return;
2464
2648
  const patchedElementKeys = new Set();
2465
2649
  const patches = [];
2650
+ const pageItemDescriptor = pageViewContextRef.current?.pageItemDescriptor ??
2651
+ pageViewContextRef.current?.page?.item?.descriptor;
2466
2652
  for (const structureField of structure.fields) {
2467
2653
  if (!structureField.elementKey)
2468
2654
  continue;
2469
2655
  if (patchedElementKeys.has(structureField.elementKey))
2470
2656
  continue;
2471
- if (!bridgeDescriptorMatchesItem(structureField.item, change.item)) {
2657
+ const targetItem = structureField.item?.id
2658
+ ? structureField.item
2659
+ : pageItemDescriptor;
2660
+ if (!targetItem ||
2661
+ !bridgeItemDescriptorMatchesItem(targetItem, change.item)) {
2472
2662
  continue;
2473
2663
  }
2474
2664
  if (!bridgeFieldMatchesChangedField(structureField.fieldId, changedFieldId, field)) {
2475
2665
  continue;
2476
2666
  }
2667
+ if (change.replaceActiveInlineEdit) {
2668
+ const identity = {
2669
+ elementKey: structureField.elementKey,
2670
+ fieldId: structureField.fieldId,
2671
+ item: structureField.item,
2672
+ };
2673
+ const inlineEdit = bridgeInlineEditSession.matches(identity)
2674
+ ? bridgeInlineEditSession.getState()
2675
+ : bridgeInlineEditSession.getRecentlyClearedState(identity);
2676
+ if (inlineEdit) {
2677
+ // End the canvas session before sending the reset patch. This
2678
+ // prevents a later host refresh from preserving the superseded
2679
+ // draft, and tombstones already-captured events from that
2680
+ // session so they cannot race the external edit back to Sitecore.
2681
+ supersedeBridgeInlineEdit(inlineEdit);
2682
+ bridgePatchSignatureRef.current.delete(`${structureField.elementKey}:${structureField.fieldId}`);
2683
+ }
2684
+ }
2477
2685
  // A remote session is live-syncing this field: its draft is
2478
2686
  // authoritative, and the saved-value echo behind this change is
2479
2687
  // stale mid-typing — applying it would roll the canvas text back
@@ -2490,6 +2698,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2490
2698
  structureField,
2491
2699
  itemDescriptor: change.item,
2492
2700
  preferRepositoryValue: change.source === "local-field-update",
2701
+ replaceActiveInlineEdit: change.replaceActiveInlineEdit,
2493
2702
  });
2494
2703
  if (!patch)
2495
2704
  continue;
@@ -2506,6 +2715,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2506
2715
  };
2507
2716
  const asyncFieldChanges = [];
2508
2717
  for (const change of fieldChanges) {
2718
+ const pageItemDescriptor = pageViewContextRef.current?.pageItemDescriptor ??
2719
+ pageViewContextRef.current?.page?.item?.descriptor;
2720
+ if (!bridgeStructureTargetsChangedItem(structure.fields, change.item, pageItemDescriptor)) {
2721
+ continue;
2722
+ }
2509
2723
  const localFieldsById = new Map((change.changes.fieldValues ?? []).map((field) => [
2510
2724
  field.id.toLowerCase(),
2511
2725
  field,
@@ -2558,8 +2772,10 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2558
2772
  };
2559
2773
  }, [
2560
2774
  buildBridgeFieldPatch,
2775
+ bridgeInlineEditSession,
2561
2776
  editContext.itemsRepository,
2562
2777
  sendBridgeFieldPatch,
2778
+ supersedeBridgeInlineEdit,
2563
2779
  ]);
2564
2780
  useEffect(() => {
2565
2781
  const bridge = bridgeClientRef.current;
@@ -2896,15 +3112,17 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
2896
3112
  const fullReloadRequested = lastFullReloadTokenRef.current !== undefined &&
2897
3113
  lastFullReloadTokenRef.current !== editContext.fullReloadToken;
2898
3114
  lastFullReloadTokenRef.current = editContext.fullReloadToken;
2899
- const shouldUseIframeSrcReload = initialLoad ||
2900
- pageViewContext.fullscreen ||
2901
- iframeOriginChanged ||
2902
- fullReloadRequested;
2903
3115
  const signatureUrl = new URL(renderUrl.toString());
2904
3116
  signatureUrl.searchParams.delete("edit_rev");
2905
3117
  const renderSignature = `${signatureUrl.toString()}|rev:${editContext.revision}`;
2906
- const modeChanged = lastRenderModeRef.current !== editContext.mode;
3118
+ const previousRenderMode = lastRenderModeRef.current;
3119
+ const modeChanged = previousRenderMode !== editContext.mode;
3120
+ const previewRenderBoundaryChanged = crossesPreviewRenderBoundary(previousRenderMode, editContext.mode);
2907
3121
  lastRenderModeRef.current = editContext.mode;
3122
+ const shouldUseIframeSrcReload = initialLoad ||
3123
+ iframeOriginChanged ||
3124
+ fullReloadRequested ||
3125
+ previewRenderBoundaryChanged;
2908
3126
  // Only skip when a live bridge confirms the last refresh actually reached
2909
3127
  // the host, or when the only meaningful trigger is an edit/suggestions
2910
3128
  // mode change. Mode is delivered by setEditorMode and cannot change the
@@ -3358,6 +3576,10 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
3358
3576
  }
3359
3577
  }, children: [_jsx("iframe", { ref: bindIframeRef, className: "page-iframe h-full w-full bg-white transition-[height] duration-300 ease-in-out motion-reduce:transition-none", style: {
3360
3578
  height: deviceHeight,
3579
+ // The iframe element exists before its document has loaded. Keep
3580
+ // that blank document out of both the UI and accessibility tree;
3581
+ // consumers can treat a visible canvas as a rendered canvas.
3582
+ visibility: loadedIframeSrc ? "visible" : "hidden",
3361
3583
  cursor: showSuggestionsCursor
3362
3584
  ? SUGGESTIONS_CURSOR_VALUE
3363
3585
  : undefined,
@@ -3375,7 +3597,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
3375
3597
  parentBridgeInjectionRef.current.attempt;
3376
3598
  }
3377
3599
  const loadedSrc = iframe?.src;
3378
- if (loadedSrc) {
3600
+ if (iframeSrc && loadedSrc) {
3379
3601
  setLoadedIframeSrc(loadedSrc);
3380
3602
  }
3381
3603
  const sendZoomToBridge = () => {