@parhelia/core 0.1.12927 → 0.1.12931
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/agents-view/AgentCard.js +4 -1
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentProfileIcon.js +8 -8
- package/dist/agents-view/AgentProfileIcon.js.map +1 -1
- package/dist/agents-view/AgentsView.js +1 -1
- package/dist/client-components/index.d.ts +1 -1
- package/dist/client-components/index.js +1 -1
- package/dist/client-components/index.js.map +1 -1
- package/dist/components/WorkspaceHomeView.d.ts +13 -0
- package/dist/components/WorkspaceHomeView.js +13 -0
- package/dist/components/WorkspaceHomeView.js.map +1 -0
- package/dist/components/ui/styled-dialog-title.d.ts +3 -1
- package/dist/components/ui/styled-dialog-title.js +2 -2
- package/dist/components/ui/styled-dialog-title.js.map +1 -1
- package/dist/config/config.js +7 -19
- package/dist/config/config.js.map +1 -1
- package/dist/config/notificationRoutes.js +1 -1
- package/dist/config/notificationRoutes.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +1 -1
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/FieldListField.js +8 -0
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.js +14 -2
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/ImageEditor.js +1 -1
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -1
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/PerfProfiler.d.ts +11 -0
- package/dist/editor/PerfProfiler.js +29 -0
- package/dist/editor/PerfProfiler.js.map +1 -0
- package/dist/editor/PictureEditor.js +1 -1
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/ai/Agents.js +15 -79
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +4 -9
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.js +2 -2
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/dialogs/capturePageScreenshot.js +1 -1
- package/dist/editor/ai/dialogs/capturePageScreenshot.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentMemoryPanel.d.ts +12 -0
- package/dist/editor/ai/terminal/components/AgentMemoryPanel.js +131 -0
- package/dist/editor/ai/terminal/components/AgentMemoryPanel.js.map +1 -0
- package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js +10 -2
- package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentSettingsContent.d.ts +2 -1
- package/dist/editor/ai/terminal/components/AgentSettingsContent.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentSettingsSections.d.ts +2 -1
- package/dist/editor/ai/terminal/components/AgentSettingsSections.js +9 -2
- package/dist/editor/ai/terminal/components/AgentSettingsSections.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalContextPanels.js +11 -0
- package/dist/editor/ai/terminal/components/AgentTerminalContextPanels.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalStatusBar.js +27 -20
- package/dist/editor/ai/terminal/components/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/terminal/components/ContextInfoBar.js +4 -3
- package/dist/editor/ai/terminal/components/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentSettingsData.d.ts +8 -1
- package/dist/editor/ai/terminal/useAgentSettingsData.js +44 -5
- package/dist/editor/ai/terminal/useAgentSettingsData.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentSettingsPanel.d.ts +1 -1
- package/dist/editor/bridge/BridgeClient.js +9 -1
- package/dist/editor/bridge/BridgeClient.js.map +1 -1
- package/dist/editor/bridge/protocol.d.ts +19 -0
- package/dist/editor/bridge/protocol.js +1 -1
- package/dist/editor/bridge/protocol.js.map +1 -1
- package/dist/editor/client/EditorShell.js +163 -67
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +35 -2
- package/dist/editor/client/editContext.js +8 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.js +64 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +95 -3
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +166 -13
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.js +6 -5
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +2 -1
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/keyboardCommands.js +1 -1
- package/dist/editor/commands/keyboardCommands.js.map +1 -1
- package/dist/editor/componentTreeHelper.js +15 -5
- package/dist/editor/componentTreeHelper.js.map +1 -1
- package/dist/editor/context-menu/InsertMenu.js +1 -1
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +79 -9
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/fieldValueDiagnostics.d.ts +79 -0
- package/dist/editor/fieldValueDiagnostics.js +133 -0
- package/dist/editor/fieldValueDiagnostics.js.map +1 -0
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -2
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +6 -0
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +5 -5
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/DragInsertionOverlay.d.ts +8 -1
- package/dist/editor/page-editor-chrome/DragInsertionOverlay.js +203 -51
- package/dist/editor/page-editor-chrome/DragInsertionOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/EmptyImageFieldIndicators.d.ts +10 -0
- package/dist/editor/page-editor-chrome/EmptyImageFieldIndicators.js +236 -0
- package/dist/editor/page-editor-chrome/EmptyImageFieldIndicators.js.map +1 -0
- package/dist/editor/page-editor-chrome/FrameMenu.d.ts +2 -0
- package/dist/editor/page-editor-chrome/FrameMenu.js +8 -6
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenus.d.ts +3 -1
- package/dist/editor/page-editor-chrome/FrameMenus.js +4 -4
- package/dist/editor/page-editor-chrome/FrameMenus.js.map +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +18 -4
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.d.ts +3 -0
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +5 -8
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +8 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +20 -5
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.d.ts +3 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +2 -2
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/overlay/placeholderZones.d.ts +10 -0
- package/dist/editor/page-editor-chrome/overlay/placeholderZones.js +61 -1
- package/dist/editor/page-editor-chrome/overlay/placeholderZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +121 -48
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +94 -7
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.js +74 -9
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +9 -7
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +390 -54
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/bridgeFieldPatch.js +49 -2
- package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -1
- package/dist/editor/page-viewer/minimapVisibility.d.ts +33 -0
- package/dist/editor/page-viewer/minimapVisibility.js +72 -0
- package/dist/editor/page-viewer/minimapVisibility.js.map +1 -0
- package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
- package/dist/editor/page-viewer/pageViewContext.js +7 -0
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/page-viewer/renderUrl.js +29 -7
- package/dist/editor/page-viewer/renderUrl.js.map +1 -1
- package/dist/editor/perfTrace.d.ts +46 -0
- package/dist/editor/perfTrace.js +213 -0
- package/dist/editor/perfTrace.js.map +1 -0
- package/dist/editor/reviews/CommentPopover.js +2 -3
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/CreateReviewConfirmStep.js +5 -4
- package/dist/editor/reviews/CreateReviewConfirmStep.js.map +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +120 -202
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/reviews/CreateReviewItemsStep.d.ts +18 -0
- package/dist/editor/reviews/CreateReviewItemsStep.js +6 -0
- package/dist/editor/reviews/CreateReviewItemsStep.js.map +1 -0
- package/dist/editor/reviews/CreateReviewLanguagesStep.d.ts +9 -0
- package/dist/editor/reviews/CreateReviewLanguagesStep.js +36 -0
- package/dist/editor/reviews/CreateReviewLanguagesStep.js.map +1 -0
- package/dist/editor/reviews/CreateReviewReviewersStep.d.ts +17 -0
- package/dist/editor/reviews/CreateReviewReviewersStep.js +33 -0
- package/dist/editor/reviews/CreateReviewReviewersStep.js.map +1 -0
- package/dist/editor/reviews/CreateReviewSettingsStep.d.ts +21 -0
- package/dist/editor/reviews/CreateReviewSettingsStep.js +33 -0
- package/dist/editor/reviews/CreateReviewSettingsStep.js.map +1 -0
- package/dist/editor/reviews/MultiReviewManager.js +1 -4
- package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +1 -1
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/reviews/openCommentsFeedback.d.ts +2 -6
- package/dist/editor/reviews/openCommentsFeedback.js +3 -11
- package/dist/editor/reviews/openCommentsFeedback.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +38 -0
- package/dist/editor/services/agentService.js +42 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +4 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +1 -1
- package/dist/editor/services/editService.js +2 -2
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +6 -0
- package/dist/editor/services/reviewsService.js +15 -0
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/settings/panels/AgentProfileConfigPanel.js +2 -1
- package/dist/editor/settings/panels/AgentProfileConfigPanel.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +31 -10
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/ContentTreePanel.d.ts +2 -2
- package/dist/editor/sidebar/ContentTreePanel.js +6 -2
- package/dist/editor/sidebar/ContentTreePanel.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +3 -2
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.js +24 -6
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/ui/ItemCollectionEditor.js +4 -4
- package/dist/editor/ui/ItemCollectionEditor.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +5 -5
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/PublishItemDialog.js +1 -1
- package/dist/editor/ui/PublishItemDialog.js.map +1 -1
- package/dist/editor/ui/PublishRestrictionsDialog.js +179 -148
- package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.js +9 -2
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/editor/views/SingleEditView.js +32 -2
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/components/TaskBoardHomeView.js +3 -6
- package/dist/task-board/components/TaskBoardHomeView.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { BRIDGE_DOM_UPDATED_EVENT, MiniMap } from "./MiniMap";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveMiniMapVisibility, } from "./minimapVisibility";
|
|
5
|
+
import { useCanvasSelection, useEditContext, useEditContextRef, useFieldsEditContext, useFieldsEditContextRef, } from "../client/editContext";
|
|
5
6
|
import { useSlotContext } from "../views/editorSlotContext";
|
|
6
7
|
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
|
|
7
8
|
import { PageEditorChrome } from "../page-editor-chrome/PageEditorChrome";
|
|
8
9
|
import { useBridgeInlineEditing, } from "../page-editor-chrome/useBridgeInlineEditing";
|
|
9
10
|
import { getInlineAiAnchorFromBridgeToolbar, getVisibleBridgeToolbarRect, waitForVisibleBridgeToolbarRect, } from "../page-editor-chrome/bridgeInlineFormatToolbarLayout";
|
|
10
11
|
import { IFRAME_OVERLAY_BRIDGE_DRAG_GESTURE_EVENT, IFRAME_OVERLAY_BRIDGE_GEOMETRY_EVENT, IFRAME_OVERLAY_BRIDGE_SCROLL_EVENT, IframeOverlayProvider, } from "../page-editor-chrome/overlay/IframeOverlayProvider";
|
|
11
|
-
import { DEVICE_CHANGE_EVENT, useViewportChangeSignal, } from "./pageViewContext";
|
|
12
|
+
import { CANVAS_REFRESH_COMPLETED_EVENT, DEVICE_CHANGE_EVENT, useViewportChangeSignal, } from "./pageViewContext";
|
|
12
13
|
import uuid from "react-uuid";
|
|
13
14
|
import { cn } from "../../lib/utils";
|
|
14
15
|
import { normalizeMarkerId } from "../utils";
|
|
@@ -25,9 +26,12 @@ import { Spinner } from "../ui/Spinner";
|
|
|
25
26
|
import { DeviceToolbar } from "./DeviceToolbar";
|
|
26
27
|
import { FullscreenControls } from "../client/ui/FullscreenControls";
|
|
27
28
|
import { toSitecoreDate } from "../utils/sitecoreDate";
|
|
29
|
+
import { pickVersionAtDate } from "../utils/versionAtDate";
|
|
28
30
|
import { BridgeClient } from "../bridge/BridgeClient";
|
|
31
|
+
import { beginPerfTrace, isPerfTraceActive, isPerfTraceEnabled, perfTraceStep, } from "../perfTrace";
|
|
29
32
|
import { getSuggestionDisplayValue } from "../reviews/suggestionDisplayValue";
|
|
30
33
|
import { buildBridgeFieldPatchPayload, getBridgeFieldPatchValue, isBridgePatchableTextField, } from "./bridgeFieldPatch";
|
|
34
|
+
import { logFieldValueDiagnostic, summarizeField, summarizeFieldDescriptor, summarizeFieldValue, } from "../fieldValueDiagnostics";
|
|
31
35
|
import { createKeyboardEventFromBridgeInteraction } from "./bridgeKeyboardShortcuts";
|
|
32
36
|
import { formatParentBridgeInjectionDiagnostic, injectParheliaBridgeFromParent, } from "./parentBridgeInjection";
|
|
33
37
|
import { getTextCompletion, normalizeTextCompletion, } from "../services/textCompletionService";
|
|
@@ -39,6 +43,9 @@ const BRIDGE_INLINE_EDIT_RELEASE_EVENT = "parhelia:bridge-inline-edit-release";
|
|
|
39
43
|
const INLINE_AI_CLOSE_EVENT = "inline-ai-close";
|
|
40
44
|
const BRIDGE_INLINE_LINK_OPEN_EVENT = "bridge-inline-link-open";
|
|
41
45
|
const SCROLL_INTO_VIEW_BRIDGE_SOURCE = "scroll-into-view";
|
|
46
|
+
// How long a requested canvas refresh may suppress viewport reactions
|
|
47
|
+
// (selection auto-scroll, scrollIntoView) before it is presumed lost.
|
|
48
|
+
const CANVAS_REFRESH_IN_FLIGHT_MAX_MS = 15000;
|
|
42
49
|
// Suggestions-mode cursor: a text I-beam (the click insertion point) badged with
|
|
43
50
|
// the lucide PenLine icon from the "Suggestions mode" toolbar button. A white
|
|
44
51
|
// halo keeps it visible on any page background; the hotspot is the I-beam centre.
|
|
@@ -602,6 +609,7 @@ export function PageViewerFrame(props) {
|
|
|
602
609
|
function PageViewerFrameContent({ compareView, pageViewContext, editContext, className, slotCloseButton, }) {
|
|
603
610
|
const fieldsContext = useFieldsEditContext();
|
|
604
611
|
const slotContext = useSlotContext();
|
|
612
|
+
const canvasSelection = useCanvasSelection();
|
|
605
613
|
const pathname = usePathname();
|
|
606
614
|
const pageViewContextRef = useRef(undefined);
|
|
607
615
|
useEffect(() => {
|
|
@@ -636,6 +644,42 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
636
644
|
const directOverlayScrollSyncRef = useRef(false);
|
|
637
645
|
const lastBridgeHoverInteractionRef = useRef(undefined);
|
|
638
646
|
const pendingRefreshScrollRef = useRef(undefined);
|
|
647
|
+
// When the pending refresh scroll was captured, and when the last explicit
|
|
648
|
+
// scrollBy command (minimap click/wheel) was issued. A capture is only safe
|
|
649
|
+
// to restore while no explicit command is newer than it: the capture
|
|
650
|
+
// describes where the canvas happened to be at refresh-request time, and
|
|
651
|
+
// blindly re-asserting it after the user commanded a scroll yanks the
|
|
652
|
+
// canvas away from where they sent it.
|
|
653
|
+
const pendingRefreshScrollCapturedAtRef = useRef(0);
|
|
654
|
+
const lastExplicitScrollCommandAtRef = useRef(0);
|
|
655
|
+
// Signature of the last canvas render actually requested (URL sans the
|
|
656
|
+
// per-request edit_rev nonce, plus the edit revision). When a re-run of the
|
|
657
|
+
// render effect produces the same signature — e.g. an edit<->suggestions
|
|
658
|
+
// mode toggle, which changes no host-rendered output — the refresh is
|
|
659
|
+
// skipped entirely: the bridge learns the mode via setEditorMode, and a
|
|
660
|
+
// needless refresh would drop scroll position and inline-edit state.
|
|
661
|
+
const lastRenderSignatureRef = useRef(undefined);
|
|
662
|
+
// Set when a canvas refresh (bridge patch or full reload) has been requested
|
|
663
|
+
// and its result has not landed yet. While in flight, bridgeGeometry still
|
|
664
|
+
// describes the PRE-refresh DOM — a moved component is still measured at its
|
|
665
|
+
// old position — so viewport-affecting reactions (selection auto-scroll,
|
|
666
|
+
// scrollIntoView) must wait for the refreshed geometry instead of scrolling
|
|
667
|
+
// toward stale rects. Timestamped so a refresh that never completes cannot
|
|
668
|
+
// suppress scrolling forever.
|
|
669
|
+
const canvasRefreshInFlightSinceRef = useRef(null);
|
|
670
|
+
// Set when refreshCompleted arrived with applied:"host-app": the page's own
|
|
671
|
+
// app accepted the refresh but its re-render lands later as DOM mutations.
|
|
672
|
+
// The in-flight window then stays open until the next structureUpdated.
|
|
673
|
+
const awaitingHostAppRefreshRenderRef = useRef(false);
|
|
674
|
+
const isCanvasRefreshInFlight = useCallback(() => {
|
|
675
|
+
const since = canvasRefreshInFlightSinceRef.current;
|
|
676
|
+
return (since !== null && Date.now() - since < CANVAS_REFRESH_IN_FLIGHT_MAX_MS);
|
|
677
|
+
}, []);
|
|
678
|
+
const completeCanvasRefresh = useCallback(() => {
|
|
679
|
+
awaitingHostAppRefreshRenderRef.current = false;
|
|
680
|
+
canvasRefreshInFlightSinceRef.current = null;
|
|
681
|
+
window.dispatchEvent(new CustomEvent(CANVAS_REFRESH_COMPLETED_EVENT));
|
|
682
|
+
}, []);
|
|
639
683
|
const restorePendingRefreshScroll = useCallback((iframe, targetScroll, observedScroll) => {
|
|
640
684
|
if (!targetScroll)
|
|
641
685
|
return;
|
|
@@ -644,6 +688,15 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
644
688
|
pendingRefreshScrollRef.current = undefined;
|
|
645
689
|
}
|
|
646
690
|
};
|
|
691
|
+
if (lastExplicitScrollCommandAtRef.current >
|
|
692
|
+
pendingRefreshScrollCapturedAtRef.current) {
|
|
693
|
+
// The user commanded a scroll after this position was captured; the
|
|
694
|
+
// bridge is delivering that newer target. Restoring the stale capture
|
|
695
|
+
// would fight it (restoreIframeWindowScroll re-asserts for ~150
|
|
696
|
+
// frames and cancels in-flight smooth scrolls).
|
|
697
|
+
clearPendingTarget();
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
647
700
|
const currentScroll = getIframeWindowScroll(iframe) ??
|
|
648
701
|
observedScroll ?? { x: 0, y: 0 };
|
|
649
702
|
const deltaX = targetScroll.x - currentScroll.x;
|
|
@@ -689,6 +742,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
689
742
|
const [loadedIframeSrc, setLoadedIframeSrc] = useState();
|
|
690
743
|
const [scroll, setScroll] = useState(0);
|
|
691
744
|
const [showMiniMap, setShowMiniMap] = useState(false);
|
|
745
|
+
const miniMapVisibilityMemoryRef = useRef({});
|
|
692
746
|
const [bridgeGeometryRevision, setBridgeGeometryRevision] = useState(0);
|
|
693
747
|
const [bridgeDomRevision, setBridgeDomRevision] = useState(0);
|
|
694
748
|
const bridgeGeometryRevisionRafRef = useRef(null);
|
|
@@ -775,16 +829,34 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
775
829
|
// Iframe blur is handled by the bridge inline-edit lifecycle now.
|
|
776
830
|
}, []);
|
|
777
831
|
const sendBeginInlineEdit = useCallback((payload) => {
|
|
778
|
-
|
|
779
|
-
false
|
|
832
|
+
const sent = bridgeClientRef.current?.sendCommand("beginInlineEdit", payload) ??
|
|
833
|
+
false;
|
|
834
|
+
logFieldValueDiagnostic("bridge-frame", "beginInlineEdit command", {
|
|
835
|
+
sent,
|
|
836
|
+
payload,
|
|
837
|
+
});
|
|
838
|
+
return sent;
|
|
780
839
|
}, []);
|
|
781
840
|
const sendCancelInlineEdit = useCallback((payload) => {
|
|
782
|
-
|
|
783
|
-
false
|
|
841
|
+
const sent = bridgeClientRef.current?.sendCommand("cancelInlineEdit", payload) ??
|
|
842
|
+
false;
|
|
843
|
+
logFieldValueDiagnostic("bridge-frame", "cancelInlineEdit command", {
|
|
844
|
+
sent,
|
|
845
|
+
payload,
|
|
846
|
+
});
|
|
847
|
+
return sent;
|
|
784
848
|
}, []);
|
|
785
849
|
const sendApplyFieldPatchCommand = useCallback((payload) => {
|
|
786
|
-
|
|
787
|
-
false
|
|
850
|
+
const sent = bridgeClientRef.current?.sendCommand("applyFieldPatch", payload) ??
|
|
851
|
+
false;
|
|
852
|
+
logFieldValueDiagnostic("bridge-frame", "applyFieldPatch command", {
|
|
853
|
+
sent,
|
|
854
|
+
payload: {
|
|
855
|
+
...payload,
|
|
856
|
+
value: summarizeFieldValue(payload.value),
|
|
857
|
+
},
|
|
858
|
+
});
|
|
859
|
+
return sent;
|
|
788
860
|
}, []);
|
|
789
861
|
const sendApplyRichTextCommand = useCallback((payload) => {
|
|
790
862
|
return (bridgeClientRef.current?.sendCommand("applyRichTextCommand", payload) ??
|
|
@@ -833,6 +905,18 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
833
905
|
sendApplyFieldPatch: sendApplyFieldPatchCommand,
|
|
834
906
|
});
|
|
835
907
|
const beginTrackedBridgeInlineEdit = useCallback(async (interaction) => {
|
|
908
|
+
logFieldValueDiagnostic("bridge-frame", "begin tracked inline edit", {
|
|
909
|
+
previousActiveInlineEdit: activeBridgeInlineEditRef.current,
|
|
910
|
+
interaction: {
|
|
911
|
+
kind: interaction.kind,
|
|
912
|
+
elementKey: interaction.elementKey,
|
|
913
|
+
fieldId: interaction.fieldId,
|
|
914
|
+
item: interaction.item,
|
|
915
|
+
selectedText: summarizeFieldValue(interaction.selectedText),
|
|
916
|
+
selectionStartOffset: interaction.selectionStartOffset,
|
|
917
|
+
selectionEndOffset: interaction.selectionEndOffset,
|
|
918
|
+
},
|
|
919
|
+
});
|
|
836
920
|
clearActiveBridgeInlineEdit();
|
|
837
921
|
const started = await beginBridgeInlineEdit(interaction);
|
|
838
922
|
if (started && interaction.elementKey && interaction.fieldId) {
|
|
@@ -857,6 +941,10 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
857
941
|
};
|
|
858
942
|
setIsBridgeInlineEditing(true);
|
|
859
943
|
}
|
|
944
|
+
logFieldValueDiagnostic("bridge-frame", "tracked inline edit result", {
|
|
945
|
+
started,
|
|
946
|
+
nextActiveInlineEdit: activeBridgeInlineEditRef.current,
|
|
947
|
+
});
|
|
860
948
|
return started;
|
|
861
949
|
}, [
|
|
862
950
|
beginBridgeInlineEdit,
|
|
@@ -865,9 +953,17 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
865
953
|
editContextRef,
|
|
866
954
|
]);
|
|
867
955
|
const endTrackedBridgeFieldFocus = useCallback(() => {
|
|
956
|
+
logFieldValueDiagnostic("bridge-frame", "ending tracked field focus", {
|
|
957
|
+
activeInlineEdit: activeBridgeInlineEditRef.current,
|
|
958
|
+
});
|
|
868
959
|
clearActiveBridgeInlineEdit();
|
|
869
960
|
endBridgeFieldFocus();
|
|
870
961
|
}, [clearActiveBridgeInlineEdit, endBridgeFieldFocus]);
|
|
962
|
+
const beginTrackedBridgeInlineEditAfterSelection = useCallback((interaction) => {
|
|
963
|
+
window.setTimeout(() => {
|
|
964
|
+
void beginTrackedBridgeInlineEdit(interaction);
|
|
965
|
+
}, 0);
|
|
966
|
+
}, [beginTrackedBridgeInlineEdit]);
|
|
871
967
|
useEffect(() => {
|
|
872
968
|
const handleBridgeInlineEditRelease = (event) => {
|
|
873
969
|
const detail = event
|
|
@@ -881,6 +977,10 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
881
977
|
if (activeEdit.item && !bridgeItemMatches(activeEdit.item, field.item)) {
|
|
882
978
|
return;
|
|
883
979
|
}
|
|
980
|
+
logFieldValueDiagnostic("bridge-frame", "inline edit released by form", {
|
|
981
|
+
activeInlineEdit: activeEdit,
|
|
982
|
+
field: summarizeFieldDescriptor(field),
|
|
983
|
+
});
|
|
884
984
|
clearActiveBridgeInlineEdit();
|
|
885
985
|
bridgePatchSignatureRef.current.delete(`${activeEdit.elementKey}:${activeEdit.fieldId}`);
|
|
886
986
|
};
|
|
@@ -932,12 +1032,33 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
932
1032
|
},
|
|
933
1033
|
mode: showSuggestions ? "suggestions" : "baseline",
|
|
934
1034
|
});
|
|
935
|
-
|
|
1035
|
+
const result = {
|
|
936
1036
|
value: showSuggestions ? display.mergedValue : display.baselineValue,
|
|
937
1037
|
source: showSuggestions && display.hasSuggestions
|
|
938
1038
|
? "suggestion-preview"
|
|
939
1039
|
: "real",
|
|
940
1040
|
};
|
|
1041
|
+
logFieldValueDiagnostic("bridge-patch", "display value resolved", {
|
|
1042
|
+
showSuggestions,
|
|
1043
|
+
preferRepositoryValue,
|
|
1044
|
+
field: summarizeField(field),
|
|
1045
|
+
structureField: {
|
|
1046
|
+
elementKey: structureField.elementKey,
|
|
1047
|
+
fieldId: structureField.fieldId,
|
|
1048
|
+
item: structureField.item,
|
|
1049
|
+
textContent: summarizeFieldValue(structureField.textContent),
|
|
1050
|
+
isRichText: structureField.isRichText,
|
|
1051
|
+
},
|
|
1052
|
+
itemDescriptor,
|
|
1053
|
+
repositoryValue: summarizeFieldValue(repositoryValue),
|
|
1054
|
+
result: {
|
|
1055
|
+
...result,
|
|
1056
|
+
value: summarizeFieldValue(result.value),
|
|
1057
|
+
},
|
|
1058
|
+
modifiedFieldCount: fieldsContext?.modifiedFields?.length,
|
|
1059
|
+
suggestedEditCount: editContext.suggestedEdits?.length,
|
|
1060
|
+
});
|
|
1061
|
+
return result;
|
|
941
1062
|
}, [
|
|
942
1063
|
editContext.mode,
|
|
943
1064
|
editContext.showSuggestedEdits,
|
|
@@ -972,10 +1093,28 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
972
1093
|
source: patch.source ?? "",
|
|
973
1094
|
observedTextContent: observedTextContent ?? "",
|
|
974
1095
|
});
|
|
975
|
-
|
|
1096
|
+
const previousSignature = bridgePatchSignatureRef.current.get(cacheKey);
|
|
1097
|
+
if (previousSignature === signature) {
|
|
1098
|
+
logFieldValueDiagnostic("bridge-patch", "skipped duplicate send", {
|
|
1099
|
+
cacheKey,
|
|
1100
|
+
patch: {
|
|
1101
|
+
...patch,
|
|
1102
|
+
value: summarizeFieldValue(patch.value),
|
|
1103
|
+
},
|
|
1104
|
+
observedTextContent: summarizeFieldValue(observedTextContent),
|
|
1105
|
+
});
|
|
976
1106
|
return false;
|
|
977
1107
|
}
|
|
978
1108
|
bridgePatchSignatureRef.current.set(cacheKey, signature);
|
|
1109
|
+
logFieldValueDiagnostic("bridge-patch", "sending patch", {
|
|
1110
|
+
cacheKey,
|
|
1111
|
+
patch: {
|
|
1112
|
+
...patch,
|
|
1113
|
+
value: summarizeFieldValue(patch.value),
|
|
1114
|
+
},
|
|
1115
|
+
observedTextContent: summarizeFieldValue(observedTextContent),
|
|
1116
|
+
previousSignature,
|
|
1117
|
+
});
|
|
979
1118
|
bridge.sendCommand("applyFieldPatch", patch);
|
|
980
1119
|
return true;
|
|
981
1120
|
}, []);
|
|
@@ -1188,16 +1327,22 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1188
1327
|
const requestBridgeTextCompletion = useCallback(async (interaction, force = false) => {
|
|
1189
1328
|
const editor = editContextRef.current;
|
|
1190
1329
|
if (!editor?.enableCompletions || editor.mode === "preview") {
|
|
1330
|
+
console.debug("[Parhelia] text completion skipped", JSON.stringify({
|
|
1331
|
+
enableCompletions: editor?.enableCompletions,
|
|
1332
|
+
mode: editor?.mode,
|
|
1333
|
+
}));
|
|
1191
1334
|
clearBridgeTextCompletion();
|
|
1192
1335
|
return;
|
|
1193
1336
|
}
|
|
1194
1337
|
const fieldDescriptor = await resolveBridgeFieldDescriptor(interaction);
|
|
1195
1338
|
if (!fieldDescriptor) {
|
|
1339
|
+
console.debug("[Parhelia] text completion skipped: no field descriptor", JSON.stringify({ elementKey: interaction.elementKey }));
|
|
1196
1340
|
clearBridgeTextCompletion();
|
|
1197
1341
|
return;
|
|
1198
1342
|
}
|
|
1199
1343
|
const field = await editor.itemsRepository.getField(fieldDescriptor);
|
|
1200
1344
|
if (!field) {
|
|
1345
|
+
console.debug("[Parhelia] text completion skipped: field not in repository", JSON.stringify(fieldDescriptor));
|
|
1201
1346
|
clearBridgeTextCompletion();
|
|
1202
1347
|
return;
|
|
1203
1348
|
}
|
|
@@ -1212,24 +1357,15 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1212
1357
|
if (!elementKey ||
|
|
1213
1358
|
!hostFieldId ||
|
|
1214
1359
|
!isBridgePatchableTextField(field, isRichText)) {
|
|
1360
|
+
console.debug("[Parhelia] text completion skipped: not patchable", JSON.stringify({
|
|
1361
|
+
elementKey,
|
|
1362
|
+
hostFieldId,
|
|
1363
|
+
fieldType: field.type,
|
|
1364
|
+
isRichText,
|
|
1365
|
+
}));
|
|
1215
1366
|
clearBridgeTextCompletion();
|
|
1216
1367
|
return;
|
|
1217
1368
|
}
|
|
1218
|
-
const selectedText = selectionMatches && typeof bridgeSelection?.text === "string"
|
|
1219
|
-
? bridgeSelection.text
|
|
1220
|
-
: typeof interaction.selectedText === "string"
|
|
1221
|
-
? interaction.selectedText
|
|
1222
|
-
: "";
|
|
1223
|
-
const selectionStartOffset = selectionMatches && typeof bridgeSelection?.startOffset === "number"
|
|
1224
|
-
? bridgeSelection.startOffset
|
|
1225
|
-
: typeof interaction.selectionStartOffset === "number"
|
|
1226
|
-
? interaction.selectionStartOffset
|
|
1227
|
-
: undefined;
|
|
1228
|
-
const selectionEndOffset = selectionMatches && typeof bridgeSelection?.endOffset === "number"
|
|
1229
|
-
? bridgeSelection.endOffset
|
|
1230
|
-
: typeof interaction.selectionEndOffset === "number"
|
|
1231
|
-
? interaction.selectionEndOffset
|
|
1232
|
-
: undefined;
|
|
1233
1369
|
const liveSelectionStartOffset = typeof interaction.selectionStartOffset === "number"
|
|
1234
1370
|
? interaction.selectionStartOffset
|
|
1235
1371
|
: undefined;
|
|
@@ -1242,6 +1378,19 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1242
1378
|
const bridgeSelectionEndOffset = selectionMatches && typeof bridgeSelection?.endOffset === "number"
|
|
1243
1379
|
? bridgeSelection?.endOffset
|
|
1244
1380
|
: undefined;
|
|
1381
|
+
const selectedText = typeof interaction.selectedText === "string"
|
|
1382
|
+
? interaction.selectedText
|
|
1383
|
+
: selectionMatches && typeof bridgeSelection?.text === "string"
|
|
1384
|
+
? bridgeSelection.text
|
|
1385
|
+
: "";
|
|
1386
|
+
const selectionStartOffset = liveSelectionStartOffset ??
|
|
1387
|
+
(selectionMatches && typeof bridgeSelection?.startOffset === "number"
|
|
1388
|
+
? bridgeSelection.startOffset
|
|
1389
|
+
: undefined);
|
|
1390
|
+
const selectionEndOffset = liveSelectionEndOffset ??
|
|
1391
|
+
(selectionMatches && typeof bridgeSelection?.endOffset === "number"
|
|
1392
|
+
? bridgeSelection.endOffset
|
|
1393
|
+
: undefined);
|
|
1245
1394
|
const activeInlineEdit = activeBridgeInlineEditRef.current;
|
|
1246
1395
|
const activeInlineEditMatches = !!(activeInlineEdit &&
|
|
1247
1396
|
activeInlineEdit.elementKey === elementKey &&
|
|
@@ -1267,7 +1416,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1267
1416
|
!hasLiveInteractionOffsets ||
|
|
1268
1417
|
(bridgeSelectionStartOffset === liveSelectionStartOffset &&
|
|
1269
1418
|
bridgeSelectionEndOffset === liveSelectionEndOffset);
|
|
1270
|
-
const metadataMatchesLiveOffsets =
|
|
1419
|
+
const metadataMatchesLiveOffsets = selectionSnapshotMatchesLiveOffsets ||
|
|
1271
1420
|
((contextBefore?.length ?? 0) === interaction.selectionStartOffset &&
|
|
1272
1421
|
(contextBefore?.length ?? 0) + selectedText.length ===
|
|
1273
1422
|
interaction.selectionEndOffset);
|
|
@@ -1378,6 +1527,12 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1378
1527
|
return;
|
|
1379
1528
|
}
|
|
1380
1529
|
}
|
|
1530
|
+
// Escape leaves insert mode, same as in the editor window itself
|
|
1531
|
+
if (interaction.key === "Escape" &&
|
|
1532
|
+
editContextRef.current?.insertMode) {
|
|
1533
|
+
editContextRef.current.setInsertMode(false);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1381
1536
|
if (isBridgeTextCompletionApplyShortcut(interaction)) {
|
|
1382
1537
|
if (bridgeTextCompletionRef.current) {
|
|
1383
1538
|
applyBridgeTextCompletion();
|
|
@@ -1517,9 +1672,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1517
1672
|
if (!editor)
|
|
1518
1673
|
return;
|
|
1519
1674
|
const selectFromBridgeInteraction = (ids) => {
|
|
1520
|
-
|
|
1675
|
+
const unchanged = bridgeComponentSelectionsMatch(editor.getSelection(), ids);
|
|
1676
|
+
if (!unchanged) {
|
|
1521
1677
|
suppressNextSelectionScrollRef.current = true;
|
|
1522
1678
|
}
|
|
1679
|
+
perfTraceStep("frame:select-dispatched", { ids, unchanged });
|
|
1523
1680
|
editor.select(ids);
|
|
1524
1681
|
};
|
|
1525
1682
|
let effectiveInteraction = interaction;
|
|
@@ -1581,7 +1738,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1581
1738
|
selectFromBridgeInteraction([]);
|
|
1582
1739
|
if (isEditableFieldClick) {
|
|
1583
1740
|
clearSelectedRangeForEditableClick();
|
|
1584
|
-
|
|
1741
|
+
beginTrackedBridgeInlineEditAfterSelection(effectiveInteraction);
|
|
1585
1742
|
}
|
|
1586
1743
|
else if (interaction.kind === "click") {
|
|
1587
1744
|
closeInlineAiIfOpen();
|
|
@@ -1589,7 +1746,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1589
1746
|
}
|
|
1590
1747
|
return;
|
|
1591
1748
|
}
|
|
1592
|
-
const currentSelection = editor.
|
|
1749
|
+
const currentSelection = editor.getSelection();
|
|
1593
1750
|
if (interaction.kind === "contextMenu") {
|
|
1594
1751
|
if (pageViewContextRef.current) {
|
|
1595
1752
|
pageViewContextRef.current.bridgeInteraction = effectiveInteraction;
|
|
@@ -1719,7 +1876,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1719
1876
|
selectFromBridgeInteraction([componentId]);
|
|
1720
1877
|
if (isEditableFieldClick) {
|
|
1721
1878
|
clearSelectedRangeForEditableClick();
|
|
1722
|
-
|
|
1879
|
+
beginTrackedBridgeInlineEditAfterSelection(effectiveInteraction);
|
|
1723
1880
|
}
|
|
1724
1881
|
else {
|
|
1725
1882
|
closeInlineAiIfOpen();
|
|
@@ -1728,6 +1885,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1728
1885
|
}, [
|
|
1729
1886
|
applyBridgeTextCompletion,
|
|
1730
1887
|
beginTrackedBridgeInlineEdit,
|
|
1888
|
+
beginTrackedBridgeInlineEditAfterSelection,
|
|
1731
1889
|
clearBridgeTextCompletion,
|
|
1732
1890
|
endTrackedBridgeFieldFocus,
|
|
1733
1891
|
requestBridgeTextCompletion,
|
|
@@ -1849,9 +2007,12 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1849
2007
|
suggestionsCursorActive: !activeBridgeInlineEditRef.current &&
|
|
1850
2008
|
(editContextRef.current?.mode === "suggestions" ||
|
|
1851
2009
|
!!editContextRef.current?.showSuggestedEdits),
|
|
1852
|
-
selectedComponentIds: editContextRef.current?.
|
|
2010
|
+
selectedComponentIds: editContextRef.current?.getSelection() ?? [],
|
|
1853
2011
|
featureFlags: {
|
|
1854
2012
|
crossOriginBridge: true,
|
|
2013
|
+
// Lets the host emit its own perf logs + interaction perf
|
|
2014
|
+
// timestamps without needing a flag in the iframe's origin.
|
|
2015
|
+
perfTrace: isPerfTraceEnabled(),
|
|
1855
2016
|
},
|
|
1856
2017
|
version: bridge.getDiagnostics().editorVersion,
|
|
1857
2018
|
acknowledgedCapabilities: bridge.getAcknowledgedCapabilities(),
|
|
@@ -1864,12 +2025,31 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1864
2025
|
}
|
|
1865
2026
|
break;
|
|
1866
2027
|
case "structureUpdated":
|
|
2028
|
+
if (isPerfTraceActive()) {
|
|
2029
|
+
perfTraceStep("frame:structure-updated-received", {
|
|
2030
|
+
components: event.payload.structure.components.length,
|
|
2031
|
+
fields: event.payload.structure.fields.length,
|
|
2032
|
+
transportMs: Date.now() - event.timestamp,
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
1867
2035
|
pageViewContext.setBridgeStructure(event.payload.structure);
|
|
2036
|
+
if (awaitingHostAppRefreshRenderRef.current) {
|
|
2037
|
+
// First structure change after a host-app refresh: the page's
|
|
2038
|
+
// own re-render has reached the DOM — the refresh is now truly
|
|
2039
|
+
// complete.
|
|
2040
|
+
completeCanvasRefresh();
|
|
2041
|
+
}
|
|
1868
2042
|
break;
|
|
1869
2043
|
case "pageSkeletonUpdated":
|
|
1870
2044
|
pageViewContext.setPageSkeleton(event.payload.pageSkeleton);
|
|
1871
2045
|
break;
|
|
1872
2046
|
case "geometryUpdated": {
|
|
2047
|
+
if (isPerfTraceActive()) {
|
|
2048
|
+
perfTraceStep("frame:geometry-updated-received", {
|
|
2049
|
+
targets: event.payload.geometry.targets.length,
|
|
2050
|
+
transportMs: Date.now() - event.timestamp,
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
1873
2053
|
latestBridgeScrollRef.current = event.payload.geometry.scroll;
|
|
1874
2054
|
pageViewContext.bridgeGeometry = event.payload.geometry;
|
|
1875
2055
|
pageViewContext.setBridgeGeometry(event.payload.geometry);
|
|
@@ -1893,11 +2073,22 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1893
2073
|
window.dispatchEvent(new CustomEvent(BRIDGE_DOM_UPDATED_EVENT));
|
|
1894
2074
|
break;
|
|
1895
2075
|
case "refreshStarted":
|
|
2076
|
+
logFieldValueDiagnostic("bridge-frame", "refresh started", {
|
|
2077
|
+
revision: event.payload.revision,
|
|
2078
|
+
activeInlineEdit: activeBridgeInlineEditRef.current,
|
|
2079
|
+
patchSignatureCount: bridgePatchSignatureRef.current.size,
|
|
2080
|
+
});
|
|
1896
2081
|
clearBridgeTextCompletion();
|
|
1897
2082
|
clearBridgePatchSignatures();
|
|
1898
2083
|
clearActiveBridgeInlineEdit();
|
|
1899
2084
|
break;
|
|
1900
2085
|
case "refreshCompleted":
|
|
2086
|
+
logFieldValueDiagnostic("bridge-frame", "refresh completed", {
|
|
2087
|
+
activeInlineEdit: activeBridgeInlineEditRef.current,
|
|
2088
|
+
hasStructure: !!event.payload.structure,
|
|
2089
|
+
hasGeometry: !!event.payload.geometry,
|
|
2090
|
+
structureFieldCount: event.payload.structure?.fields.length,
|
|
2091
|
+
});
|
|
1901
2092
|
// Runtime refresh replaces host DOM without a new bridge ready event.
|
|
1902
2093
|
// Field patches sent during the refresh window may have been cached
|
|
1903
2094
|
// against the previous DOM generation, so force the refreshed host to
|
|
@@ -1934,6 +2125,18 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1934
2125
|
};
|
|
1935
2126
|
restorePendingRefreshScroll(iframe, targetScroll, observedScroll);
|
|
1936
2127
|
}
|
|
2128
|
+
if (event.payload.applied === "host-app") {
|
|
2129
|
+
// The page's own app (e.g. a Next.js rendering host) accepted
|
|
2130
|
+
// the refresh, but its re-render lands later as ordinary DOM
|
|
2131
|
+
// mutations. Completing now would reopen viewport reactions
|
|
2132
|
+
// (selection auto-scroll, scrollIntoView, the inserting
|
|
2133
|
+
// indicator) while geometry still describes the pre-edit DOM —
|
|
2134
|
+
// wait for the mutation-driven structureUpdated instead.
|
|
2135
|
+
awaitingHostAppRefreshRenderRef.current = true;
|
|
2136
|
+
}
|
|
2137
|
+
else {
|
|
2138
|
+
completeCanvasRefresh();
|
|
2139
|
+
}
|
|
1937
2140
|
setShowSpinner(false);
|
|
1938
2141
|
break;
|
|
1939
2142
|
case "selectionChanged":
|
|
@@ -1944,11 +2147,32 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1944
2147
|
if (event.payload.elementKey && event.payload.fieldId) {
|
|
1945
2148
|
setIsBridgeInlineEditing(true);
|
|
1946
2149
|
}
|
|
2150
|
+
logFieldValueDiagnostic("bridge-frame", "fieldValueChanged event", {
|
|
2151
|
+
elementKey: event.payload.elementKey,
|
|
2152
|
+
fieldId: event.payload.fieldId,
|
|
2153
|
+
item: event.payload.item,
|
|
2154
|
+
isRichText: event.payload.isRichText,
|
|
2155
|
+
value: summarizeFieldValue(event.payload.value),
|
|
2156
|
+
activeInlineEdit: activeBridgeInlineEditRef.current,
|
|
2157
|
+
});
|
|
1947
2158
|
handleBridgeFieldValueChanged(event.payload);
|
|
1948
2159
|
break;
|
|
1949
2160
|
case "inlineEditEnded":
|
|
2161
|
+
logFieldValueDiagnostic("bridge-frame", "inlineEditEnded event", {
|
|
2162
|
+
reason: event.payload.reason,
|
|
2163
|
+
elementKey: event.payload.elementKey,
|
|
2164
|
+
fieldId: event.payload.fieldId,
|
|
2165
|
+
item: event.payload.item,
|
|
2166
|
+
isRichText: event.payload.isRichText,
|
|
2167
|
+
value: summarizeFieldValue(event.payload.value),
|
|
2168
|
+
activeInlineEdit: activeBridgeInlineEditRef.current,
|
|
2169
|
+
});
|
|
1950
2170
|
handleBridgeInlineEditEnded(event.payload);
|
|
1951
2171
|
clearActiveBridgeInlineEdit();
|
|
2172
|
+
logFieldValueDiagnostic("bridge-frame", "active inline edit cleared after end", {
|
|
2173
|
+
fieldId: event.payload.fieldId,
|
|
2174
|
+
reason: event.payload.reason,
|
|
2175
|
+
});
|
|
1952
2176
|
break;
|
|
1953
2177
|
case "interaction":
|
|
1954
2178
|
if (event.payload.kind === "hover") {
|
|
@@ -1960,6 +2184,24 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
1960
2184
|
else {
|
|
1961
2185
|
lastBridgeHoverInteractionRef.current = undefined;
|
|
1962
2186
|
}
|
|
2187
|
+
if (isPerfTraceEnabled() &&
|
|
2188
|
+
(event.payload.kind === "click" ||
|
|
2189
|
+
event.payload.kind === "doubleClick" ||
|
|
2190
|
+
event.payload.kind === "contextMenu")) {
|
|
2191
|
+
// Backdate to the host's DOM-event timestamp when available so
|
|
2192
|
+
// host-side dispatch (hit-test + deferred-click debounce) is
|
|
2193
|
+
// part of the waterfall; the envelope timestamp is when the
|
|
2194
|
+
// host posted the message.
|
|
2195
|
+
const eventAt = event.payload.perf?.eventAt;
|
|
2196
|
+
beginPerfTrace(`select-${event.payload.kind}`, {
|
|
2197
|
+
componentId: event.payload.componentId,
|
|
2198
|
+
fieldId: event.payload.fieldId,
|
|
2199
|
+
hostDispatchMs: eventAt !== undefined
|
|
2200
|
+
? event.timestamp - eventAt
|
|
2201
|
+
: "n/a (host without perf tracing)",
|
|
2202
|
+
transportMs: Date.now() - event.timestamp,
|
|
2203
|
+
}, eventAt ?? event.timestamp);
|
|
2204
|
+
}
|
|
1963
2205
|
pageViewContext.setBridgeInteraction(event.payload);
|
|
1964
2206
|
void handleBridgeInteraction(event.payload, iframe);
|
|
1965
2207
|
break;
|
|
@@ -2032,6 +2274,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2032
2274
|
scheduleBridgeGeometryRevision,
|
|
2033
2275
|
shouldTrackMinimapScroll,
|
|
2034
2276
|
restorePendingRefreshScroll,
|
|
2277
|
+
completeCanvasRefresh,
|
|
2035
2278
|
]);
|
|
2036
2279
|
useEffect(() => {
|
|
2037
2280
|
const repository = editContext.itemsRepository;
|
|
@@ -2245,7 +2488,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2245
2488
|
};
|
|
2246
2489
|
pageViewContext.requestBridgeGeometry = requestBridgeGeometry;
|
|
2247
2490
|
const requestBridgeScrollBy = (payload) => {
|
|
2248
|
-
|
|
2491
|
+
const accepted = bridgeClientRef.current?.sendCommand("scrollBy", payload) ?? false;
|
|
2492
|
+
if (accepted) {
|
|
2493
|
+
lastExplicitScrollCommandAtRef.current = Date.now();
|
|
2494
|
+
}
|
|
2495
|
+
return accepted;
|
|
2249
2496
|
};
|
|
2250
2497
|
const requestBridgeRichTextCommand = (payload) => {
|
|
2251
2498
|
return (bridgeClientRef.current?.sendCommand("applyRichTextCommand", payload) ??
|
|
@@ -2331,24 +2578,24 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2331
2578
|
const bridgeDom = pageViewContextRef.current?.bridgeDom;
|
|
2332
2579
|
const contentHeight = bridgeDom?.scrollHeight ??
|
|
2333
2580
|
getBridgeGeometryDocumentSize(bridgeGeometry)?.height;
|
|
2581
|
+
const contentWidth = bridgeDom?.viewport.width ?? bridgeGeometry?.viewport.width;
|
|
2334
2582
|
const clientHeight = iframe.clientHeight;
|
|
2335
2583
|
if (!contentHeight || !clientHeight)
|
|
2336
2584
|
return;
|
|
2337
|
-
const upperThreshold = clientHeight + 100; // show minimap if content exceeds this height
|
|
2338
2585
|
// Check if minimap is enabled in settings and user controls
|
|
2339
2586
|
const minimapEnabled = editContext.parheliaSettings?.showMinimap !== false &&
|
|
2340
2587
|
editContext.showMinimap;
|
|
2341
2588
|
if (!minimapEnabled) {
|
|
2589
|
+
miniMapVisibilityMemoryRef.current = {};
|
|
2342
2590
|
if (showMiniMap) {
|
|
2343
2591
|
setShowMiniMap(false);
|
|
2344
2592
|
}
|
|
2345
2593
|
return;
|
|
2346
2594
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
setShowMiniMap(true);
|
|
2595
|
+
const resolution = resolveMiniMapVisibility(showMiniMap, { contentHeight, contentWidth, clientHeight }, miniMapVisibilityMemoryRef.current);
|
|
2596
|
+
miniMapVisibilityMemoryRef.current = resolution.memory;
|
|
2597
|
+
if (resolution.visible !== showMiniMap) {
|
|
2598
|
+
setShowMiniMap(resolution.visible);
|
|
2352
2599
|
}
|
|
2353
2600
|
}, 100);
|
|
2354
2601
|
useEffect(() => {
|
|
@@ -2369,6 +2616,13 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2369
2616
|
editContextRef.current?.setSelectedRange(undefined);
|
|
2370
2617
|
}
|
|
2371
2618
|
}, [editContext.mode]);
|
|
2619
|
+
// Version that is valid at the active preview date (based on the versions'
|
|
2620
|
+
// publishing restrictions). Derived outside the refresh effect so the effect
|
|
2621
|
+
// re-runs on the resolved version number, not on itemVersions' identity.
|
|
2622
|
+
const previewDateVersion = editContext.mode === "preview" && editContext.previewDate
|
|
2623
|
+
? pickVersionAtDate(editContext.itemVersions, editContext.previewDate)
|
|
2624
|
+
?.version
|
|
2625
|
+
: undefined;
|
|
2372
2626
|
useEffect(() => {
|
|
2373
2627
|
if (!pageItemDescriptor ||
|
|
2374
2628
|
!pageViewContext.editUrl ||
|
|
@@ -2387,7 +2641,23 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2387
2641
|
renderUrl.searchParams.set("parhelia_session", editContext.sessionId);
|
|
2388
2642
|
}
|
|
2389
2643
|
if (editContext.mode === "preview" && editContext.previewDate) {
|
|
2390
|
-
|
|
2644
|
+
// Pin the version that is valid at the preview date. The render URL can
|
|
2645
|
+
// carry the version pin as `sc_version`, `version`, or both; leaving
|
|
2646
|
+
// either behind keeps the page pinned to the previously selected
|
|
2647
|
+
// version, so every pin present must be rewritten. Only update params
|
|
2648
|
+
// that exist: proxied host URLs must not gain sc_* params (they break
|
|
2649
|
+
// Sitecore's httpRequestBegin pipeline — see applyParheliaRenderContext).
|
|
2650
|
+
// sc_date stays on the URL for hosts that resolve datasources by date.
|
|
2651
|
+
if (previewDateVersion != null) {
|
|
2652
|
+
if (renderUrl.searchParams.has("sc_version"))
|
|
2653
|
+
renderUrl.searchParams.set("sc_version", String(previewDateVersion));
|
|
2654
|
+
if (renderUrl.searchParams.has("version"))
|
|
2655
|
+
renderUrl.searchParams.set("version", String(previewDateVersion));
|
|
2656
|
+
}
|
|
2657
|
+
else {
|
|
2658
|
+
renderUrl.searchParams.delete("sc_version");
|
|
2659
|
+
renderUrl.searchParams.delete("version");
|
|
2660
|
+
}
|
|
2391
2661
|
renderUrl.searchParams.set("sc_date", toSitecoreDate(editContext.previewDate));
|
|
2392
2662
|
}
|
|
2393
2663
|
// Layout-mode marker. Only set when editing shared layout, paired with the
|
|
@@ -2406,21 +2676,44 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2406
2676
|
const currentIframeOrigin = getUrlOrigin(currentIframeUrl);
|
|
2407
2677
|
const renderUrlIsCrossOrigin = renderUrl.origin !== window.location.origin;
|
|
2408
2678
|
const iframeOriginChanged = !!(currentIframeOrigin && currentIframeOrigin !== renderUrl.origin);
|
|
2679
|
+
// Proxied host URLs carry the version pin as `version` (sc_* params are
|
|
2680
|
+
// stripped), so consider both param names when detecting a change.
|
|
2681
|
+
const getUrlVersionPin = (url) => url.searchParams.get("sc_version") ?? url.searchParams.get("version");
|
|
2409
2682
|
const currentVersion = currentIframeUrl
|
|
2410
|
-
? new URL(currentIframeUrl)
|
|
2683
|
+
? getUrlVersionPin(new URL(currentIframeUrl))
|
|
2411
2684
|
: null;
|
|
2412
|
-
const newVersion = renderUrl
|
|
2685
|
+
const newVersion = getUrlVersionPin(renderUrl);
|
|
2413
2686
|
const versionChanged = (currentVersion !== null || newVersion !== null) &&
|
|
2414
2687
|
currentVersion !== newVersion;
|
|
2415
2688
|
const initialLoad = currentItemDescriptor?.id !== pageItemDescriptor.id ||
|
|
2416
2689
|
currentItemDescriptor?.language !== pageItemDescriptor.language ||
|
|
2417
2690
|
currentItemDescriptor?.version !== pageItemDescriptor.version;
|
|
2418
2691
|
const shouldUseIframeSrcReload = initialLoad || pageViewContext.fullscreen || iframeOriginChanged;
|
|
2692
|
+
const signatureUrl = new URL(renderUrl.toString());
|
|
2693
|
+
signatureUrl.searchParams.delete("edit_rev");
|
|
2694
|
+
const renderSignature = `${signatureUrl.toString()}|rev:${editContext.revision}`;
|
|
2695
|
+
// Only skip when a live bridge confirms the last refresh actually reached
|
|
2696
|
+
// the host. Before the bridge is connected, re-runs must keep going: the
|
|
2697
|
+
// retry/fallback-reload path below is what recovers a canvas whose first
|
|
2698
|
+
// load raced the bridge injection.
|
|
2699
|
+
if (!shouldUseIframeSrcReload &&
|
|
2700
|
+
!versionChanged &&
|
|
2701
|
+
lastRenderSignatureRef.current === renderSignature &&
|
|
2702
|
+
bridgeClientRef.current?.supportsCapability("refresh")) {
|
|
2703
|
+
setCurrentItemDescriptor(pageItemDescriptor);
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2419
2706
|
function runFallbackRefresh() {
|
|
2707
|
+
lastRenderSignatureRef.current = renderSignature;
|
|
2420
2708
|
pageViewContext.setIframeSupportsRefresh(false);
|
|
2421
|
-
pendingRefreshScrollRef.current
|
|
2422
|
-
|
|
2423
|
-
|
|
2709
|
+
if (!pendingRefreshScrollRef.current) {
|
|
2710
|
+
pendingRefreshScrollRef.current =
|
|
2711
|
+
getIframeWindowScroll(iframeRef.current) ??
|
|
2712
|
+
latestBridgeScrollRef.current;
|
|
2713
|
+
pendingRefreshScrollCapturedAtRef.current = Date.now();
|
|
2714
|
+
}
|
|
2715
|
+
canvasRefreshInFlightSinceRef.current = Date.now();
|
|
2716
|
+
awaitingHostAppRefreshRenderRef.current = false;
|
|
2424
2717
|
setShowSpinner(true);
|
|
2425
2718
|
console.log(initialLoad
|
|
2426
2719
|
? "Initial load - setting iframe src"
|
|
@@ -2433,9 +2726,11 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2433
2726
|
setIframeSrc(renderUrl.toString());
|
|
2434
2727
|
}
|
|
2435
2728
|
function runBridgeRefresh() {
|
|
2729
|
+
lastRenderSignatureRef.current = renderSignature;
|
|
2436
2730
|
pendingRefreshScrollRef.current =
|
|
2437
2731
|
getIframeWindowScroll(iframeRef.current) ??
|
|
2438
2732
|
latestBridgeScrollRef.current;
|
|
2733
|
+
pendingRefreshScrollCapturedAtRef.current = Date.now();
|
|
2439
2734
|
const bridge = bridgeClientRef.current;
|
|
2440
2735
|
if (!bridge?.supportsCapability("refresh")) {
|
|
2441
2736
|
runFallbackRefresh();
|
|
@@ -2443,6 +2738,8 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2443
2738
|
}
|
|
2444
2739
|
console.log("Bridge - requesting iframe refresh");
|
|
2445
2740
|
pageViewContext.setIframeSupportsRefresh(true);
|
|
2741
|
+
canvasRefreshInFlightSinceRef.current = Date.now();
|
|
2742
|
+
awaitingHostAppRefreshRenderRef.current = false;
|
|
2446
2743
|
const accepted = bridge.sendCommand("refresh", {
|
|
2447
2744
|
url: renderUrl.toString(),
|
|
2448
2745
|
revision: editContext.revision,
|
|
@@ -2450,6 +2747,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2450
2747
|
});
|
|
2451
2748
|
if (!accepted) {
|
|
2452
2749
|
pendingRefreshScrollRef.current = undefined;
|
|
2750
|
+
canvasRefreshInFlightSinceRef.current = null;
|
|
2453
2751
|
pageViewContext.setIframeSupportsRefresh(false);
|
|
2454
2752
|
runFallbackRefresh();
|
|
2455
2753
|
}
|
|
@@ -2481,13 +2779,24 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2481
2779
|
pageViewContext.fullscreen,
|
|
2482
2780
|
editContext.mode,
|
|
2483
2781
|
editContext.previewDate,
|
|
2782
|
+
previewDateVersion,
|
|
2484
2783
|
editContext.sessionId,
|
|
2485
2784
|
editContext.layoutMode,
|
|
2486
2785
|
]);
|
|
2487
2786
|
useEffect(() => {
|
|
2488
2787
|
if (fieldsContext?.focusedField) {
|
|
2489
|
-
|
|
2490
|
-
|
|
2788
|
+
// A refresh is being applied: the field's rect still describes the
|
|
2789
|
+
// pre-edit DOM (after a move, its old position — where the drag
|
|
2790
|
+
// started), so scrolling now would yank the canvas to a stale spot.
|
|
2791
|
+
// This fires on the post-operation auto-focus of the moved/added
|
|
2792
|
+
// component; a genuine user focus during a refresh loses one
|
|
2793
|
+
// auto-scroll, which is the lesser evil.
|
|
2794
|
+
if (isCanvasRefreshInFlight())
|
|
2795
|
+
return;
|
|
2796
|
+
// Event-time read: this effect is keyed on focusedField, not selection.
|
|
2797
|
+
const currentSelection = editContext.getSelection();
|
|
2798
|
+
if (currentSelection.length > 0 &&
|
|
2799
|
+
fieldsContext.focusedField.item.id !== currentSelection[0])
|
|
2491
2800
|
return;
|
|
2492
2801
|
const bounds = findBridgeFieldDocumentBounds(pageViewContextRef.current?.bridgeGeometry, fieldsContext.focusedField);
|
|
2493
2802
|
const activePageViewContext = pageViewContextRef.current;
|
|
@@ -2495,7 +2804,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2495
2804
|
scrollBridgeBoundsIntoView(activePageViewContext, bounds, latestBridgeScrollRef.current);
|
|
2496
2805
|
}
|
|
2497
2806
|
}
|
|
2498
|
-
}, [fieldsContext?.focusedField]);
|
|
2807
|
+
}, [fieldsContext?.focusedField, isCanvasRefreshInFlight]);
|
|
2499
2808
|
// Drive the selection-scroll suppression window from the drag lifecycle.
|
|
2500
2809
|
// Declared BEFORE the selection-scroll effect so it runs first in the same
|
|
2501
2810
|
// commit (when a drop changes both dragObject and selection at once).
|
|
@@ -2512,7 +2821,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2512
2821
|
}
|
|
2513
2822
|
}, [editContext.dragObject]);
|
|
2514
2823
|
useEffect(() => {
|
|
2515
|
-
const selectionKey =
|
|
2824
|
+
const selectionKey = canvasSelection.join("|");
|
|
2516
2825
|
const selectionChanged = selectionKey !== lastScrolledSelectionKeyRef.current;
|
|
2517
2826
|
lastScrolledSelectionKeyRef.current = selectionKey;
|
|
2518
2827
|
// The effect also re-runs when focusedField changes (e.g. a field blurs
|
|
@@ -2525,24 +2834,43 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2525
2834
|
suppressNextSelectionScrollRef.current = false;
|
|
2526
2835
|
return;
|
|
2527
2836
|
}
|
|
2837
|
+
// Programmatic selections (the drop target's parent, the post-operation
|
|
2838
|
+
// focus of a moved/added component) arm this suppression at their source:
|
|
2839
|
+
// their targets may still measure at pre-edit geometry, and unlike user
|
|
2840
|
+
// clicks they carry no intent to see the component. Deterministic — no
|
|
2841
|
+
// time window to lose against a slow operation or frozen host.
|
|
2842
|
+
if (editContext.consumeNextSelectionAutoScrollSuppression?.())
|
|
2843
|
+
return;
|
|
2528
2844
|
// Suppressed during a drag and briefly after it ends. The drag owns
|
|
2529
2845
|
// scrolling, and the drop's re-selection must not jump the page away from
|
|
2530
2846
|
// where the user dropped (toward stale pre-move geometry).
|
|
2531
2847
|
if (Date.now() < suppressSelectionScrollUntilRef.current)
|
|
2532
2848
|
return;
|
|
2533
|
-
|
|
2534
|
-
|
|
2849
|
+
// Suppressed while a refresh is applying: geometry still measures the
|
|
2850
|
+
// pre-refresh DOM (a just-moved component still sits at its old rect), so
|
|
2851
|
+
// scrolling now would target a stale position. The refresh restore owns
|
|
2852
|
+
// the viewport until the refreshed geometry lands.
|
|
2853
|
+
if (isCanvasRefreshInFlight())
|
|
2854
|
+
return;
|
|
2855
|
+
if (!fieldsContext?.focusedField && canvasSelection.length > 0) {
|
|
2856
|
+
const lastSelectedComponent = getComponentById(canvasSelection[canvasSelection.length - 1], pageViewContextRef.current.page);
|
|
2535
2857
|
if (lastSelectedComponent) {
|
|
2536
2858
|
editContext.setScrollIntoView(lastSelectedComponent.id);
|
|
2537
2859
|
}
|
|
2538
2860
|
}
|
|
2539
|
-
}, [
|
|
2861
|
+
}, [canvasSelection, fieldsContext?.focusedField, isCanvasRefreshInFlight]);
|
|
2540
2862
|
useEffect(() => {
|
|
2541
2863
|
if (!editContext.scrollIntoView)
|
|
2542
2864
|
return;
|
|
2543
2865
|
const activePageViewContext = pageViewContextRef.current;
|
|
2544
2866
|
if (!activePageViewContext)
|
|
2545
2867
|
return;
|
|
2868
|
+
// Defer (without consuming the request) while a refresh is applying:
|
|
2869
|
+
// resolving now would measure the target against pre-refresh geometry and
|
|
2870
|
+
// scroll to a stale rect. The effect re-runs when the refreshed geometry
|
|
2871
|
+
// arrives (bridgeGeometry dependency) and resolves against fresh bounds.
|
|
2872
|
+
if (isCanvasRefreshInFlight())
|
|
2873
|
+
return;
|
|
2546
2874
|
const scrollTarget = editContext.scrollIntoView;
|
|
2547
2875
|
const requestState = scrollIntoViewRequestRef.current;
|
|
2548
2876
|
const signature = getScrollIntoViewSignature(scrollTarget);
|
|
@@ -2577,6 +2905,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2577
2905
|
editContext.scrollIntoView,
|
|
2578
2906
|
pageViewContext.page,
|
|
2579
2907
|
pageViewContext.bridgeGeometry,
|
|
2908
|
+
isCanvasRefreshInFlight,
|
|
2580
2909
|
]);
|
|
2581
2910
|
useEffect(() => {
|
|
2582
2911
|
const handleMessage = (message) => {
|
|
@@ -2651,10 +2980,16 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2651
2980
|
};
|
|
2652
2981
|
}, [iframeElement]);
|
|
2653
2982
|
useEffect(() => {
|
|
2654
|
-
bridgeClientRef.current?.sendCommand("setSelection", {
|
|
2655
|
-
componentIds:
|
|
2983
|
+
const sent = bridgeClientRef.current?.sendCommand("setSelection", {
|
|
2984
|
+
componentIds: canvasSelection,
|
|
2656
2985
|
});
|
|
2657
|
-
|
|
2986
|
+
if (isPerfTraceActive()) {
|
|
2987
|
+
perfTraceStep("frame:setSelection-command-sent", {
|
|
2988
|
+
ids: canvasSelection,
|
|
2989
|
+
sent: sent ?? false,
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
}, [canvasSelection]);
|
|
2658
2993
|
useEffect(() => {
|
|
2659
2994
|
const bridge = bridgeClientRef.current;
|
|
2660
2995
|
bridge?.sendCommand("setPreviewMode", {
|
|
@@ -2787,6 +3122,7 @@ function PageViewerFrameContent({ compareView, pageViewContext, editContext, cla
|
|
|
2787
3122
|
if (pendingRefreshScrollRef.current) {
|
|
2788
3123
|
restorePendingRefreshScroll(iframeRef.current, pendingRefreshScrollRef.current);
|
|
2789
3124
|
}
|
|
3125
|
+
completeCanvasRefresh();
|
|
2790
3126
|
}
|
|
2791
3127
|
} }), iframeElement && (_jsx(IframeOverlayProvider, { iframe: iframeElement, mode: "scrolling", scrollScale: 1, visualScale: 1, invalidationKey: `${zoom}:${bridgeGeometryRevision}`, deferredInvalidationMs: ZOOM_TRANSITION_MS + 50, bleed: { top: 2, bottom: 2, left: 2 }, children: _jsx(PageEditorChrome, { iframe: iframeElement, compareView: compareView, pageViewContext: pageViewContext, onBridgeRichTextCommand: sendApplyRichTextCommand }) })), pageViewContext.deviceHeight && pageViewContext.device && (_jsx("div", { className: "bg-neutral-grey-5 relative z-40 h-full w-full" }))] }), shouldRenderMiniMap && (_jsx(MiniMap, { compareView: compareView, scroll: scroll, mainViewIframeRef: iframeRef, pageViewContext: pageViewContext })), showSpinner && (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-neutral-grey-5/50 absolute top-0 left-0 h-full w-full" }), _jsx("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform", children: _jsx(Spinner, {}) })] }))] }), _jsx(FieldActionsOverlay, { ref: fieldActionsOverlay, generatorButtons: contextMenuFieldButtons, onActionClick: handleActionClick, onParameterizedActionExecute: handleParameterizedActionExecute, currentOverlay: editContext?.currentOverlay, fieldId: contextMenuField?.fieldId || "", setCurrentOverlay: editContext?.setCurrentOverlay || (() => { }), preSelectedAction: preSelectedAction })] }));
|
|
2792
3128
|
}
|